win32-service 0.6.1-x86-mswin32-60 → 0.7.0-x86-mswin32-60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +21 -0
- data/MANIFEST +6 -5
- data/README +2 -1
- data/Rakefile +128 -0
- data/doc/daemon.txt +5 -3
- data/doc/service.txt +13 -10
- data/examples/demo_daemon.rb +89 -0
- data/examples/demo_daemon_ctl.rb +122 -0
- data/examples/demo_services.rb +23 -0
- data/ext/win32/daemon.c +596 -0
- data/lib/win32/daemon.so +0 -0
- data/lib/win32/service.rb +52 -24
- data/test/{tc_daemon.rb → test_win32_daemon.rb} +4 -1
- data/test/{tc_service.rb → test_win32_service.rb} +5 -14
- data/test/test_win32_service_configure.rb +86 -0
- data/test/test_win32_service_create.rb +103 -0
- data/test/{tc_service_info.rb → test_win32_service_info.rb} +12 -3
- data/test/{tc_service_status.rb → test_win32_service_status.rb} +3 -0
- data/win32-service.gemspec +49 -0
- metadata +42 -19
- data/test/tc_service_create.rb +0 -83
data/lib/win32/daemon.so
CHANGED
Binary file
|
data/lib/win32/service.rb
CHANGED
@@ -23,7 +23,8 @@ module Win32
|
|
23
23
|
include Windows::Process
|
24
24
|
include Windows::Security
|
25
25
|
include Windows::MSVCRT::String
|
26
|
-
include Windows::MSVCRT::Buffer
|
26
|
+
include Windows::MSVCRT::Buffer
|
27
|
+
|
27
28
|
extend Windows::Error
|
28
29
|
extend Windows::Service
|
29
30
|
extend Windows::File
|
@@ -32,7 +33,8 @@ module Win32
|
|
32
33
|
extend Windows::MSVCRT::String
|
33
34
|
extend Windows::MSVCRT::Buffer
|
34
35
|
|
35
|
-
|
36
|
+
# The version of the win32-service library
|
37
|
+
VERSION = '0.7.0'
|
36
38
|
|
37
39
|
# SCM security and access rights
|
38
40
|
|
@@ -227,10 +229,14 @@ module Win32
|
|
227
229
|
|
228
230
|
# :startdoc: #
|
229
231
|
|
230
|
-
# Creates a new service with
|
231
|
-
#
|
232
|
-
#
|
232
|
+
# Creates a new service with the specified +options+. A +service_name+
|
233
|
+
# must be specified or an ArgumentError is raised. A +host+ option may
|
234
|
+
# be specified. If no host is specified the local machine is used.
|
233
235
|
#
|
236
|
+
# Possible Options:
|
237
|
+
#
|
238
|
+
# * service_name => nil (you must specify)
|
239
|
+
# * host => nil (optional)
|
234
240
|
# * display_name => service_name
|
235
241
|
# * desired_access => Service::ALL_ACCESS
|
236
242
|
# * service_type => Service::WIN32_OWN_PROCESS |
|
@@ -252,7 +258,9 @@ module Win32
|
|
252
258
|
# Example:
|
253
259
|
#
|
254
260
|
# # Configure everything
|
255
|
-
# Service.new(
|
261
|
+
# Service.new(
|
262
|
+
# :service_name => 'some_service',
|
263
|
+
# :host => 'localhost',
|
256
264
|
# :service_type => Service::WIN32_OWN_PROCESS,
|
257
265
|
# :description => 'A custom service I wrote just for fun',
|
258
266
|
# :start_type => Service::AUTO_START,
|
@@ -265,10 +273,7 @@ module Win32
|
|
265
273
|
# :display_name => 'This is some service',
|
266
274
|
# )
|
267
275
|
#
|
268
|
-
def initialize(
|
269
|
-
raise TypeError unless service_name.is_a?(String)
|
270
|
-
raise TypeError if host && !host.is_a?(String)
|
271
|
-
|
276
|
+
def initialize(options={})
|
272
277
|
unless options.is_a?(Hash)
|
273
278
|
raise ArgumentError, 'options parameter must be a hash'
|
274
279
|
end
|
@@ -294,7 +299,9 @@ module Win32
|
|
294
299
|
'failure_reboot_message' => nil,
|
295
300
|
'failure_command' => nil,
|
296
301
|
'failure_actions' => nil,
|
297
|
-
'failure_delay' => 0
|
302
|
+
'failure_delay' => 0,
|
303
|
+
'host' => nil,
|
304
|
+
'service_name' => nil
|
298
305
|
}
|
299
306
|
|
300
307
|
# Validate the hash options
|
@@ -305,6 +312,16 @@ module Win32
|
|
305
312
|
end
|
306
313
|
opts[key] = value
|
307
314
|
}
|
315
|
+
|
316
|
+
unless opts['service_name']
|
317
|
+
raise ArgumentError, 'No service_name specified'
|
318
|
+
end
|
319
|
+
|
320
|
+
service_name = opts.delete('service_name')
|
321
|
+
host = opts.delete('host')
|
322
|
+
|
323
|
+
raise TypeError unless service_name.is_a?(String)
|
324
|
+
raise TypeError if host && !host.is_a?(String)
|
308
325
|
|
309
326
|
handle_scm = OpenSCManager(host, 0, SC_MANAGER_CREATE_SERVICE)
|
310
327
|
|
@@ -408,10 +425,11 @@ module Win32
|
|
408
425
|
# Examples:
|
409
426
|
#
|
410
427
|
# # Configure only the display name
|
411
|
-
# Service.configure('some_service',
|
428
|
+
# Service.configure(:service_name => 'some_service', :display_name => 'Test 33')
|
412
429
|
#
|
413
430
|
# # Configure everything
|
414
|
-
# Service.configure(
|
431
|
+
# Service.configure(
|
432
|
+
# :service_name => 'some_service'
|
415
433
|
# :service_type => Service::WIN32_OWN_PROCESS,
|
416
434
|
# :start_type => Service::AUTO_START,
|
417
435
|
# :error_control => Service::ERROR_NORMAL,
|
@@ -424,9 +442,7 @@ module Win32
|
|
424
442
|
# :description => 'A custom service I wrote just for fun'
|
425
443
|
# )
|
426
444
|
#
|
427
|
-
def self.configure(
|
428
|
-
raise TypeError unless service.is_a?(String)
|
429
|
-
|
445
|
+
def self.configure(options={})
|
430
446
|
unless options.is_a?(Hash)
|
431
447
|
raise ArgumentError, 'options parameter must be a hash'
|
432
448
|
end
|
@@ -450,7 +466,9 @@ module Win32
|
|
450
466
|
'failure_reboot_message' => nil,
|
451
467
|
'failure_command' => nil,
|
452
468
|
'failure_actions' => nil,
|
453
|
-
'failure_delay' => 0
|
469
|
+
'failure_delay' => 0,
|
470
|
+
'service_name' => nil,
|
471
|
+
'host' => nil
|
454
472
|
}
|
455
473
|
|
456
474
|
# Validate the hash options
|
@@ -461,6 +479,16 @@ module Win32
|
|
461
479
|
end
|
462
480
|
opts[key] = value
|
463
481
|
}
|
482
|
+
|
483
|
+
unless opts['service_name']
|
484
|
+
raise ArgumentError, 'No service_name specified'
|
485
|
+
end
|
486
|
+
|
487
|
+
service = opts.delete('service_name')
|
488
|
+
host = opts.delete('host')
|
489
|
+
|
490
|
+
raise TypeError unless service.is_a?(String)
|
491
|
+
raise TypeError unless host.is_a?(String) if host
|
464
492
|
|
465
493
|
handle_scm = OpenSCManager(host, 0, SC_MANAGER_CONNECT)
|
466
494
|
|
@@ -861,9 +889,9 @@ module Win32
|
|
861
889
|
CloseServiceHandle(handle_scs)
|
862
890
|
CloseServiceHandle(handle_scm)
|
863
891
|
|
864
|
-
binary_path_name = 0.chr *
|
865
|
-
load_order_group = 0.chr *
|
866
|
-
dependencies = 0.chr *
|
892
|
+
binary_path_name = 0.chr * 1024
|
893
|
+
load_order_group = 0.chr * 1024
|
894
|
+
dependencies = 0.chr * 1024
|
867
895
|
service_start_name = 0.chr * 260
|
868
896
|
display_name = 0.chr * 260
|
869
897
|
|
@@ -1095,15 +1123,15 @@ module Win32
|
|
1095
1123
|
config_buf = get_config_info(handle_scs)
|
1096
1124
|
|
1097
1125
|
if config_buf != ERROR_FILE_NOT_FOUND
|
1098
|
-
binary_path = 0.chr *
|
1126
|
+
binary_path = 0.chr * 1024
|
1099
1127
|
strcpy(binary_path, config_buf[12,4].unpack('L').first)
|
1100
1128
|
binary_path = binary_path.unpack('Z*')[0]
|
1101
1129
|
|
1102
|
-
load_order = 0.chr *
|
1130
|
+
load_order = 0.chr * 1024
|
1103
1131
|
strcpy(load_order, config_buf[16,4].unpack('L').first)
|
1104
1132
|
load_order = load_order.unpack('Z*')[0]
|
1105
1133
|
|
1106
|
-
start_name = 0.chr *
|
1134
|
+
start_name = 0.chr * 1024
|
1107
1135
|
strcpy(start_name, config_buf[28,4].unpack('L').first)
|
1108
1136
|
start_name = start_name.unpack('Z*')[0]
|
1109
1137
|
|
@@ -1114,7 +1142,7 @@ module Win32
|
|
1114
1142
|
|
1115
1143
|
deps = get_dependencies(config_buf[24,4].unpack('L').first)
|
1116
1144
|
|
1117
|
-
description = 0.chr *
|
1145
|
+
description = 0.chr * 2048
|
1118
1146
|
buf = get_config2_info(handle_scs, SERVICE_CONFIG_DESCRIPTION)
|
1119
1147
|
|
1120
1148
|
strcpy(description, buf[0,4].unpack('L').first)
|
@@ -7,6 +7,9 @@
|
|
7
7
|
# These tests are rather limited, since the acid test is to install
|
8
8
|
# your daemon as a service and see how it behaves.
|
9
9
|
#########################################################################
|
10
|
+
require 'rubygems'
|
11
|
+
gem 'test-unit'
|
12
|
+
|
10
13
|
require 'win32/daemon'
|
11
14
|
require 'test/unit'
|
12
15
|
include Win32
|
@@ -17,7 +20,7 @@ class TC_Daemon < Test::Unit::TestCase
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def test_version
|
20
|
-
assert_equal('0.
|
23
|
+
assert_equal('0.7.0', Daemon::VERSION)
|
21
24
|
end
|
22
25
|
|
23
26
|
def test_constructor
|
@@ -3,6 +3,9 @@
|
|
3
3
|
#
|
4
4
|
# Test case for the Win32::Service class.
|
5
5
|
##########################################################################
|
6
|
+
require 'rubygems'
|
7
|
+
gem 'test-unit'
|
8
|
+
|
6
9
|
require 'win32/service'
|
7
10
|
require 'socket'
|
8
11
|
require 'test/unit'
|
@@ -20,19 +23,7 @@ class TC_Win32_Service < Test::Unit::TestCase
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def test_version
|
23
|
-
assert_equal('0.
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_service_configure
|
27
|
-
assert_respond_to(Win32::Service, :configure)
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_service_configure_expected_errors
|
31
|
-
assert_raise(ArgumentError){ Win32::Service.configure }
|
32
|
-
assert_raise(ArgumentError){ Win32::Service.configure('bogus') }
|
33
|
-
assert_raise(ArgumentError){ Win32::Service.configure('bogus', 'bogus') }
|
34
|
-
assert_raise(Win32::Service::Error){ Win32::Service.configure('x', nil, :description => 'Test') }
|
35
|
-
assert_raise(Win32::Service::Error){ Win32::Service.configure('x', 'y', :description => 'Test') }
|
26
|
+
assert_equal('0.7.0', Win32::Service::VERSION)
|
36
27
|
end
|
37
28
|
|
38
29
|
def test_services_basic
|
@@ -41,7 +32,7 @@ class TC_Win32_Service < Test::Unit::TestCase
|
|
41
32
|
assert_nothing_raised{ Win32::Service.services(nil) }
|
42
33
|
assert_nothing_raised{ Win32::Service.services(nil, 'network') }
|
43
34
|
end
|
44
|
-
|
35
|
+
|
45
36
|
def test_services_non_block_form
|
46
37
|
assert_nothing_raised{ @services = Win32::Service.services }
|
47
38
|
assert_kind_of(Array, @services)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#######################################################################
|
2
|
+
# test_win32_service_configure.rb
|
3
|
+
#
|
4
|
+
# Test suite that validates the Service.configure method.
|
5
|
+
#######################################################################
|
6
|
+
require 'rubygems'
|
7
|
+
gem 'test-unit'
|
8
|
+
|
9
|
+
require 'win32/service'
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
class TC_Service_Configure < Test::Unit::TestCase
|
13
|
+
def self.startup
|
14
|
+
@@service = "notepad_service"
|
15
|
+
@@command = "C:\\windows\\system32\\notepad.exe"
|
16
|
+
|
17
|
+
Win32::Service.new(
|
18
|
+
:service_name => @@service,
|
19
|
+
:binary_path_name => @@command
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def config_info
|
24
|
+
Win32::Service.config_info(@@service)
|
25
|
+
end
|
26
|
+
|
27
|
+
def full_info
|
28
|
+
service = nil
|
29
|
+
Win32::Service.services{ |s|
|
30
|
+
if s.service_name == @@service
|
31
|
+
service = s
|
32
|
+
break
|
33
|
+
end
|
34
|
+
}
|
35
|
+
service
|
36
|
+
end
|
37
|
+
|
38
|
+
def service_configure(opt)
|
39
|
+
options = {:service_name => @@service}
|
40
|
+
options = options.merge(opt)
|
41
|
+
assert_nothing_raised{ Win32::Service.configure(options) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup
|
45
|
+
@info = Win32::Service.config_info(@@service)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_service_configure_basic
|
49
|
+
assert_respond_to(Win32::Service, :configure)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_service_type
|
53
|
+
assert_equal('own process, interactive', config_info.service_type)
|
54
|
+
service_configure(:service_type => Win32::Service::WIN32_SHARE_PROCESS)
|
55
|
+
assert_equal('share process', config_info.service_type)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_description
|
59
|
+
assert_equal('', full_info.description)
|
60
|
+
service_configure(:description => 'test service')
|
61
|
+
assert_equal('test service', full_info.description)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_start_type
|
65
|
+
assert_equal('demand start', config_info.start_type)
|
66
|
+
service_configure(:start_type => Win32::Service::DISABLED)
|
67
|
+
assert_equal('disabled', config_info.start_type)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_service_configure_expected_errors
|
71
|
+
assert_raise(ArgumentError){ Win32::Service.configure }
|
72
|
+
assert_raise(ArgumentError){ Win32::Service.configure('bogus') }
|
73
|
+
assert_raise(ArgumentError){ Win32::Service.configure({}) }
|
74
|
+
assert_raise(ArgumentError){ Win32::Service.configure(:binary_path_name => 'notepad.exe') }
|
75
|
+
end
|
76
|
+
|
77
|
+
def teardown
|
78
|
+
@info = nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.shutdown
|
82
|
+
Win32::Service.delete(@@service) if Win32::Service.exists?(@@service)
|
83
|
+
@@service = nil
|
84
|
+
@@command = nil
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
########################################################################
|
2
|
+
# tc_service_create.rb
|
3
|
+
#
|
4
|
+
# Test case for the Service.create method. This test case will create
|
5
|
+
# a dummy (notepad) service. It won't actually run of course.
|
6
|
+
########################################################################
|
7
|
+
require 'rubygems'
|
8
|
+
gem 'test-unit'
|
9
|
+
|
10
|
+
require 'win32/service'
|
11
|
+
require 'test/unit'
|
12
|
+
|
13
|
+
class TC_Service_Create < Test::Unit::TestCase
|
14
|
+
def self.startup
|
15
|
+
@@service1 = "notepad_service1"
|
16
|
+
@@service2 = "notepad_service2"
|
17
|
+
@@command = "C:\\windows\\system32\\notepad.exe"
|
18
|
+
|
19
|
+
Win32::Service.new(
|
20
|
+
:service_name => @@service1,
|
21
|
+
:binary_path_name => @@command
|
22
|
+
)
|
23
|
+
|
24
|
+
Win32::Service.new(
|
25
|
+
:service_name => @@service2,
|
26
|
+
:display_name => 'Notepad Test',
|
27
|
+
:desired_access => Win32::Service::ALL_ACCESS,
|
28
|
+
:service_type => Win32::Service::WIN32_OWN_PROCESS,
|
29
|
+
:start_type => Win32::Service::DISABLED,
|
30
|
+
:error_control => Win32::Service::ERROR_IGNORE,
|
31
|
+
:binary_path_name => @@command,
|
32
|
+
:load_order_group => 'Network',
|
33
|
+
:dependencies => 'W32Time',
|
34
|
+
:description => 'Test service. Please delete me'
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup
|
39
|
+
@info1 = Win32::Service.config_info(@@service1)
|
40
|
+
@info2 = Win32::Service.config_info(@@service2)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_new_basic
|
44
|
+
assert_respond_to(Win32::Service, :new)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_create_alias
|
48
|
+
assert_respond_to(Win32::Service, :create)
|
49
|
+
assert_equal(Win32::Service.method(:create), Win32::Service.method(:new))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Sanity test to ensure the services were actually created. If this test
|
53
|
+
# fails, the results for other tests are meaningless.
|
54
|
+
#
|
55
|
+
def test_service_created
|
56
|
+
assert_true(Win32::Service.exists?(@@service1))
|
57
|
+
assert_true(Win32::Service.exists?(@@service2))
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_service_configuration_info
|
61
|
+
assert_equal('own process, interactive', @info1.service_type)
|
62
|
+
assert_equal('demand start', @info1.start_type)
|
63
|
+
assert_equal('normal', @info1.error_control)
|
64
|
+
assert_equal(@@command, @info1.binary_path_name)
|
65
|
+
assert_equal('', @info1.load_order_group)
|
66
|
+
assert_equal(0, @info1.tag_id)
|
67
|
+
assert_equal([], @info1.dependencies)
|
68
|
+
assert_equal('LocalSystem', @info1.service_start_name)
|
69
|
+
assert_equal('notepad_service1', @info1.display_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_service_configuration_info_multiple_options
|
73
|
+
assert_equal('own process', @info2.service_type)
|
74
|
+
assert_equal('disabled', @info2.start_type)
|
75
|
+
assert_equal('ignore', @info2.error_control)
|
76
|
+
assert_equal(@@command, @info2.binary_path_name)
|
77
|
+
assert_equal('Network', @info2.load_order_group)
|
78
|
+
assert_equal(0, @info2.tag_id)
|
79
|
+
assert_equal(['W32Time'], @info2.dependencies)
|
80
|
+
assert_equal('LocalSystem', @info2.service_start_name)
|
81
|
+
assert_equal('Notepad Test', @info2.display_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_create_expected_errors
|
85
|
+
assert_raise(ArgumentError){ Win32::Service.new }
|
86
|
+
assert_raise(ArgumentError){ Win32::Service.new(:binary_path_name => 'test.exe') }
|
87
|
+
assert_raise(ArgumentError){ Win32::Service.new(:bogus => 'test.exe') }
|
88
|
+
end
|
89
|
+
|
90
|
+
def teardown
|
91
|
+
@info1 = nil
|
92
|
+
@info2 = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.shutdown
|
96
|
+
Win32::Service.delete(@@service1) if Win32::Service.exists?(@@service1)
|
97
|
+
Win32::Service.delete(@@service2) if Win32::Service.exists?(@@service2)
|
98
|
+
|
99
|
+
@@service1 = nil
|
100
|
+
@@service2 = nil
|
101
|
+
@@command = nil
|
102
|
+
end
|
103
|
+
end
|
@@ -3,13 +3,19 @@
|
|
3
3
|
#
|
4
4
|
# Test case for the Struct::ServiceInfo structure.
|
5
5
|
########################################################################
|
6
|
+
require 'rubygems'
|
7
|
+
gem 'test-unit'
|
8
|
+
|
6
9
|
require 'win32/service'
|
7
10
|
require 'test/unit'
|
8
11
|
|
9
12
|
class TC_Struct_ServiceInfo < Test::Unit::TestCase
|
13
|
+
def self.startup
|
14
|
+
@@services = Win32::Service.services
|
15
|
+
end
|
16
|
+
|
10
17
|
def setup
|
11
|
-
@
|
12
|
-
@service_info = @services[0]
|
18
|
+
@service_info = @@services[0]
|
13
19
|
|
14
20
|
@error_controls = [
|
15
21
|
'critical',
|
@@ -162,9 +168,12 @@ class TC_Struct_ServiceInfo < Test::Unit::TestCase
|
|
162
168
|
end
|
163
169
|
|
164
170
|
def teardown
|
165
|
-
@services = nil
|
166
171
|
@types = nil
|
167
172
|
@states = nil
|
168
173
|
@controls = nil
|
169
174
|
end
|
175
|
+
|
176
|
+
def self.shutdown
|
177
|
+
@@services = nil
|
178
|
+
end
|
170
179
|
end
|