win32-service 0.7.2 → 0.8.0
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 +4 -0
- data/MANIFEST +2 -3
- data/README +75 -71
- data/Rakefile +7 -90
- data/doc/daemon.txt +7 -7
- data/doc/service.txt +8 -9
- data/examples/demo_daemon.rb +11 -11
- data/examples/demo_daemon_ctl.rb +3 -3
- data/examples/demo_services.rb +30 -30
- data/lib/win32/daemon.rb +345 -0
- data/lib/win32/service.rb +346 -434
- data/lib/win32/windows/constants.rb +137 -0
- data/lib/win32/windows/functions.rb +63 -0
- data/lib/win32/windows/helper.rb +41 -0
- data/lib/win32/windows/structs.rb +96 -0
- data/test/test_win32_daemon.rb +10 -13
- data/test/test_win32_service.rb +54 -48
- data/test/test_win32_service_configure.rb +19 -22
- data/test/test_win32_service_create.rb +129 -131
- data/test/test_win32_service_info.rb +8 -9
- data/test/test_win32_service_status.rb +2 -5
- data/win32-service.gemspec +5 -11
- metadata +61 -74
- data/ext/extconf.rb +0 -9
- data/ext/win32/daemon.c +0 -612
data/examples/demo_daemon_ctl.rb
CHANGED
@@ -37,7 +37,7 @@
|
|
37
37
|
require 'win32/service'
|
38
38
|
require 'rbconfig'
|
39
39
|
include Win32
|
40
|
-
include
|
40
|
+
include RbConfig
|
41
41
|
|
42
42
|
# Make sure you're using the version you think you're using.
|
43
43
|
puts 'VERSION: ' + Service::VERSION
|
@@ -46,7 +46,7 @@ SERVICE_NAME = 'DemoSvc'
|
|
46
46
|
SERVICE_DISPLAYNAME = 'Demo'
|
47
47
|
|
48
48
|
# Quote the full path to deal with possible spaces in the path name.
|
49
|
-
ruby = File.join(CONFIG['bindir'], '
|
49
|
+
ruby = File.join(CONFIG['bindir'], CONFIG['ruby_install_name']).tr('/', '\\')
|
50
50
|
path = ' "' + File.dirname(File.expand_path($0)).tr('/', '\\')
|
51
51
|
path += '\demo_daemon.rb"'
|
52
52
|
cmd = ruby + path
|
@@ -62,7 +62,7 @@ case ARGV[0].downcase
|
|
62
62
|
:description => 'Sample Ruby service',
|
63
63
|
:binary_path_name => cmd
|
64
64
|
)
|
65
|
-
puts 'Service ' + SERVICE_NAME + ' installed'
|
65
|
+
puts 'Service ' + SERVICE_NAME + ' installed'
|
66
66
|
when 'start'
|
67
67
|
if Service.status(SERVICE_NAME).current_state != 'running'
|
68
68
|
Service.start(SERVICE_NAME, nil, 'hello', 'world')
|
data/examples/demo_services.rb
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
#######################################################################
|
2
|
-
# demo_services.rb
|
3
|
-
#
|
4
|
-
# Test script for general futzing that shows off the basic
|
5
|
-
# capabilities of this library. Modify as you see fit.
|
6
|
-
#
|
7
|
-
# You can run this sample program via the "example:services" task.
|
8
|
-
#######################################################################
|
9
|
-
require 'win32/service'
|
10
|
-
include Win32
|
11
|
-
|
12
|
-
puts "VERSION: " + Service::VERSION
|
13
|
-
|
14
|
-
p Service.exists?('Schedule')
|
15
|
-
p Service.exists?('bogusxxx')
|
16
|
-
|
17
|
-
status = Service.status('Schedule')
|
18
|
-
p status
|
19
|
-
|
20
|
-
info = Service.config_info('Schedule')
|
21
|
-
|
22
|
-
print "\n\nShowing config info for Schedule service\n\n"
|
23
|
-
p info
|
24
|
-
|
25
|
-
print "\n\nAbout to show all services\n\n"
|
26
|
-
sleep 10
|
27
|
-
|
28
|
-
Service.services{ |struct|
|
29
|
-
p struct
|
30
|
-
}
|
1
|
+
#######################################################################
|
2
|
+
# demo_services.rb
|
3
|
+
#
|
4
|
+
# Test script for general futzing that shows off the basic
|
5
|
+
# capabilities of this library. Modify as you see fit.
|
6
|
+
#
|
7
|
+
# You can run this sample program via the "example:services" task.
|
8
|
+
#######################################################################
|
9
|
+
require 'win32/service'
|
10
|
+
include Win32
|
11
|
+
|
12
|
+
puts "VERSION: " + Service::VERSION
|
13
|
+
|
14
|
+
p Service.exists?('Schedule')
|
15
|
+
p Service.exists?('bogusxxx')
|
16
|
+
|
17
|
+
status = Service.status('Schedule')
|
18
|
+
p status
|
19
|
+
|
20
|
+
info = Service.config_info('Schedule')
|
21
|
+
|
22
|
+
print "\n\nShowing config info for Schedule service\n\n"
|
23
|
+
p info
|
24
|
+
|
25
|
+
print "\n\nAbout to show all services\n\n"
|
26
|
+
sleep 10
|
27
|
+
|
28
|
+
Service.services{ |struct|
|
29
|
+
p struct
|
30
|
+
}
|
data/lib/win32/daemon.rb
ADDED
@@ -0,0 +1,345 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'windows', 'helper')
|
2
|
+
require File.join(File.dirname(__FILE__), 'windows', 'constants')
|
3
|
+
require File.join(File.dirname(__FILE__), 'windows', 'structs')
|
4
|
+
require File.join(File.dirname(__FILE__), 'windows', 'functions')
|
5
|
+
|
6
|
+
require 'ffi'
|
7
|
+
|
8
|
+
# The Win32 module serves as a namespace only.
|
9
|
+
module Win32
|
10
|
+
|
11
|
+
# The Daemon class
|
12
|
+
class Daemon
|
13
|
+
include Windows::Constants
|
14
|
+
include Windows::Structs
|
15
|
+
include Windows::Functions
|
16
|
+
|
17
|
+
extend Windows::Structs
|
18
|
+
extend Windows::Functions
|
19
|
+
|
20
|
+
# The version of this library
|
21
|
+
VERSION = '0.8.0'
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Service is not running
|
26
|
+
STOPPED = SERVICE_STOPPED
|
27
|
+
|
28
|
+
# Service has received a start signal but is not yet running
|
29
|
+
START_PENDING = SERVICE_START_PENDING
|
30
|
+
|
31
|
+
# Service has received a stop signal but is not yet stopped
|
32
|
+
STOP_PENDING = SERVICE_STOP_PENDING
|
33
|
+
|
34
|
+
# Service is running
|
35
|
+
RUNNING = SERVICE_RUNNING
|
36
|
+
|
37
|
+
# Service has received a signal to resume but is not yet running
|
38
|
+
CONTINUE_PENDING = SERVICE_CONTINUE_PENDING
|
39
|
+
|
40
|
+
# Service has received a signal to pause but is not yet paused
|
41
|
+
PAUSE_PENDING = SERVICE_PAUSE_PENDING
|
42
|
+
|
43
|
+
# Service is paused
|
44
|
+
PAUSED = SERVICE_PAUSED
|
45
|
+
|
46
|
+
# Service controls
|
47
|
+
|
48
|
+
# Notifies service that it should stop
|
49
|
+
CONTROL_STOP = SERVICE_CONTROL_STOP
|
50
|
+
|
51
|
+
# Notifies service that it should pause
|
52
|
+
CONTROL_PAUSE = SERVICE_CONTROL_PAUSE
|
53
|
+
|
54
|
+
# Notifies service that it should resume
|
55
|
+
CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE
|
56
|
+
|
57
|
+
# Notifies service that it should return its current status information
|
58
|
+
CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE
|
59
|
+
|
60
|
+
# Notifies a service that its parameters have changed
|
61
|
+
CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE
|
62
|
+
|
63
|
+
# Notifies a service that there is a new component for binding
|
64
|
+
CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD
|
65
|
+
|
66
|
+
# Notifies a service that a component for binding has been removed
|
67
|
+
CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE
|
68
|
+
|
69
|
+
# Notifies a service that a component for binding has been enabled
|
70
|
+
CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE
|
71
|
+
|
72
|
+
# Notifies a service that a component for binding has been disabled
|
73
|
+
CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE
|
74
|
+
|
75
|
+
IDLE = 0
|
76
|
+
|
77
|
+
# Wraps SetServiceStatus.
|
78
|
+
SetTheServiceStatus = Proc.new do |dwCurrentState, dwWin32ExitCode,dwCheckPoint, dwWaitHint|
|
79
|
+
ss = SERVICE_STATUS.new # Current status of the service.
|
80
|
+
|
81
|
+
# Disable control requests until the service is started.
|
82
|
+
if dwCurrentState == SERVICE_START_PENDING
|
83
|
+
ss[:dwControlsAccepted] = 0
|
84
|
+
else
|
85
|
+
ss[:dwControlsAccepted] =
|
86
|
+
SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
|
87
|
+
SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN
|
88
|
+
end
|
89
|
+
|
90
|
+
# Initialize ss structure.
|
91
|
+
ss[:dwServiceType] = SERVICE_WIN32_OWN_PROCESS
|
92
|
+
ss[:dwServiceSpecificExitCode] = 0
|
93
|
+
ss[:dwCurrentState] = dwCurrentState
|
94
|
+
ss[:dwWin32ExitCode] = dwWin32ExitCode
|
95
|
+
ss[:dwCheckPoint] = dwCheckPoint
|
96
|
+
ss[:dwWaitHint] = dwWaitHint
|
97
|
+
|
98
|
+
@@dwServiceState = dwCurrentState
|
99
|
+
|
100
|
+
# Send status of the service to the Service Controller.
|
101
|
+
if !SetServiceStatus(@@ssh, ss)
|
102
|
+
SetEvent(@@hStopEvent)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Handles control signals from the service control manager.
|
107
|
+
Service_Ctrl_ex = Proc.new do |dwCtrlCode,dwEventType,lpEventData,lpContext|
|
108
|
+
@@waiting_control_code = dwCtrlCode;
|
109
|
+
|
110
|
+
dwState = SERVICE_RUNNING
|
111
|
+
|
112
|
+
case dwCtrlCode
|
113
|
+
when SERVICE_CONTROL_STOP
|
114
|
+
dwState = SERVICE_STOP_PENDING
|
115
|
+
when SERVICE_CONTROL_SHUTDOWN
|
116
|
+
dwState = SERVICE_STOP_PENDING
|
117
|
+
when SERVICE_CONTROL_PAUSE
|
118
|
+
dwState = SERVICE_PAUSED
|
119
|
+
when SERVICE_CONTROL_CONTINUE
|
120
|
+
dwState = SERVICE_RUNNING
|
121
|
+
end
|
122
|
+
|
123
|
+
# Set the status of the service.
|
124
|
+
SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)
|
125
|
+
|
126
|
+
# Tell service_main thread to stop.
|
127
|
+
if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN
|
128
|
+
if !SetEvent(@@hStopEvent)
|
129
|
+
SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Called by the service control manager after the call to StartServiceCtrlDispatcher.
|
135
|
+
Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc,lpszArgv|
|
136
|
+
# Obtain the name of the service.
|
137
|
+
if lpszArgv.address!=0
|
138
|
+
argv = lpszArgv.get_array_of_string(0,dwArgc)
|
139
|
+
lpszServiceName = argv[0]
|
140
|
+
else
|
141
|
+
lpszServiceName = ''
|
142
|
+
end
|
143
|
+
|
144
|
+
# Args passed to Service.start
|
145
|
+
if(dwArgc > 1)
|
146
|
+
@@Argv = argv[1..-1]
|
147
|
+
else
|
148
|
+
@@Argv = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
# Register the service ctrl handler.
|
152
|
+
@@ssh = RegisterServiceCtrlHandlerEx(
|
153
|
+
lpszServiceName,
|
154
|
+
Service_Ctrl_ex,
|
155
|
+
nil
|
156
|
+
)
|
157
|
+
|
158
|
+
# No service to stop, no service handle to notify, nothing to do but exit.
|
159
|
+
return if @@ssh == 0
|
160
|
+
|
161
|
+
# The service has started.
|
162
|
+
SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)
|
163
|
+
|
164
|
+
SetEvent(@@hStartEvent)
|
165
|
+
|
166
|
+
# Main loop for the service.
|
167
|
+
while(WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0) do
|
168
|
+
end
|
169
|
+
|
170
|
+
# Main loop for the service.
|
171
|
+
while(WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0) do
|
172
|
+
end
|
173
|
+
|
174
|
+
# Stop the service.
|
175
|
+
SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)
|
176
|
+
end
|
177
|
+
|
178
|
+
ThreadProc = FFI::Function.new(:ulong,[:pointer]) do |lpParameter|
|
179
|
+
ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
|
180
|
+
|
181
|
+
s = SERVICE_TABLE_ENTRY.new(ste[0])
|
182
|
+
s[:lpServiceName] = FFI::MemoryPointer.from_string('')
|
183
|
+
s[:lpServiceProc] = Service_Main
|
184
|
+
|
185
|
+
s = SERVICE_TABLE_ENTRY.new(ste[1])
|
186
|
+
s[:lpServiceName] = nil
|
187
|
+
s[:lpServiceProc] = nil
|
188
|
+
|
189
|
+
# No service to step, no service handle, no ruby exceptions, just terminate the thread..
|
190
|
+
if !StartServiceCtrlDispatcher(ste)
|
191
|
+
return 1
|
192
|
+
end
|
193
|
+
|
194
|
+
return 0
|
195
|
+
end
|
196
|
+
|
197
|
+
public
|
198
|
+
|
199
|
+
# This is a shortcut for Daemon.new + Daemon#mainloop.
|
200
|
+
#
|
201
|
+
def self.mainloop
|
202
|
+
self.new.mainloop
|
203
|
+
end
|
204
|
+
|
205
|
+
# This is the method that actually puts your code into a loop and allows it
|
206
|
+
# to run as a service. The code that is actually run while in the mainloop
|
207
|
+
# is what you defined in your own Daemon#service_main method.
|
208
|
+
#
|
209
|
+
def mainloop
|
210
|
+
@@waiting_control_code = IDLE_CONTROL_CODE
|
211
|
+
@@dwServiceState = 0
|
212
|
+
|
213
|
+
# Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
|
214
|
+
# associated with a tty. This helps newbs avoid Errno::EBADF errors.
|
215
|
+
STDIN.reopen('NUL') if STDIN.isatty
|
216
|
+
STDOUT.reopen('NUL') if STDOUT.isatty
|
217
|
+
STDERR.reopen('NUL') if STDERR.isatty
|
218
|
+
|
219
|
+
# Calling init here so that init failures never even tries to start the
|
220
|
+
# service. Of course that means that init methods must be very quick
|
221
|
+
# because the SCM will be receiving no START_PENDING messages while
|
222
|
+
# init's running.
|
223
|
+
#
|
224
|
+
# TODO: Fix?
|
225
|
+
service_init() if respond_to?('service_init')
|
226
|
+
|
227
|
+
# Create the event to signal the service to start.
|
228
|
+
@@hStartEvent = CreateEvent(nil, true, false, nil)
|
229
|
+
|
230
|
+
if @@hStartEvent == 0
|
231
|
+
raise SystemCallError.new('CreateEvent', FFI.errno)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Create the event to signal the service to stop.
|
235
|
+
@@hStopEvent = CreateEvent(nil, true, false, nil)
|
236
|
+
|
237
|
+
if @@hStopEvent == 0
|
238
|
+
raise SystemCallError.new('CreateEvent', FFI.errno)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Create the event to signal the service that stop has completed
|
242
|
+
@@hStopCompletedEvent = CreateEvent(nil, true, false, nil)
|
243
|
+
|
244
|
+
if @@hStopCompletedEvent == 0
|
245
|
+
raise SystemCallError.new('CreateEvent', FFI.errno)
|
246
|
+
end
|
247
|
+
|
248
|
+
hThread = CreateThread(nil, 0, ThreadProc, nil, 0, nil)
|
249
|
+
|
250
|
+
if hThread == 0
|
251
|
+
raise SystemCallError.new('CreateThread', FFI.errno)
|
252
|
+
end
|
253
|
+
|
254
|
+
events = FFI::MemoryPointer.new(:pointer, FFI::Pointer.size*2)
|
255
|
+
events.put_pointer(0, FFI::Pointer.new(hThread))
|
256
|
+
events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
|
257
|
+
|
258
|
+
while ((index = WaitForMultipleObjects(2, events, false, 1000)) == WAIT_TIMEOUT) do
|
259
|
+
end
|
260
|
+
|
261
|
+
if index == WAIT_FAILED
|
262
|
+
raise SystemCallError.new('WaitForMultipleObjects', FFI.errno)
|
263
|
+
end
|
264
|
+
|
265
|
+
# The thread exited, so the show is off.
|
266
|
+
if index == WAIT_OBJECT_0
|
267
|
+
raise "Service_Main thread exited abnormally"
|
268
|
+
end
|
269
|
+
|
270
|
+
thr = Thread.new do
|
271
|
+
while(WaitForSingleObject(@@hStopEvent, 10) == WAIT_TIMEOUT)
|
272
|
+
# Check to see if anything interesting has been signaled
|
273
|
+
case @@waiting_control_code
|
274
|
+
when SERVICE_CONTROL_PAUSE
|
275
|
+
service_pause() if respond_to?('service_pause')
|
276
|
+
when SERVICE_CONTROL_CONTINUE
|
277
|
+
service_resume() if respond_to?('service_resume')
|
278
|
+
when SERVICE_CONTROL_INTERROGATE
|
279
|
+
service_interrogate() if respond_to?('service_interrogate')
|
280
|
+
when SERVICE_CONTROL_SHUTDOWN
|
281
|
+
service_shutdown() if respond_to?('service_shutdown')
|
282
|
+
when SERVICE_CONTROL_PARAMCHANGE
|
283
|
+
service_paramchange() if respond_to?('service_paramchange')
|
284
|
+
when SERVICE_CONTROL_NETBINDADD
|
285
|
+
service_netbindadd() if respond_to?('service_netbindadd')
|
286
|
+
when SERVICE_CONTROL_NETBINDREMOVE
|
287
|
+
service_netbindremove() if respond_to?('service_netbindremove')
|
288
|
+
when SERVICE_CONTROL_NETBINDENABLE
|
289
|
+
service_netbindenable() if respond_to?('service_netbindenable')
|
290
|
+
when SERVICE_CONTROL_NETBINDDISABLE
|
291
|
+
service_netbinddisable() if respond_to?('service_netbinddisable')
|
292
|
+
end
|
293
|
+
@@waiting_control_code = IDLE_CONTROL_CODE
|
294
|
+
end
|
295
|
+
|
296
|
+
service_stop() if respond_to?('service_stop')
|
297
|
+
end
|
298
|
+
|
299
|
+
if respond_to?('service_main')
|
300
|
+
service_main(*@@Argv)
|
301
|
+
end
|
302
|
+
|
303
|
+
thr.join
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns the state of the service (as an constant integer) which can be any
|
307
|
+
# of the service status constants, e.g. RUNNING, PAUSED, etc.
|
308
|
+
#
|
309
|
+
# This method is typically used within your service_main method to setup the
|
310
|
+
# loop. For example:
|
311
|
+
#
|
312
|
+
# class MyDaemon < Daemon
|
313
|
+
# def service_main
|
314
|
+
# while state == RUNNING || state == PAUSED || state == IDLE
|
315
|
+
# # Your main loop here
|
316
|
+
# end
|
317
|
+
# end
|
318
|
+
# end
|
319
|
+
#
|
320
|
+
# See the Daemon#running? method for an abstraction of the above code.
|
321
|
+
#
|
322
|
+
def state
|
323
|
+
@@dwServiceState
|
324
|
+
end
|
325
|
+
|
326
|
+
#
|
327
|
+
# Returns whether or not the service is in a running state, i.e. the service
|
328
|
+
# status is either RUNNING, PAUSED or IDLE.
|
329
|
+
#
|
330
|
+
# This is typically used within your service_main method to setup the main
|
331
|
+
# loop. For example:
|
332
|
+
#
|
333
|
+
# class MyDaemon < Daemon
|
334
|
+
# def service_main
|
335
|
+
# while running?
|
336
|
+
# # Your main loop here
|
337
|
+
# end
|
338
|
+
# end
|
339
|
+
# end
|
340
|
+
#
|
341
|
+
def running?
|
342
|
+
[SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)
|
343
|
+
end
|
344
|
+
end # Daemon
|
345
|
+
end # Win32
|
data/lib/win32/service.rb
CHANGED
@@ -1,11 +1,7 @@
|
|
1
|
-
require '
|
2
|
-
require 'windows
|
3
|
-
require 'windows
|
4
|
-
require 'windows
|
5
|
-
require 'windows/process'
|
6
|
-
require 'windows/security'
|
7
|
-
require 'windows/msvcrt/string'
|
8
|
-
require 'windows/msvcrt/buffer'
|
1
|
+
require File.join(File.dirname(__FILE__), 'windows', 'helper')
|
2
|
+
require File.join(File.dirname(__FILE__), 'windows', 'constants')
|
3
|
+
require File.join(File.dirname(__FILE__), 'windows', 'structs')
|
4
|
+
require File.join(File.dirname(__FILE__), 'windows', 'functions')
|
9
5
|
|
10
6
|
# The Win32 module serves as a namespace only.
|
11
7
|
module Win32
|
@@ -13,32 +9,18 @@ module Win32
|
|
13
9
|
# The Service class encapsulates services controller actions, such as
|
14
10
|
# creating, starting, configuring or deleting services.
|
15
11
|
class Service
|
12
|
+
include Windows::Constants
|
13
|
+
include Windows::Structs
|
14
|
+
include Windows::Functions
|
15
|
+
|
16
|
+
extend Windows::Structs
|
17
|
+
extend Windows::Functions
|
16
18
|
|
17
|
-
# This is the error typically raised if one of the Service methods
|
18
|
-
# should fail for any reason.
|
19
|
-
class Error < StandardError; end
|
20
|
-
|
21
|
-
include Windows::Error
|
22
|
-
include Windows::Service
|
23
|
-
include Windows::File
|
24
|
-
include Windows::Process
|
25
|
-
include Windows::Security
|
26
|
-
include Windows::MSVCRT::String
|
27
|
-
include Windows::MSVCRT::Buffer
|
28
|
-
|
29
|
-
extend Windows::Error
|
30
|
-
extend Windows::Service
|
31
|
-
extend Windows::File
|
32
|
-
extend Windows::Process
|
33
|
-
extend Windows::Security
|
34
|
-
extend Windows::MSVCRT::String
|
35
|
-
extend Windows::MSVCRT::Buffer
|
36
|
-
|
37
19
|
# The version of the win32-service library
|
38
|
-
VERSION = '0.
|
39
|
-
|
20
|
+
VERSION = '0.8.0'
|
21
|
+
|
40
22
|
# SCM security and access rights
|
41
|
-
|
23
|
+
|
42
24
|
# Includes STANDARD_RIGHTS_REQUIRED, in addition to all other rights
|
43
25
|
MANAGER_ALL_ACCESS = SC_MANAGER_ALL_ACCESS
|
44
26
|
|
@@ -89,7 +71,7 @@ module Win32
|
|
89
71
|
|
90
72
|
# Required to call ControlService with a user defined control code
|
91
73
|
USER_DEFINED_CONTROL = SERVICE_USER_DEFINED_CONTROL
|
92
|
-
|
74
|
+
|
93
75
|
# Service Types
|
94
76
|
|
95
77
|
# Driver service
|
@@ -109,7 +91,7 @@ module Win32
|
|
109
91
|
|
110
92
|
DRIVER = SERVICE_DRIVER
|
111
93
|
TYPE_ALL = SERVICE_TYPE_ALL
|
112
|
-
|
94
|
+
|
113
95
|
# Service start options
|
114
96
|
|
115
97
|
# A service started automatically by the SCM during system startup
|
@@ -126,7 +108,7 @@ module Win32
|
|
126
108
|
|
127
109
|
# A service that cannot be started
|
128
110
|
DISABLED = SERVICE_DISABLED
|
129
|
-
|
111
|
+
|
130
112
|
# Error control
|
131
113
|
|
132
114
|
# Error logged, startup continues
|
@@ -140,9 +122,9 @@ module Win32
|
|
140
122
|
|
141
123
|
# Error logged, startup fails, system restarted last known good config
|
142
124
|
ERROR_CRITICAL = SERVICE_ERROR_CRITICAL
|
143
|
-
|
125
|
+
|
144
126
|
# Current state
|
145
|
-
|
127
|
+
|
146
128
|
# Service is not running
|
147
129
|
STOPPED = SERVICE_STOPPED
|
148
130
|
|
@@ -163,7 +145,7 @@ module Win32
|
|
163
145
|
|
164
146
|
# Service is paused
|
165
147
|
PAUSED = SERVICE_PAUSED
|
166
|
-
|
148
|
+
|
167
149
|
# Service controls
|
168
150
|
|
169
151
|
# Notifies service that it should stop
|
@@ -206,9 +188,9 @@ module Win32
|
|
206
188
|
|
207
189
|
# Run a command
|
208
190
|
ACTION_RUN_COMMAND = SC_ACTION_RUN_COMMAND
|
209
|
-
|
191
|
+
|
210
192
|
# :stopdoc: #
|
211
|
-
|
193
|
+
|
212
194
|
StatusStruct = Struct.new(
|
213
195
|
'ServiceStatus',
|
214
196
|
:service_type,
|
@@ -235,7 +217,7 @@ module Win32
|
|
235
217
|
:service_start_name,
|
236
218
|
:display_name
|
237
219
|
)
|
238
|
-
|
220
|
+
|
239
221
|
ServiceStruct = Struct.new(
|
240
222
|
'ServiceInfo',
|
241
223
|
:service_name,
|
@@ -266,7 +248,7 @@ module Win32
|
|
266
248
|
)
|
267
249
|
|
268
250
|
# :startdoc: #
|
269
|
-
|
251
|
+
|
270
252
|
# Creates a new service with the specified +options+. A +service_name+
|
271
253
|
# must be specified or an ArgumentError is raised. A +host+ option may
|
272
254
|
# be specified. If no host is specified the local machine is used.
|
@@ -311,7 +293,7 @@ module Win32
|
|
311
293
|
# :display_name => 'This is some service',
|
312
294
|
# )
|
313
295
|
#
|
314
|
-
def initialize(options={})
|
296
|
+
def initialize(options={})
|
315
297
|
unless options.is_a?(Hash)
|
316
298
|
raise ArgumentError, 'options parameter must be a hash'
|
317
299
|
end
|
@@ -339,7 +321,7 @@ module Win32
|
|
339
321
|
'failure_actions' => nil,
|
340
322
|
'failure_delay' => 0,
|
341
323
|
'host' => nil,
|
342
|
-
'service_name' => nil
|
324
|
+
'service_name' => nil
|
343
325
|
}
|
344
326
|
|
345
327
|
# Validate the hash options
|
@@ -350,24 +332,22 @@ module Win32
|
|
350
332
|
end
|
351
333
|
opts[key] = value
|
352
334
|
}
|
353
|
-
|
335
|
+
|
354
336
|
unless opts['service_name']
|
355
|
-
raise ArgumentError, 'No service_name specified'
|
337
|
+
raise ArgumentError, 'No service_name specified'
|
356
338
|
end
|
357
|
-
|
339
|
+
|
358
340
|
service_name = opts.delete('service_name')
|
359
341
|
host = opts.delete('host')
|
360
|
-
|
342
|
+
|
361
343
|
raise TypeError unless service_name.is_a?(String)
|
362
344
|
raise TypeError if host && !host.is_a?(String)
|
363
345
|
|
364
346
|
begin
|
365
|
-
handle_scm = OpenSCManager(host,
|
347
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CREATE_SERVICE)
|
348
|
+
|
349
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
366
350
|
|
367
|
-
if handle_scm == 0
|
368
|
-
raise Error, get_last_error
|
369
|
-
end
|
370
|
-
|
371
351
|
# Display name defaults to service_name
|
372
352
|
opts['display_name'] ||= service_name
|
373
353
|
|
@@ -377,12 +357,13 @@ module Win32
|
|
377
357
|
unless dependencies.is_a?(Array) || dependencies.is_a?(String)
|
378
358
|
raise TypeError, 'dependencies must be a string or array'
|
379
359
|
end
|
380
|
-
|
360
|
+
|
381
361
|
if dependencies.is_a?(Array)
|
382
362
|
dependencies = dependencies.join("\000")
|
383
363
|
end
|
384
|
-
|
364
|
+
|
385
365
|
dependencies += "\000"
|
366
|
+
dependencies = FFI::MemoryPointer.from_string(dependencies)
|
386
367
|
end
|
387
368
|
|
388
369
|
handle_scs = CreateService(
|
@@ -395,19 +376,17 @@ module Win32
|
|
395
376
|
opts['error_control'],
|
396
377
|
opts['binary_path_name'],
|
397
378
|
opts['load_order_group'],
|
398
|
-
|
379
|
+
nil,
|
399
380
|
dependencies,
|
400
381
|
opts['service_start_name'],
|
401
382
|
opts['password']
|
402
383
|
)
|
403
384
|
|
404
|
-
if handle_scs == 0
|
405
|
-
raise Error, get_last_error
|
406
|
-
end
|
385
|
+
FFI.raise_windows_error('CreateService') if handle_scs == 0
|
407
386
|
|
408
387
|
if opts['description']
|
409
|
-
description =
|
410
|
-
description[
|
388
|
+
description = SERVICE_DESCRIPTION.new
|
389
|
+
description[:lpDescription] = FFI::MemoryPointer.from_string(opts['description'])
|
411
390
|
|
412
391
|
bool = ChangeServiceConfig2(
|
413
392
|
handle_scs,
|
@@ -415,16 +394,14 @@ module Win32
|
|
415
394
|
description
|
416
395
|
)
|
417
396
|
|
418
|
-
unless bool
|
419
|
-
raise Error, get_last_error
|
420
|
-
end
|
397
|
+
FFI.raise_windows_error('ChangeServiceConfig2') unless bool
|
421
398
|
end
|
422
|
-
|
399
|
+
|
423
400
|
if opts['failure_reset_period'] || opts['failure_reboot_message'] ||
|
424
401
|
opts['failure_command'] || opts['failure_actions']
|
425
402
|
then
|
426
403
|
Service.configure_failure_actions(handle_scs, opts)
|
427
|
-
end
|
404
|
+
end
|
428
405
|
ensure
|
429
406
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
430
407
|
CloseServiceHandle(handle_scm) if handle_scm && handle_scm > 0
|
@@ -476,7 +453,7 @@ module Win32
|
|
476
453
|
# :description => 'A custom service I wrote just for fun'
|
477
454
|
# )
|
478
455
|
#
|
479
|
-
def self.configure(options={})
|
456
|
+
def self.configure(options={})
|
480
457
|
unless options.is_a?(Hash)
|
481
458
|
raise ArgumentError, 'options parameter must be a hash'
|
482
459
|
end
|
@@ -513,11 +490,11 @@ module Win32
|
|
513
490
|
end
|
514
491
|
opts[key] = value
|
515
492
|
}
|
516
|
-
|
493
|
+
|
517
494
|
unless opts['service_name']
|
518
|
-
raise ArgumentError, 'No service_name specified'
|
495
|
+
raise ArgumentError, 'No service_name specified'
|
519
496
|
end
|
520
|
-
|
497
|
+
|
521
498
|
service = opts.delete('service_name')
|
522
499
|
host = opts.delete('host')
|
523
500
|
|
@@ -525,14 +502,12 @@ module Win32
|
|
525
502
|
raise TypeError unless host.is_a?(String) if host
|
526
503
|
|
527
504
|
begin
|
528
|
-
handle_scm = OpenSCManager(host,
|
505
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
|
506
|
+
|
507
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
529
508
|
|
530
|
-
if handle_scm == 0
|
531
|
-
raise Error, get_last_error
|
532
|
-
end
|
533
|
-
|
534
509
|
desired_access = SERVICE_CHANGE_CONFIG
|
535
|
-
|
510
|
+
|
536
511
|
if opts['failure_actions']
|
537
512
|
desired_access |= SERVICE_START
|
538
513
|
end
|
@@ -543,9 +518,7 @@ module Win32
|
|
543
518
|
desired_access
|
544
519
|
)
|
545
520
|
|
546
|
-
if handle_scs == 0
|
547
|
-
raise Error, get_last_error
|
548
|
-
end
|
521
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
549
522
|
|
550
523
|
dependencies = opts['dependencies']
|
551
524
|
|
@@ -557,7 +530,7 @@ module Win32
|
|
557
530
|
if dependencies.is_a?(Array)
|
558
531
|
dependencies = dependencies.join("\000")
|
559
532
|
end
|
560
|
-
|
533
|
+
|
561
534
|
dependencies += "\000"
|
562
535
|
end
|
563
536
|
|
@@ -568,20 +541,18 @@ module Win32
|
|
568
541
|
opts['error_control'],
|
569
542
|
opts['binary_path_name'],
|
570
543
|
opts['load_order_group'],
|
571
|
-
|
544
|
+
nil,
|
572
545
|
dependencies,
|
573
546
|
opts['service_start_name'],
|
574
547
|
opts['password'],
|
575
548
|
opts['display_name']
|
576
549
|
)
|
577
550
|
|
578
|
-
unless bool
|
579
|
-
raise Error, get_last_error
|
580
|
-
end
|
551
|
+
FFI.raise_windows_error('ChangeServiceConfig') unless bool
|
581
552
|
|
582
553
|
if opts['description']
|
583
|
-
description =
|
584
|
-
description[
|
554
|
+
description = SERVICE_DESCRIPTION.new
|
555
|
+
description[:lpDescription] = FFI::MemoryPointer.from_string(opts['description'])
|
585
556
|
|
586
557
|
bool = ChangeServiceConfig2(
|
587
558
|
handle_scs,
|
@@ -589,9 +560,7 @@ module Win32
|
|
589
560
|
description
|
590
561
|
)
|
591
562
|
|
592
|
-
unless bool
|
593
|
-
raise Error, get_last_error
|
594
|
-
end
|
563
|
+
FFI.raise_windows_error('ChangeServiceConfig2') unless bool
|
595
564
|
end
|
596
565
|
|
597
566
|
if opts['failure_reset_period'] || opts['failure_reboot_message'] ||
|
@@ -606,24 +575,22 @@ module Win32
|
|
606
575
|
|
607
576
|
self
|
608
577
|
end
|
609
|
-
|
578
|
+
|
610
579
|
# Returns whether or not +service+ exists on +host+ or localhost, if
|
611
580
|
# no host is specified.
|
612
581
|
#
|
613
582
|
# Example:
|
614
583
|
#
|
615
584
|
# Service.exists?('W32Time') => true
|
616
|
-
#
|
585
|
+
#
|
617
586
|
def self.exists?(service, host=nil)
|
618
587
|
bool = false
|
619
588
|
|
620
589
|
begin
|
621
|
-
handle_scm = OpenSCManager(host,
|
622
|
-
|
623
|
-
if handle_scm == 0
|
624
|
-
|
625
|
-
end
|
626
|
-
|
590
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
|
591
|
+
|
592
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
593
|
+
|
627
594
|
handle_scs = OpenService(handle_scm, service, SERVICE_QUERY_STATUS)
|
628
595
|
bool = true if handle_scs > 0
|
629
596
|
ensure
|
@@ -631,9 +598,9 @@ module Win32
|
|
631
598
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
632
599
|
end
|
633
600
|
|
634
|
-
bool
|
601
|
+
bool
|
635
602
|
end
|
636
|
-
|
603
|
+
|
637
604
|
# Returns the display name of the specified service name, i.e. the string
|
638
605
|
# displayed in the Services GUI. Raises a Service::Error if the service
|
639
606
|
# name cannot be found.
|
@@ -647,33 +614,31 @@ module Win32
|
|
647
614
|
# Service.get_display_name('W32Time') => 'Windows Time'
|
648
615
|
#
|
649
616
|
def self.get_display_name(service, host=nil)
|
650
|
-
handle_scm = OpenSCManager(host,
|
651
|
-
|
652
|
-
if handle_scm == 0
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
display_name
|
657
|
-
display_buf = [display_name.size].pack('L')
|
617
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
|
618
|
+
|
619
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
620
|
+
|
621
|
+
display_name = FFI::MemoryPointer.new(260)
|
622
|
+
display_size = FFI::MemoryPointer.new(:ulong)
|
623
|
+
display_size.write_ulong(display_name.size)
|
658
624
|
|
659
625
|
begin
|
660
626
|
bool = GetServiceDisplayName(
|
661
627
|
handle_scm,
|
662
628
|
service,
|
663
629
|
display_name,
|
664
|
-
|
630
|
+
display_size
|
665
631
|
)
|
666
632
|
|
667
|
-
|
668
|
-
|
669
|
-
end
|
633
|
+
|
634
|
+
FFI.raise_windows_error('OpenSCManager') unless bool
|
670
635
|
ensure
|
671
636
|
CloseServiceHandle(handle_scm)
|
672
637
|
end
|
673
638
|
|
674
|
-
display_name.
|
639
|
+
display_name.read_string
|
675
640
|
end
|
676
|
-
|
641
|
+
|
677
642
|
# Returns the service name of the specified service from the provided
|
678
643
|
# +display_name+. Raises a Service::Error if the +display_name+ cannote
|
679
644
|
# be found.
|
@@ -687,31 +652,28 @@ module Win32
|
|
687
652
|
# Service.get_service_name('Windows Time') => 'W32Time'
|
688
653
|
#
|
689
654
|
def self.get_service_name(display_name, host=nil)
|
690
|
-
handle_scm = OpenSCManager(host,
|
691
|
-
|
692
|
-
if handle_scm == 0
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
service_name
|
697
|
-
|
698
|
-
|
655
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
|
656
|
+
|
657
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
658
|
+
|
659
|
+
service_name = FFI::MemoryPointer.new(260)
|
660
|
+
service_size = FFI::MemoryPointer.new(:ulong)
|
661
|
+
service_size.write_ulong(service_name.size)
|
662
|
+
|
699
663
|
begin
|
700
664
|
bool = GetServiceKeyName(
|
701
665
|
handle_scm,
|
702
666
|
display_name,
|
703
667
|
service_name,
|
704
|
-
|
668
|
+
service_size
|
705
669
|
)
|
706
670
|
|
707
|
-
unless bool
|
708
|
-
raise Error, get_last_error
|
709
|
-
end
|
671
|
+
FFI.raise_windows_error('GetServiceKeyName') unless bool
|
710
672
|
ensure
|
711
673
|
CloseServiceHandle(handle_scm)
|
712
674
|
end
|
713
675
|
|
714
|
-
service_name.
|
676
|
+
service_name.read_string
|
715
677
|
end
|
716
678
|
|
717
679
|
# Attempts to start the named +service+ on +host+, or the local machine
|
@@ -725,42 +687,53 @@ module Win32
|
|
725
687
|
#
|
726
688
|
# # Start 'SomeSvc' on host 'foo', passing 'hello' as an argument
|
727
689
|
# Service.start('SomeSvc', 'foo', 'hello') => self
|
728
|
-
#
|
690
|
+
#
|
729
691
|
def self.start(service, host=nil, *args)
|
730
692
|
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
|
731
|
-
|
732
|
-
if handle_scm == 0
|
733
|
-
|
734
|
-
end
|
735
|
-
|
693
|
+
|
694
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
695
|
+
|
736
696
|
begin
|
737
697
|
handle_scs = OpenService(handle_scm, service, SERVICE_START)
|
738
|
-
|
739
|
-
if handle_scs == 0
|
740
|
-
|
741
|
-
end
|
742
|
-
|
698
|
+
|
699
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
700
|
+
|
743
701
|
num_args = 0
|
744
|
-
|
702
|
+
|
745
703
|
if args.empty?
|
746
704
|
args = nil
|
747
|
-
else
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
705
|
+
else # TODO: Verify that this works
|
706
|
+
str_ptrs = []
|
707
|
+
num_args = args.size
|
708
|
+
|
709
|
+
# First arg must be service name
|
710
|
+
str_ptrs << FFI::MemoryPointer.from_string(service)
|
711
|
+
|
712
|
+
args.each{ |string|
|
713
|
+
str_ptrs << FFI::MemoryPointer.from_string(string)
|
714
|
+
}
|
715
|
+
|
716
|
+
str_ptrs << nil
|
717
|
+
|
718
|
+
vector = FFI::MemoryPointer.new(:pointer, str_ptrs.size)
|
719
|
+
|
720
|
+
str_ptrs.each_with_index{ |p, i|
|
721
|
+
vector[i].put_pointer(0, p)
|
722
|
+
}
|
723
|
+
end
|
724
|
+
|
725
|
+
unless StartService(handle_scs, num_args, vector)
|
726
|
+
FFI.raise_windows_error('StartService')
|
754
727
|
end
|
755
|
-
|
728
|
+
|
756
729
|
ensure
|
757
730
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
758
731
|
CloseServiceHandle(handle_scm)
|
759
732
|
end
|
760
|
-
|
761
|
-
self
|
733
|
+
|
734
|
+
self
|
762
735
|
end
|
763
|
-
|
736
|
+
|
764
737
|
# Stops a the given +service+ on +host+, or the local host if no host
|
765
738
|
# is specified. Returns self.
|
766
739
|
#
|
@@ -770,14 +743,14 @@ module Win32
|
|
770
743
|
# Example:
|
771
744
|
#
|
772
745
|
# Service.stop('W32Time') => self
|
773
|
-
#
|
746
|
+
#
|
774
747
|
def self.stop(service, host=nil)
|
775
748
|
service_signal = SERVICE_STOP
|
776
749
|
control_signal = SERVICE_CONTROL_STOP
|
777
750
|
send_signal(service, host, service_signal, control_signal)
|
778
751
|
self
|
779
752
|
end
|
780
|
-
|
753
|
+
|
781
754
|
# Pauses the given +service+ on +host+, or the local host if no host
|
782
755
|
# is specified. Returns self
|
783
756
|
#
|
@@ -791,14 +764,14 @@ module Win32
|
|
791
764
|
# Example:
|
792
765
|
#
|
793
766
|
# Service.pause('Schedule') => self
|
794
|
-
#
|
767
|
+
#
|
795
768
|
def self.pause(service, host=nil)
|
796
769
|
service_signal = SERVICE_PAUSE_CONTINUE
|
797
770
|
control_signal = SERVICE_CONTROL_PAUSE
|
798
771
|
send_signal(service, host, service_signal, control_signal)
|
799
772
|
self
|
800
773
|
end
|
801
|
-
|
774
|
+
|
802
775
|
# Resume the given +service+ on +host+, or the local host if no host
|
803
776
|
# is specified. Returns self.
|
804
777
|
#
|
@@ -808,14 +781,14 @@ module Win32
|
|
808
781
|
# Example:
|
809
782
|
#
|
810
783
|
# Service.resume('Schedule') => self
|
811
|
-
#
|
784
|
+
#
|
812
785
|
def self.resume(service, host=nil)
|
813
786
|
service_signal = SERVICE_PAUSE_CONTINUE
|
814
787
|
control_signal = SERVICE_CONTROL_CONTINUE
|
815
788
|
send_signal(service, host, service_signal, control_signal)
|
816
789
|
self
|
817
790
|
end
|
818
|
-
|
791
|
+
|
819
792
|
# Deletes the specified +service+ from +host+, or the local host if
|
820
793
|
# no host is specified. Returns self.
|
821
794
|
#
|
@@ -830,21 +803,17 @@ module Win32
|
|
830
803
|
# Service.delete('SomeService') => self
|
831
804
|
#
|
832
805
|
def self.delete(service, host=nil)
|
833
|
-
handle_scm = OpenSCManager(host,
|
834
|
-
|
835
|
-
if handle_scm == 0
|
836
|
-
|
837
|
-
end
|
838
|
-
|
806
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CREATE_SERVICE)
|
807
|
+
|
808
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
809
|
+
|
839
810
|
begin
|
840
811
|
handle_scs = OpenService(handle_scm, service, DELETE)
|
841
812
|
|
842
|
-
if handle_scs == 0
|
843
|
-
|
844
|
-
end
|
845
|
-
|
813
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
814
|
+
|
846
815
|
unless DeleteService(handle_scs)
|
847
|
-
|
816
|
+
FFI.raise_windows_error('DeleteService')
|
848
817
|
end
|
849
818
|
ensure
|
850
819
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
@@ -871,72 +840,48 @@ module Win32
|
|
871
840
|
|
872
841
|
handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
|
873
842
|
|
874
|
-
if handle_scm == 0
|
875
|
-
raise Error, get_last_error
|
876
|
-
end
|
843
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
877
844
|
|
878
845
|
begin
|
879
846
|
handle_scs = OpenService(handle_scm, service, SERVICE_QUERY_CONFIG)
|
880
847
|
|
881
|
-
if handle_scs == 0
|
882
|
-
raise Error, get_last_error
|
883
|
-
end
|
848
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
884
849
|
|
885
850
|
# First, get the buf size needed
|
886
|
-
|
851
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
887
852
|
|
888
|
-
bool = QueryServiceConfig(handle_scs, nil, 0,
|
853
|
+
bool = QueryServiceConfig(handle_scs, nil, 0, bytes)
|
889
854
|
|
890
|
-
if !bool &&
|
891
|
-
|
855
|
+
if !bool && FFI.errno != ERROR_INSUFFICIENT_BUFFER
|
856
|
+
FFI.raise_windows_error('QueryServiceConfig')
|
892
857
|
end
|
893
858
|
|
894
|
-
buf =
|
895
|
-
bytes =
|
859
|
+
buf = FFI::MemoryPointer.new(:char, bytes.read_ulong)
|
860
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
896
861
|
|
897
|
-
bool = QueryServiceConfig(handle_scs, buf, buf.size,
|
862
|
+
bool = QueryServiceConfig(handle_scs, buf, buf.size, bytes)
|
898
863
|
|
899
|
-
|
900
|
-
|
901
|
-
|
864
|
+
struct = QUERY_SERVICE_CONFIG.new(buf) # cast the buffer
|
865
|
+
|
866
|
+
FFI.raise_windows_error('QueryServiceConfig') unless bool
|
902
867
|
ensure
|
903
868
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
904
869
|
CloseServiceHandle(handle_scm)
|
905
870
|
end
|
906
871
|
|
907
|
-
binary_path_name = 0.chr * 1024
|
908
|
-
load_order_group = 0.chr * 1024
|
909
|
-
dependencies = 0.chr * 1024
|
910
|
-
service_start_name = 0.chr * 260
|
911
|
-
display_name = 0.chr * 260
|
912
|
-
|
913
|
-
strcpy(binary_path_name, buf[12,4].unpack('L')[0])
|
914
|
-
binary_path_name = binary_path_name.unpack('Z*')[0]
|
915
|
-
|
916
|
-
strcpy(load_order_group, buf[16,4].unpack('L')[0])
|
917
|
-
load_order_group = load_order_group.unpack('Z*')[0]
|
918
|
-
|
919
|
-
dependencies = get_dependencies(buf[24,4].unpack('L').first)
|
920
|
-
|
921
|
-
strcpy(service_start_name, buf[28,4].unpack('L')[0])
|
922
|
-
service_start_name = service_start_name.unpack('Z*')[0]
|
923
|
-
|
924
|
-
strcpy(display_name, buf[32,4].unpack('L')[0])
|
925
|
-
display_name = display_name.unpack('Z*')[0]
|
926
|
-
|
927
872
|
ConfigStruct.new(
|
928
|
-
get_service_type(
|
929
|
-
get_start_type(
|
930
|
-
get_error_control(
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
873
|
+
get_service_type(struct[:dwServiceType]),
|
874
|
+
get_start_type(struct[:dwStartType]),
|
875
|
+
get_error_control(struct[:dwErrorControl]),
|
876
|
+
struct[:lpBinaryPathName].read_string,
|
877
|
+
struct[:lpLoadOrderGroup].read_string,
|
878
|
+
struct[:dwTagId],
|
879
|
+
struct[:lpDependencies].read_array_of_null_separated_strings,
|
880
|
+
struct[:lpServiceStartName].read_string,
|
881
|
+
struct[:lpDisplayName].read_string
|
937
882
|
)
|
938
883
|
end
|
939
|
-
|
884
|
+
|
940
885
|
# Returns a ServiceStatus struct indicating the status of service +name+
|
941
886
|
# on +host+, or the localhost if none is provided.
|
942
887
|
#
|
@@ -945,27 +890,23 @@ module Win32
|
|
945
890
|
# Service.status('W32Time') => <struct Struct::ServiceStatus ...>
|
946
891
|
#
|
947
892
|
def self.status(service, host=nil)
|
948
|
-
handle_scm = OpenSCManager(host,
|
949
|
-
|
950
|
-
if handle_scm == 0
|
951
|
-
|
952
|
-
end
|
953
|
-
|
893
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
|
894
|
+
|
895
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
896
|
+
|
954
897
|
begin
|
955
898
|
handle_scs = OpenService(
|
956
899
|
handle_scm,
|
957
900
|
service,
|
958
901
|
SERVICE_QUERY_STATUS
|
959
902
|
)
|
960
|
-
|
961
|
-
if handle_scs == 0
|
962
|
-
|
963
|
-
end
|
964
|
-
|
903
|
+
|
904
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
905
|
+
|
965
906
|
# SERVICE_STATUS_PROCESS struct
|
966
|
-
status =
|
967
|
-
bytes =
|
968
|
-
|
907
|
+
status = SERVICE_STATUS_PROCESS.new
|
908
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
909
|
+
|
969
910
|
bool = QueryServiceStatusEx(
|
970
911
|
handle_scs,
|
971
912
|
SC_STATUS_PROCESS_INFO,
|
@@ -973,40 +914,35 @@ module Win32
|
|
973
914
|
status.size,
|
974
915
|
bytes
|
975
916
|
)
|
976
|
-
|
977
|
-
unless bool
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
current_state = get_current_state(status[4,4].unpack('L').first)
|
985
|
-
controls = get_controls_accepted(status[8,4].unpack('L').first)
|
986
|
-
interactive = dw_service_type & SERVICE_INTERACTIVE_PROCESS > 0
|
987
|
-
|
917
|
+
|
918
|
+
FFI.raise_windows_error('QueryServiceStatusEx') unless bool
|
919
|
+
|
920
|
+
service_type = get_service_type(status[:dwServiceType])
|
921
|
+
current_state = get_current_state(status[:dwCurrentState])
|
922
|
+
controls = get_controls_accepted(status[:dwControlsAccepted])
|
923
|
+
interactive = status[:dwServiceType] & SERVICE_INTERACTIVE_PROCESS > 0
|
924
|
+
|
988
925
|
# Note that the pid and service flags will always return 0 if you're
|
989
926
|
# on Windows NT 4 or using a version of Ruby compiled with VC++ 6
|
990
927
|
# or earlier.
|
991
|
-
#
|
928
|
+
#
|
992
929
|
status_struct = StatusStruct.new(
|
993
930
|
service_type,
|
994
931
|
current_state,
|
995
932
|
controls,
|
996
|
-
status[
|
997
|
-
status[
|
998
|
-
status[
|
999
|
-
status[
|
933
|
+
status[:dwWin32ExitCode],
|
934
|
+
status[:dwServiceSpecificExitCode],
|
935
|
+
status[:dwCheckPoint],
|
936
|
+
status[:dwWaitHint],
|
1000
937
|
interactive,
|
1001
|
-
status[
|
1002
|
-
status[
|
938
|
+
status[:dwProcessId],
|
939
|
+
status[:dwServiceFlags]
|
1003
940
|
)
|
1004
|
-
|
1005
941
|
ensure
|
1006
942
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
1007
943
|
CloseServiceHandle(handle_scm)
|
1008
944
|
end
|
1009
|
-
|
945
|
+
|
1010
946
|
status_struct
|
1011
947
|
end
|
1012
948
|
|
@@ -1030,25 +966,23 @@ module Win32
|
|
1030
966
|
#
|
1031
967
|
# # Enumerate over all 'network' services locally
|
1032
968
|
# Service.services(nil, 'network'){ |service| p service }
|
1033
|
-
#
|
969
|
+
#
|
1034
970
|
def self.services(host=nil, group=nil)
|
1035
971
|
unless host.nil?
|
1036
972
|
raise TypeError unless host.is_a?(String) # Avoid strange errors
|
1037
973
|
end
|
1038
|
-
|
974
|
+
|
1039
975
|
unless group.nil?
|
1040
976
|
raise TypeError unless group.is_a?(String) # Avoid strange errors
|
1041
977
|
end
|
1042
|
-
|
1043
|
-
handle_scm = OpenSCManager(host,
|
1044
|
-
|
1045
|
-
if handle_scm == 0
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
services_returned = [0].pack('L')
|
1051
|
-
resume_handle = [0].pack('L')
|
978
|
+
|
979
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
|
980
|
+
|
981
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
982
|
+
|
983
|
+
bytes_needed = FFI::MemoryPointer.new(:ulong)
|
984
|
+
services_returned = FFI::MemoryPointer.new(:ulong)
|
985
|
+
resume_handle = FFI::MemoryPointer.new(:ulong)
|
1052
986
|
|
1053
987
|
begin
|
1054
988
|
# The first call is used to determine the required buffer size
|
@@ -1057,7 +991,7 @@ module Win32
|
|
1057
991
|
SC_ENUM_PROCESS_INFO,
|
1058
992
|
SERVICE_WIN32 | SERVICE_DRIVER,
|
1059
993
|
SERVICE_STATE_ALL,
|
1060
|
-
|
994
|
+
nil,
|
1061
995
|
0,
|
1062
996
|
bytes_needed,
|
1063
997
|
services_returned,
|
@@ -1065,14 +999,12 @@ module Win32
|
|
1065
999
|
group
|
1066
1000
|
)
|
1067
1001
|
|
1068
|
-
|
1069
|
-
|
1070
|
-
if !bool && err_num == ERROR_MORE_DATA
|
1071
|
-
service_buf = 0.chr * bytes_needed.unpack('L').first
|
1002
|
+
if !bool && FFI.errno == ERROR_MORE_DATA
|
1003
|
+
service_buf = FFI::MemoryPointer.new(:char, bytes_needed.read_ulong)
|
1072
1004
|
else
|
1073
|
-
|
1005
|
+
FFI.raise_windows_error('EnumServiceStatusEx')
|
1074
1006
|
end
|
1075
|
-
|
1007
|
+
|
1076
1008
|
bool = EnumServicesStatusEx(
|
1077
1009
|
handle_scm,
|
1078
1010
|
SC_ENUM_PROCESS_INFO,
|
@@ -1085,40 +1017,31 @@ module Win32
|
|
1085
1017
|
resume_handle,
|
1086
1018
|
group
|
1087
1019
|
)
|
1088
|
-
|
1089
|
-
unless bool
|
1090
|
-
raise Error, get_last_error
|
1091
|
-
end
|
1092
1020
|
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1021
|
+
FFI.raise_windows_error('EnumServiceStatusEx') unless bool
|
1022
|
+
|
1023
|
+
num_services = services_returned.read_ulong
|
1024
|
+
|
1096
1025
|
services_array = [] unless block_given?
|
1097
1026
|
|
1098
1027
|
1.upto(num_services){ |num|
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
win_exit_code = info[20,4].unpack('L').first
|
1117
|
-
ser_exit_code = info[24,4].unpack('L').first
|
1118
|
-
check_point = info[28,4].unpack('L').first
|
1119
|
-
wait_hint = info[32,4].unpack('L').first
|
1120
|
-
pid = info[36,4].unpack('L').first
|
1121
|
-
service_flags = info[40,4].unpack('L').first
|
1028
|
+
# Cast the buffer
|
1029
|
+
struct = ENUM_SERVICE_STATUS_PROCESS.new(service_buf)
|
1030
|
+
|
1031
|
+
service_name = struct[:lpServiceName].read_string
|
1032
|
+
display_name = struct[:lpDisplayName].read_string
|
1033
|
+
|
1034
|
+
service_type = get_service_type(struct[:ServiceStatusProcess][:dwServiceType])
|
1035
|
+
current_state = get_current_state(struct[:ServiceStatusProcess][:dwCurrentState])
|
1036
|
+
controls = get_controls_accepted(struct[:ServiceStatusProcess][:dwControlsAccepted])
|
1037
|
+
|
1038
|
+
interactive = struct[:ServiceStatusProcess][:dwServiceType] & SERVICE_INTERACTIVE_PROCESS > 0
|
1039
|
+
win_exit_code = struct[:ServiceStatusProcess][:dwWin32ExitCode]
|
1040
|
+
ser_exit_code = struct[:ServiceStatusProcess][:dwServiceSpecificExitCode]
|
1041
|
+
check_point = struct[:ServiceStatusProcess][:dwCheckPoint]
|
1042
|
+
wait_hint = struct[:ServiceStatusProcess][:dwWaitHint]
|
1043
|
+
pid = struct[:ServiceStatusProcess][:dwProcessId]
|
1044
|
+
service_flags = struct[:ServiceStatusProcess][:dwServiceFlags]
|
1122
1045
|
|
1123
1046
|
begin
|
1124
1047
|
handle_scs = OpenService(
|
@@ -1126,43 +1049,34 @@ module Win32
|
|
1126
1049
|
service_name,
|
1127
1050
|
SERVICE_QUERY_CONFIG
|
1128
1051
|
)
|
1129
|
-
|
1130
|
-
if handle_scs == 0
|
1131
|
-
raise Error, get_last_error
|
1132
|
-
end
|
1133
1052
|
|
1134
|
-
|
1053
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
1135
1054
|
|
1136
|
-
|
1137
|
-
binary_path = 0.chr * 1024
|
1138
|
-
strcpy(binary_path, config_buf[12,4].unpack('L').first)
|
1139
|
-
binary_path = binary_path.unpack('Z*')[0]
|
1055
|
+
config_struct = get_config_info(handle_scs)
|
1140
1056
|
|
1141
|
-
|
1142
|
-
|
1143
|
-
load_order
|
1144
|
-
|
1145
|
-
|
1146
|
-
strcpy(start_name, config_buf[28,4].unpack('L').first)
|
1147
|
-
start_name = start_name.unpack('Z*')[0]
|
1148
|
-
|
1149
|
-
start_type = get_start_type(config_buf[4,4].unpack('L').first)
|
1150
|
-
error_ctrl = get_error_control(config_buf[8,4].unpack('L').first)
|
1057
|
+
if config_struct != ERROR_FILE_NOT_FOUND
|
1058
|
+
binary_path = config_struct[:lpBinaryPathName].read_string
|
1059
|
+
load_order = config_struct[:lpLoadOrderGroup].read_string
|
1060
|
+
start_name = config_struct[:lpServiceStartName].read_string
|
1061
|
+
tag_id = config_struct[:dwTagId]
|
1151
1062
|
|
1152
|
-
|
1063
|
+
start_type = get_start_type(config_struct[:dwStartType])
|
1064
|
+
error_ctrl = get_error_control(config_struct[:dwErrorControl])
|
1153
1065
|
|
1154
|
-
deps =
|
1066
|
+
deps = config_struct[:lpDependencies].read_array_of_null_separated_strings
|
1155
1067
|
|
1156
|
-
|
1157
|
-
buf = get_config2_info(handle_scs, SERVICE_CONFIG_DESCRIPTION)
|
1068
|
+
buf = get_config2_info(handle_scs, SERVICE_CONFIG_DESCRIPTION)
|
1158
1069
|
|
1159
|
-
|
1160
|
-
|
1070
|
+
if buf.read_pointer.null?
|
1071
|
+
description = ''
|
1072
|
+
else
|
1073
|
+
description = buf.read_pointer.read_string
|
1074
|
+
end
|
1161
1075
|
else
|
1162
1076
|
msg = "WARNING: The registry entry for the #{service_name} "
|
1163
1077
|
msg += "service could not be found."
|
1164
1078
|
warn msg
|
1165
|
-
|
1079
|
+
|
1166
1080
|
binary_path = nil
|
1167
1081
|
load_order = nil
|
1168
1082
|
start_name = nil
|
@@ -1172,47 +1086,51 @@ module Win32
|
|
1172
1086
|
deps = nil
|
1173
1087
|
description = nil
|
1174
1088
|
end
|
1175
|
-
|
1089
|
+
|
1176
1090
|
buf2 = get_config2_info(handle_scs, SERVICE_CONFIG_FAILURE_ACTIONS)
|
1177
|
-
|
1178
|
-
if buf2
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1091
|
+
|
1092
|
+
if buf2.is_a?(FFI::MemoryPointer)
|
1093
|
+
fail_struct = SERVICE_FAILURE_ACTIONS.new(buf2)
|
1094
|
+
|
1095
|
+
reset_period = fail_struct[:dwResetPeriod]
|
1096
|
+
num_actions = fail_struct[:cActions]
|
1097
|
+
|
1098
|
+
if fail_struct[:lpRebootMsg].null?
|
1099
|
+
reboot_msg = nil
|
1100
|
+
else
|
1101
|
+
reboot_msg = fail_struct[:lpRebootMsg].read_string
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
if fail_struct[:lpCommand].null?
|
1105
|
+
command = nil
|
1106
|
+
else
|
1107
|
+
command = fail_struct[:lpCommand].read_string
|
1108
|
+
end
|
1109
|
+
|
1190
1110
|
actions = nil
|
1191
|
-
|
1111
|
+
|
1192
1112
|
if num_actions > 0
|
1193
|
-
action_ptr =
|
1194
|
-
|
1195
|
-
memcpy(action_buf, action_ptr, action_buf.size)
|
1196
|
-
|
1197
|
-
i = 0
|
1113
|
+
action_ptr = fail_struct[:lpsaActions]
|
1114
|
+
|
1198
1115
|
actions = {}
|
1116
|
+
|
1199
1117
|
num_actions.times{ |n|
|
1200
|
-
|
1201
|
-
|
1118
|
+
sc_action = SC_ACTION.new(action_ptr[n])
|
1119
|
+
delay = sc_action[:Delay]
|
1120
|
+
action_type = get_action_type(sc_action[:Type])
|
1202
1121
|
actions[n+1] = {:action_type => action_type, :delay => delay}
|
1203
|
-
i += 8
|
1204
1122
|
}
|
1205
1123
|
end
|
1206
1124
|
else
|
1207
|
-
reset_period
|
1208
|
-
|
1209
|
-
command
|
1210
|
-
actions
|
1125
|
+
reset_period = nil
|
1126
|
+
reboot_msg = nil
|
1127
|
+
command = nil
|
1128
|
+
actions = nil
|
1211
1129
|
end
|
1212
1130
|
ensure
|
1213
1131
|
CloseServiceHandle(handle_scs) if handle_scs > 0
|
1214
1132
|
end
|
1215
|
-
|
1133
|
+
|
1216
1134
|
struct = ServiceStruct.new(
|
1217
1135
|
service_name,
|
1218
1136
|
display_name,
|
@@ -1240,55 +1158,55 @@ module Win32
|
|
1240
1158
|
num_actions,
|
1241
1159
|
actions
|
1242
1160
|
)
|
1243
|
-
|
1161
|
+
|
1244
1162
|
if block_given?
|
1245
1163
|
yield struct
|
1246
1164
|
else
|
1247
1165
|
services_array << struct
|
1248
1166
|
end
|
1249
1167
|
|
1250
|
-
|
1168
|
+
service_buf += ENUM_SERVICE_STATUS_PROCESS.size
|
1251
1169
|
}
|
1252
1170
|
ensure
|
1253
1171
|
CloseServiceHandle(handle_scm)
|
1254
1172
|
end
|
1255
|
-
|
1173
|
+
|
1256
1174
|
block_given? ? nil : services_array
|
1257
1175
|
end
|
1258
|
-
|
1176
|
+
|
1259
1177
|
private
|
1260
|
-
|
1178
|
+
|
1261
1179
|
# Configures failure actions for a given service.
|
1262
1180
|
#
|
1263
1181
|
def self.configure_failure_actions(handle_scs, opts)
|
1264
1182
|
if opts['failure_actions']
|
1265
1183
|
token_handle = 0.chr * 4
|
1266
|
-
|
1184
|
+
|
1267
1185
|
bool = OpenProcessToken(
|
1268
1186
|
GetCurrentProcess(),
|
1269
1187
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
1270
1188
|
token_handle
|
1271
1189
|
)
|
1272
|
-
|
1190
|
+
|
1273
1191
|
unless bool
|
1274
1192
|
error = get_last_error
|
1275
1193
|
CloseServiceHandle(handle_scs)
|
1276
|
-
raise Error, error
|
1194
|
+
raise Error, error
|
1277
1195
|
end
|
1278
|
-
|
1196
|
+
|
1279
1197
|
token_handle = token_handle.unpack('L').first
|
1280
|
-
|
1198
|
+
|
1281
1199
|
# Get the LUID for shutdown privilege.
|
1282
1200
|
luid = 0.chr * 8
|
1283
|
-
|
1201
|
+
|
1284
1202
|
unless LookupPrivilegeValue('', 'SeShutdownPrivilege', luid)
|
1285
1203
|
error = get_last_error
|
1286
1204
|
CloseServiceHandle(handle_scs)
|
1287
|
-
raise Error, error
|
1205
|
+
raise Error, error
|
1288
1206
|
end
|
1289
|
-
|
1207
|
+
|
1290
1208
|
tkp = [1].pack('L') + luid + [SE_PRIVILEGE_ENABLED].pack('L')
|
1291
|
-
|
1209
|
+
|
1292
1210
|
# Enable shutdown privilege in access token of this process
|
1293
1211
|
bool = AdjustTokenPrivileges(
|
1294
1212
|
token_handle,
|
@@ -1298,69 +1216,69 @@ module Win32
|
|
1298
1216
|
nil,
|
1299
1217
|
nil
|
1300
1218
|
)
|
1301
|
-
|
1219
|
+
|
1302
1220
|
unless bool
|
1303
1221
|
error = get_last_error
|
1304
1222
|
CloseServiceHandle(handle_scs)
|
1305
|
-
raise Error, error
|
1306
|
-
end
|
1307
|
-
end
|
1308
|
-
|
1223
|
+
raise Error, error
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
|
1309
1227
|
fail_buf = 0.chr * 20 # sizeof(SERVICE_FAILURE_ACTIONS)
|
1310
1228
|
|
1311
1229
|
if opts['failure_reset_period']
|
1312
1230
|
fail_buf[0,4] = [opts['failure_reset_period']].pack('L')
|
1313
1231
|
end
|
1314
|
-
|
1232
|
+
|
1315
1233
|
if opts['failure_reboot_message']
|
1316
1234
|
fail_buf[4,4] = [opts['failure_reboot_message']].pack('p*')
|
1317
1235
|
end
|
1318
|
-
|
1236
|
+
|
1319
1237
|
if opts['failure_command']
|
1320
1238
|
fail_buf[8,4] = [opts['failure_command']].pack('p*')
|
1321
1239
|
end
|
1322
|
-
|
1240
|
+
|
1323
1241
|
if opts['failure_actions']
|
1324
1242
|
actions = []
|
1325
|
-
|
1243
|
+
|
1326
1244
|
opts['failure_actions'].each{ |action|
|
1327
1245
|
action_buf = 0.chr * 8
|
1328
1246
|
action_buf[0, 4] = [action].pack('L')
|
1329
1247
|
action_buf[4, 4] = [opts['failure_delay']].pack('L')
|
1330
1248
|
actions << action_buf
|
1331
1249
|
}
|
1332
|
-
|
1250
|
+
|
1333
1251
|
actions = actions.join
|
1334
|
-
|
1252
|
+
|
1335
1253
|
fail_buf[12,4] = [opts['failure_actions'].length].pack('L')
|
1336
1254
|
fail_buf[16,4] = [actions].pack('p*')
|
1337
1255
|
end
|
1338
|
-
|
1256
|
+
|
1339
1257
|
bool = ChangeServiceConfig2(
|
1340
1258
|
handle_scs,
|
1341
1259
|
SERVICE_CONFIG_FAILURE_ACTIONS,
|
1342
1260
|
fail_buf
|
1343
1261
|
)
|
1344
|
-
|
1262
|
+
|
1345
1263
|
unless bool
|
1346
1264
|
error = get_last_error
|
1347
1265
|
CloseServiceHandle(handle_scs)
|
1348
1266
|
raise Error, error
|
1349
|
-
end
|
1267
|
+
end
|
1350
1268
|
end
|
1351
1269
|
|
1352
1270
|
# Unravels a pointer to an array of dependencies. Takes the address
|
1353
1271
|
# that points the array as an argument.
|
1354
1272
|
#
|
1355
1273
|
def self.get_dependencies(address)
|
1356
|
-
dep_buf = ""
|
1274
|
+
dep_buf = ""
|
1357
1275
|
|
1358
1276
|
while address != 0
|
1359
1277
|
char_buf = 0.chr
|
1360
1278
|
memcpy(char_buf, address, 1)
|
1361
|
-
address += 1
|
1279
|
+
address += 1
|
1362
1280
|
dep_buf += char_buf
|
1363
|
-
break if dep_buf[-2,2] == "\0\0"
|
1281
|
+
break if dep_buf[-2,2] == "\0\0"
|
1364
1282
|
end
|
1365
1283
|
|
1366
1284
|
dependencies = []
|
@@ -1371,7 +1289,7 @@ module Win32
|
|
1371
1289
|
|
1372
1290
|
dependencies
|
1373
1291
|
end
|
1374
|
-
|
1292
|
+
|
1375
1293
|
# Returns a human readable string indicating the action type.
|
1376
1294
|
#
|
1377
1295
|
def self.get_action_type(action_type)
|
@@ -1386,7 +1304,7 @@ module Win32
|
|
1386
1304
|
'command'
|
1387
1305
|
else
|
1388
1306
|
'unknown'
|
1389
|
-
|
1307
|
+
end
|
1390
1308
|
end
|
1391
1309
|
|
1392
1310
|
# Shortcut for QueryServiceConfig. Returns the buffer. In rare cases
|
@@ -1395,24 +1313,22 @@ module Win32
|
|
1395
1313
|
# instead.
|
1396
1314
|
#
|
1397
1315
|
def self.get_config_info(handle)
|
1398
|
-
bytes_needed =
|
1316
|
+
bytes_needed = FFI::MemoryPointer.new(:ulong)
|
1399
1317
|
|
1400
1318
|
# First attempt at QueryServiceConfig is to get size needed
|
1401
|
-
bool = QueryServiceConfig(handle,
|
1402
|
-
|
1403
|
-
err_num = GetLastError()
|
1319
|
+
bool = QueryServiceConfig(handle, nil, 0, bytes_needed)
|
1404
1320
|
|
1405
|
-
if !bool &&
|
1406
|
-
config_buf =
|
1407
|
-
elsif
|
1408
|
-
return
|
1321
|
+
if !bool && FFI.errno == ERROR_INSUFFICIENT_BUFFER
|
1322
|
+
config_buf = FFI::MemoryPointer.new(:char, bytes_needed.read_ulong)
|
1323
|
+
elsif FFI.errno == ERROR_FILE_NOT_FOUND
|
1324
|
+
return FFI.errno
|
1409
1325
|
else
|
1410
|
-
error =
|
1326
|
+
error = FFI.errno
|
1411
1327
|
CloseServiceHandle(handle)
|
1412
|
-
|
1328
|
+
FFI.raise_windows_error('QueryServiceConfig', error)
|
1413
1329
|
end
|
1414
1330
|
|
1415
|
-
bytes_needed =
|
1331
|
+
bytes_needed = FFI::MemoryPointer.new(:ulong)
|
1416
1332
|
|
1417
1333
|
# Second attempt at QueryServiceConfig gets the actual info
|
1418
1334
|
begin
|
@@ -1423,34 +1339,34 @@ module Win32
|
|
1423
1339
|
bytes_needed
|
1424
1340
|
)
|
1425
1341
|
|
1426
|
-
|
1342
|
+
FFI.raise_windows_error('QueryServiceConfig') unless bool
|
1427
1343
|
ensure
|
1428
1344
|
CloseServiceHandle(handle) unless bool
|
1429
1345
|
end
|
1430
1346
|
|
1431
|
-
config_buf
|
1347
|
+
QUERY_SERVICE_CONFIG.new(config_buf) # cast the buffer
|
1432
1348
|
end
|
1433
|
-
|
1349
|
+
|
1434
1350
|
# Shortcut for QueryServiceConfig2. Returns the buffer.
|
1435
|
-
#
|
1436
|
-
def self.get_config2_info(handle, info_level)
|
1437
|
-
bytes_needed =
|
1438
|
-
|
1351
|
+
#
|
1352
|
+
def self.get_config2_info(handle, info_level)
|
1353
|
+
bytes_needed = FFI::MemoryPointer.new(:ulong)
|
1354
|
+
|
1439
1355
|
# First attempt at QueryServiceConfig2 is to get size needed
|
1440
|
-
bool = QueryServiceConfig2(handle, info_level,
|
1356
|
+
bool = QueryServiceConfig2(handle, info_level, nil, 0, bytes_needed)
|
1441
1357
|
|
1442
|
-
err_num =
|
1358
|
+
err_num = FFI.errno
|
1443
1359
|
|
1444
1360
|
if !bool && err_num == ERROR_INSUFFICIENT_BUFFER
|
1445
|
-
config2_buf =
|
1361
|
+
config2_buf = FFI::MemoryPointer.new(:char, bytes_needed.read_ulong)
|
1446
1362
|
elsif err_num == ERROR_FILE_NOT_FOUND
|
1447
1363
|
return err_num
|
1448
1364
|
else
|
1449
1365
|
CloseServiceHandle(handle)
|
1450
|
-
|
1366
|
+
FFI.raise_windows_error('QueryServiceConfig2', err_num)
|
1451
1367
|
end
|
1452
|
-
|
1453
|
-
bytes_needed =
|
1368
|
+
|
1369
|
+
bytes_needed = FFI::MemoryPointer.new(:ulong)
|
1454
1370
|
|
1455
1371
|
# Second attempt at QueryServiceConfig2 gets the actual info
|
1456
1372
|
begin
|
@@ -1462,14 +1378,14 @@ module Win32
|
|
1462
1378
|
bytes_needed
|
1463
1379
|
)
|
1464
1380
|
|
1465
|
-
|
1381
|
+
FFI.raise_windows_error('QueryServiceConfig2') unless bool
|
1466
1382
|
ensure
|
1467
1383
|
CloseServiceHandle(handle) unless bool
|
1468
1384
|
end
|
1469
|
-
|
1385
|
+
|
1470
1386
|
config2_buf
|
1471
1387
|
end
|
1472
|
-
|
1388
|
+
|
1473
1389
|
# Returns a human readable string indicating the error control
|
1474
1390
|
#
|
1475
1391
|
def self.get_error_control(error_control)
|
@@ -1486,7 +1402,7 @@ module Win32
|
|
1486
1402
|
nil
|
1487
1403
|
end
|
1488
1404
|
end
|
1489
|
-
|
1405
|
+
|
1490
1406
|
# Returns a human readable string indicating the start type.
|
1491
1407
|
#
|
1492
1408
|
def self.get_start_type(start_type)
|
@@ -1505,7 +1421,7 @@ module Win32
|
|
1505
1421
|
nil
|
1506
1422
|
end
|
1507
1423
|
end
|
1508
|
-
|
1424
|
+
|
1509
1425
|
# Returns an array of human readable strings indicating the controls
|
1510
1426
|
# that the service accepts.
|
1511
1427
|
#
|
@@ -1550,9 +1466,9 @@ module Win32
|
|
1550
1466
|
|
1551
1467
|
array
|
1552
1468
|
end
|
1553
|
-
|
1469
|
+
|
1554
1470
|
# Converts a service state numeric constant into a readable string.
|
1555
|
-
#
|
1471
|
+
#
|
1556
1472
|
def self.get_current_state(state)
|
1557
1473
|
case state
|
1558
1474
|
when SERVICE_CONTINUE_PENDING
|
@@ -1573,9 +1489,9 @@ module Win32
|
|
1573
1489
|
nil
|
1574
1490
|
end
|
1575
1491
|
end
|
1576
|
-
|
1492
|
+
|
1577
1493
|
# Converts a service type numeric constant into a human readable string.
|
1578
|
-
#
|
1494
|
+
#
|
1579
1495
|
def self.get_service_type(service_type)
|
1580
1496
|
case service_type
|
1581
1497
|
when SERVICE_FILE_SYSTEM_DRIVER
|
@@ -1602,33 +1518,29 @@ module Win32
|
|
1602
1518
|
nil
|
1603
1519
|
end
|
1604
1520
|
end
|
1605
|
-
|
1521
|
+
|
1606
1522
|
# A shortcut method that simplifies the various service control methods.
|
1607
|
-
#
|
1523
|
+
#
|
1608
1524
|
def self.send_signal(service, host, service_signal, control_signal)
|
1609
|
-
handle_scm = OpenSCManager(host,
|
1610
|
-
|
1611
|
-
if handle_scm == 0
|
1612
|
-
|
1613
|
-
end
|
1614
|
-
|
1525
|
+
handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
|
1526
|
+
|
1527
|
+
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
|
1528
|
+
|
1615
1529
|
begin
|
1616
1530
|
handle_scs = OpenService(handle_scm, service, service_signal)
|
1617
|
-
|
1618
|
-
if handle_scs == 0
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
status = [0,0,0,0,0,0,0].pack('LLLLLLL')
|
1623
|
-
|
1531
|
+
|
1532
|
+
FFI.raise_windows_error('OpenService') if handle_scs == 0
|
1533
|
+
|
1534
|
+
status = SERVICE_STATUS.new
|
1535
|
+
|
1624
1536
|
unless ControlService(handle_scs, control_signal, status)
|
1625
|
-
|
1626
|
-
end
|
1537
|
+
FFI.raise_windows_error('ControlService')
|
1538
|
+
end
|
1627
1539
|
ensure
|
1628
1540
|
CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
|
1629
1541
|
CloseServiceHandle(handle_scm) if handle_scm && handle_scm > 0
|
1630
1542
|
end
|
1631
|
-
|
1543
|
+
|
1632
1544
|
status
|
1633
1545
|
end
|
1634
1546
|
|