win32-service 0.8.10 → 1.0.1

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.
@@ -1 +1 @@
1
- require_relative 'win32/daemon'
1
+ require_relative 'win32/daemon'
@@ -1 +1 @@
1
- require_relative 'win32/service'
1
+ require_relative 'win32/service'
@@ -1,361 +1,361 @@
1
- require_relative 'windows/constants'
2
- require_relative 'windows/structs'
3
- require_relative 'windows/functions'
4
-
5
- # The Win32 module serves as a namespace only.
6
- module Win32
7
-
8
- # The Daemon class
9
- class Daemon
10
- include Windows::ServiceConstants
11
- include Windows::ServiceStructs
12
- include Windows::ServiceFunctions
13
-
14
- extend Windows::ServiceStructs
15
- extend Windows::ServiceFunctions
16
-
17
- # The version of this library
18
- VERSION = '0.8.10'.freeze
19
-
20
- private
21
-
22
- # Service is not running
23
- STOPPED = SERVICE_STOPPED
24
-
25
- # Service has received a start signal but is not yet running
26
- START_PENDING = SERVICE_START_PENDING
27
-
28
- # Service has received a stop signal but is not yet stopped
29
- STOP_PENDING = SERVICE_STOP_PENDING
30
-
31
- # Service is running
32
- RUNNING = SERVICE_RUNNING
33
-
34
- # Service has received a signal to resume but is not yet running
35
- CONTINUE_PENDING = SERVICE_CONTINUE_PENDING
36
-
37
- # Service has received a signal to pause but is not yet paused
38
- PAUSE_PENDING = SERVICE_PAUSE_PENDING
39
-
40
- # Service is paused
41
- PAUSED = SERVICE_PAUSED
42
-
43
- # Service controls
44
-
45
- # Notifies service that it should stop
46
- CONTROL_STOP = SERVICE_CONTROL_STOP
47
-
48
- # Notifies service that it should pause
49
- CONTROL_PAUSE = SERVICE_CONTROL_PAUSE
50
-
51
- # Notifies service that it should resume
52
- CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE
53
-
54
- # Notifies service that it should return its current status information
55
- CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE
56
-
57
- # Notifies a service that its parameters have changed
58
- CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE
59
-
60
- # Notifies a service that there is a new component for binding
61
- CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD
62
-
63
- # Notifies a service that a component for binding has been removed
64
- CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE
65
-
66
- # Notifies a service that a component for binding has been enabled
67
- CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE
68
-
69
- # Notifies a service that a component for binding has been disabled
70
- CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE
71
-
72
- IDLE = 0
73
-
74
- # Wraps SetServiceStatus.
75
- SetTheServiceStatus = Proc.new do |dwCurrentState, dwWin32ExitCode,dwCheckPoint, dwWaitHint|
76
- ss = SERVICE_STATUS.new # Current status of the service.
77
-
78
- # Disable control requests until the service is started.
79
- if dwCurrentState == SERVICE_START_PENDING
80
- ss[:dwControlsAccepted] = 0
81
- else
82
- ss[:dwControlsAccepted] =
83
- SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
84
- SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN
85
- end
86
-
87
- # Initialize ss structure.
88
- ss[:dwServiceType] = SERVICE_WIN32_OWN_PROCESS
89
- ss[:dwServiceSpecificExitCode] = 0
90
- ss[:dwCurrentState] = dwCurrentState
91
- ss[:dwWin32ExitCode] = dwWin32ExitCode
92
- ss[:dwCheckPoint] = dwCheckPoint
93
- ss[:dwWaitHint] = dwWaitHint
94
-
95
- @@dwServiceState = dwCurrentState
96
-
97
- # Send status of the service to the Service Controller.
98
- if !SetServiceStatus(@@ssh, ss)
99
- SetEvent(@@hStopEvent)
100
- end
101
- end
102
-
103
- ERROR_CALL_NOT_IMPLEMENTED = 0x78
104
-
105
- # Handles control signals from the service control manager.
106
- Service_Ctrl_ex = Proc.new do |dwCtrlCode,dwEventType,lpEventData,lpContext|
107
- @@waiting_control_code = dwCtrlCode;
108
- return_value = NO_ERROR
109
-
110
- begin
111
- dwState = SERVICE_RUNNING
112
-
113
- case dwCtrlCode
114
- when SERVICE_CONTROL_STOP
115
- dwState = SERVICE_STOP_PENDING
116
- when SERVICE_CONTROL_SHUTDOWN
117
- dwState = SERVICE_STOP_PENDING
118
- when SERVICE_CONTROL_PAUSE
119
- dwState = SERVICE_PAUSED
120
- when SERVICE_CONTROL_CONTINUE
121
- dwState = SERVICE_RUNNING
122
- #else
123
- # TODO: Handle other control codes? Retain the current state?
124
- end
125
-
126
- # Set the status of the service except on interrogation.
127
- unless dwCtrlCode == SERVICE_CONTROL_INTERROGATE
128
- SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)
129
- end
130
-
131
- # Tell service_main thread to stop.
132
- if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN
133
- if SetEvent(@@hStopEvent) == 0
134
- SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)
135
- end
136
- end
137
- rescue
138
- return_value = ERROR_CALL_NOT_IMPLEMENTED
139
- end
140
-
141
- return_value
142
- end
143
-
144
- # Called by the service control manager after the call to StartServiceCtrlDispatcher.
145
- Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc,lpszArgv|
146
- begin
147
- # Obtain the name of the service.
148
- if lpszArgv.address!=0
149
- argv = lpszArgv.get_array_of_string(0,dwArgc)
150
- lpszServiceName = argv[0]
151
- else
152
- lpszServiceName = ''
153
- end
154
-
155
- # Args passed to Service.start
156
- if(dwArgc > 1)
157
- @@Argv = argv[1..-1]
158
- else
159
- @@Argv = nil
160
- end
161
-
162
- # Register the service ctrl handler.
163
- @@ssh = RegisterServiceCtrlHandlerEx(
164
- lpszServiceName,
165
- Service_Ctrl_ex,
166
- nil
167
- )
168
-
169
- # No service to stop, no service handle to notify, nothing to do but exit.
170
- return if @@ssh == 0
171
-
172
- # The service has started.
173
- SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)
174
-
175
- SetEvent(@@hStartEvent)
176
-
177
- # Main loop for the service.
178
- while(WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0) do
179
- end
180
-
181
- # Main loop for the service.
182
- while(WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0) do
183
- end
184
- ensure
185
- # Stop the service.
186
- SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)
187
- end
188
- end
189
-
190
- ThreadProc = FFI::Function.new(:ulong,[:pointer]) do |lpParameter|
191
- ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
192
-
193
- s = SERVICE_TABLE_ENTRY.new(ste[0])
194
- s[:lpServiceName] = FFI::MemoryPointer.from_string('')
195
- s[:lpServiceProc] = lpParameter
196
-
197
- s = SERVICE_TABLE_ENTRY.new(ste[1])
198
- s[:lpServiceName] = nil
199
- s[:lpServiceProc] = nil
200
-
201
- # No service to step, no service handle, no ruby exceptions, just terminate the thread..
202
- if !StartServiceCtrlDispatcher(ste)
203
- return 1
204
- end
205
-
206
- return 0
207
- end
208
-
209
- public
210
-
211
- # This is a shortcut for Daemon.new + Daemon#mainloop.
212
- #
213
- def self.mainloop
214
- self.new.mainloop
215
- end
216
-
217
- # This is the method that actually puts your code into a loop and allows it
218
- # to run as a service. The code that is actually run while in the mainloop
219
- # is what you defined in your own Daemon#service_main method.
220
- #
221
- def mainloop
222
- @@waiting_control_code = IDLE_CONTROL_CODE
223
- @@dwServiceState = 0
224
-
225
- # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
226
- # associated with a tty. This helps newbs avoid Errno::EBADF errors.
227
- STDIN.reopen('NUL') if STDIN.isatty
228
- STDOUT.reopen('NUL') if STDOUT.isatty
229
- STDERR.reopen('NUL') if STDERR.isatty
230
-
231
- # Calling init here so that init failures never even tries to start the
232
- # service. Of course that means that init methods must be very quick
233
- # because the SCM will be receiving no START_PENDING messages while
234
- # init's running.
235
- #
236
- # TODO: Fix?
237
- service_init() if respond_to?('service_init')
238
-
239
- # Create the event to signal the service to start.
240
- @@hStartEvent = CreateEvent(nil, 1, 0, nil)
241
-
242
- if @@hStartEvent == 0
243
- raise SystemCallError.new('CreateEvent', FFI.errno)
244
- end
245
-
246
- # Create the event to signal the service to stop.
247
- @@hStopEvent = CreateEvent(nil, 1, 0, nil)
248
-
249
- if @@hStopEvent == 0
250
- raise SystemCallError.new('CreateEvent', FFI.errno)
251
- end
252
-
253
- # Create the event to signal the service that stop has completed
254
- @@hStopCompletedEvent = CreateEvent(nil, 1, 0, nil)
255
-
256
- if @@hStopCompletedEvent == 0
257
- raise SystemCallError.new('CreateEvent', FFI.errno)
258
- end
259
-
260
- hThread = CreateThread(nil, 0, ThreadProc, Service_Main, 0, nil)
261
-
262
- if hThread == 0
263
- raise SystemCallError.new('CreateThread', FFI.errno)
264
- end
265
-
266
- events = FFI::MemoryPointer.new(:pointer, 2)
267
- events.put_pointer(0, FFI::Pointer.new(hThread))
268
- events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
269
-
270
- while ((index = WaitForMultipleObjects(2, events, 0, 1000)) == WAIT_TIMEOUT) do
271
- end
272
-
273
- if index == WAIT_FAILED
274
- raise SystemCallError.new('WaitForMultipleObjects', FFI.errno)
275
- end
276
-
277
- # The thread exited, so the show is off.
278
- if index == WAIT_OBJECT_0
279
- raise "Service_Main thread exited abnormally"
280
- end
281
-
282
- thr = Thread.new do
283
- begin
284
- while(WaitForSingleObject(@@hStopEvent, 1000) == WAIT_TIMEOUT)
285
- # Check to see if anything interesting has been signaled
286
- case @@waiting_control_code
287
- when SERVICE_CONTROL_PAUSE
288
- service_pause() if respond_to?('service_pause')
289
- when SERVICE_CONTROL_CONTINUE
290
- service_resume() if respond_to?('service_resume')
291
- when SERVICE_CONTROL_INTERROGATE
292
- service_interrogate() if respond_to?('service_interrogate')
293
- when SERVICE_CONTROL_SHUTDOWN
294
- service_shutdown() if respond_to?('service_shutdown')
295
- when SERVICE_CONTROL_PARAMCHANGE
296
- service_paramchange() if respond_to?('service_paramchange')
297
- when SERVICE_CONTROL_NETBINDADD
298
- service_netbindadd() if respond_to?('service_netbindadd')
299
- when SERVICE_CONTROL_NETBINDREMOVE
300
- service_netbindremove() if respond_to?('service_netbindremove')
301
- when SERVICE_CONTROL_NETBINDENABLE
302
- service_netbindenable() if respond_to?('service_netbindenable')
303
- when SERVICE_CONTROL_NETBINDDISABLE
304
- service_netbinddisable() if respond_to?('service_netbinddisable')
305
- end
306
- @@waiting_control_code = IDLE_CONTROL_CODE
307
- end
308
-
309
- service_stop() if respond_to?('service_stop')
310
- ensure
311
- SetEvent(@@hStopCompletedEvent)
312
- end
313
- end
314
-
315
- if respond_to?('service_main')
316
- service_main(*@@Argv)
317
- end
318
-
319
- thr.join
320
- end
321
-
322
- # Returns the state of the service (as an constant integer) which can be any
323
- # of the service status constants, e.g. RUNNING, PAUSED, etc.
324
- #
325
- # This method is typically used within your service_main method to setup the
326
- # loop. For example:
327
- #
328
- # class MyDaemon < Daemon
329
- # def service_main
330
- # while state == RUNNING || state == PAUSED || state == IDLE
331
- # # Your main loop here
332
- # end
333
- # end
334
- # end
335
- #
336
- # See the Daemon#running? method for an abstraction of the above code.
337
- #
338
- def state
339
- @@dwServiceState
340
- end
341
-
342
- #
343
- # Returns whether or not the service is in a running state, i.e. the service
344
- # status is either RUNNING, PAUSED or IDLE.
345
- #
346
- # This is typically used within your service_main method to setup the main
347
- # loop. For example:
348
- #
349
- # class MyDaemon < Daemon
350
- # def service_main
351
- # while running?
352
- # # Your main loop here
353
- # end
354
- # end
355
- # end
356
- #
357
- def running?
358
- [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)
359
- end
360
- end # Daemon
361
- end # Win32
1
+ require_relative 'windows/constants'
2
+ require_relative 'windows/structs'
3
+ require_relative 'windows/functions'
4
+
5
+ # The Win32 module serves as a namespace only.
6
+ module Win32
7
+
8
+ # The Daemon class
9
+ class Daemon
10
+ include Windows::ServiceConstants
11
+ include Windows::ServiceStructs
12
+ include Windows::ServiceFunctions
13
+
14
+ extend Windows::ServiceStructs
15
+ extend Windows::ServiceFunctions
16
+
17
+ # The version of this library
18
+ VERSION = '0.8.10'.freeze
19
+
20
+ private
21
+
22
+ # Service is not running
23
+ STOPPED = SERVICE_STOPPED
24
+
25
+ # Service has received a start signal but is not yet running
26
+ START_PENDING = SERVICE_START_PENDING
27
+
28
+ # Service has received a stop signal but is not yet stopped
29
+ STOP_PENDING = SERVICE_STOP_PENDING
30
+
31
+ # Service is running
32
+ RUNNING = SERVICE_RUNNING
33
+
34
+ # Service has received a signal to resume but is not yet running
35
+ CONTINUE_PENDING = SERVICE_CONTINUE_PENDING
36
+
37
+ # Service has received a signal to pause but is not yet paused
38
+ PAUSE_PENDING = SERVICE_PAUSE_PENDING
39
+
40
+ # Service is paused
41
+ PAUSED = SERVICE_PAUSED
42
+
43
+ # Service controls
44
+
45
+ # Notifies service that it should stop
46
+ CONTROL_STOP = SERVICE_CONTROL_STOP
47
+
48
+ # Notifies service that it should pause
49
+ CONTROL_PAUSE = SERVICE_CONTROL_PAUSE
50
+
51
+ # Notifies service that it should resume
52
+ CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE
53
+
54
+ # Notifies service that it should return its current status information
55
+ CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE
56
+
57
+ # Notifies a service that its parameters have changed
58
+ CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE
59
+
60
+ # Notifies a service that there is a new component for binding
61
+ CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD
62
+
63
+ # Notifies a service that a component for binding has been removed
64
+ CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE
65
+
66
+ # Notifies a service that a component for binding has been enabled
67
+ CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE
68
+
69
+ # Notifies a service that a component for binding has been disabled
70
+ CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE
71
+
72
+ IDLE = 0
73
+
74
+ # Wraps SetServiceStatus.
75
+ SetTheServiceStatus = Proc.new do |dwCurrentState, dwWin32ExitCode,dwCheckPoint, dwWaitHint|
76
+ ss = SERVICE_STATUS.new # Current status of the service.
77
+
78
+ # Disable control requests until the service is started.
79
+ if dwCurrentState == SERVICE_START_PENDING
80
+ ss[:dwControlsAccepted] = 0
81
+ else
82
+ ss[:dwControlsAccepted] =
83
+ SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
84
+ SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE
85
+ end
86
+
87
+ # Initialize ss structure.
88
+ ss[:dwServiceType] = SERVICE_WIN32_OWN_PROCESS
89
+ ss[:dwServiceSpecificExitCode] = 0
90
+ ss[:dwCurrentState] = dwCurrentState
91
+ ss[:dwWin32ExitCode] = dwWin32ExitCode
92
+ ss[:dwCheckPoint] = dwCheckPoint
93
+ ss[:dwWaitHint] = dwWaitHint
94
+
95
+ @@dwServiceState = dwCurrentState
96
+
97
+ # Send status of the service to the Service Controller.
98
+ if !SetServiceStatus(@@ssh, ss)
99
+ SetEvent(@@hStopEvent)
100
+ end
101
+ end
102
+
103
+ ERROR_CALL_NOT_IMPLEMENTED = 0x78
104
+
105
+ # Handles control signals from the service control manager.
106
+ Service_Ctrl_ex = Proc.new do |dwCtrlCode,dwEventType,lpEventData,lpContext|
107
+ @@waiting_control_code = dwCtrlCode;
108
+ return_value = NO_ERROR
109
+
110
+ begin
111
+ dwState = SERVICE_RUNNING
112
+
113
+ case dwCtrlCode
114
+ when SERVICE_CONTROL_STOP
115
+ dwState = SERVICE_STOP_PENDING
116
+ when SERVICE_CONTROL_SHUTDOWN
117
+ dwState = SERVICE_STOP_PENDING
118
+ when SERVICE_CONTROL_PAUSE
119
+ dwState = SERVICE_PAUSED
120
+ when SERVICE_CONTROL_CONTINUE
121
+ dwState = SERVICE_RUNNING
122
+ #else
123
+ # TODO: Handle other control codes? Retain the current state?
124
+ end
125
+
126
+ # Set the status of the service except on interrogation.
127
+ unless dwCtrlCode == SERVICE_CONTROL_INTERROGATE
128
+ SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)
129
+ end
130
+
131
+ # Tell service_main thread to stop.
132
+ if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN
133
+ if SetEvent(@@hStopEvent) == 0
134
+ SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)
135
+ end
136
+ end
137
+ rescue
138
+ return_value = ERROR_CALL_NOT_IMPLEMENTED
139
+ end
140
+
141
+ return_value
142
+ end
143
+
144
+ # Called by the service control manager after the call to StartServiceCtrlDispatcher.
145
+ Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc,lpszArgv|
146
+ begin
147
+ # Obtain the name of the service.
148
+ if lpszArgv.address!=0
149
+ argv = lpszArgv.get_array_of_string(0,dwArgc)
150
+ lpszServiceName = argv[0]
151
+ else
152
+ lpszServiceName = ''
153
+ end
154
+
155
+ # Args passed to Service.start
156
+ if(dwArgc > 1)
157
+ @@Argv = argv[1..-1]
158
+ else
159
+ @@Argv = nil
160
+ end
161
+
162
+ # Register the service ctrl handler.
163
+ @@ssh = RegisterServiceCtrlHandlerEx(
164
+ lpszServiceName,
165
+ Service_Ctrl_ex,
166
+ nil
167
+ )
168
+
169
+ # No service to stop, no service handle to notify, nothing to do but exit.
170
+ return if @@ssh == 0
171
+
172
+ # The service has started.
173
+ SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)
174
+
175
+ SetEvent(@@hStartEvent)
176
+
177
+ # Main loop for the service.
178
+ while(WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0) do
179
+ end
180
+
181
+ # Main loop for the service.
182
+ while(WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0) do
183
+ end
184
+ ensure
185
+ # Stop the service.
186
+ SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)
187
+ end
188
+ end
189
+
190
+ ThreadProc = FFI::Function.new(:ulong,[:pointer]) do |lpParameter|
191
+ ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
192
+
193
+ s = SERVICE_TABLE_ENTRY.new(ste[0])
194
+ s[:lpServiceName] = FFI::MemoryPointer.from_string('')
195
+ s[:lpServiceProc] = lpParameter
196
+
197
+ s = SERVICE_TABLE_ENTRY.new(ste[1])
198
+ s[:lpServiceName] = nil
199
+ s[:lpServiceProc] = nil
200
+
201
+ # No service to step, no service handle, no ruby exceptions, just terminate the thread..
202
+ if !StartServiceCtrlDispatcher(ste)
203
+ return 1
204
+ end
205
+
206
+ return 0
207
+ end
208
+
209
+ public
210
+
211
+ # This is a shortcut for Daemon.new + Daemon#mainloop.
212
+ #
213
+ def self.mainloop
214
+ self.new.mainloop
215
+ end
216
+
217
+ # This is the method that actually puts your code into a loop and allows it
218
+ # to run as a service. The code that is actually run while in the mainloop
219
+ # is what you defined in your own Daemon#service_main method.
220
+ #
221
+ def mainloop
222
+ @@waiting_control_code = IDLE_CONTROL_CODE
223
+ @@dwServiceState = 0
224
+
225
+ # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
226
+ # associated with a tty. This helps newbs avoid Errno::EBADF errors.
227
+ STDIN.reopen('NUL') if STDIN.isatty
228
+ STDOUT.reopen('NUL') if STDOUT.isatty
229
+ STDERR.reopen('NUL') if STDERR.isatty
230
+
231
+ # Calling init here so that init failures never even tries to start the
232
+ # service. Of course that means that init methods must be very quick
233
+ # because the SCM will be receiving no START_PENDING messages while
234
+ # init's running.
235
+ #
236
+ # TODO: Fix?
237
+ service_init() if respond_to?('service_init')
238
+
239
+ # Create the event to signal the service to start.
240
+ @@hStartEvent = CreateEvent(nil, 1, 0, nil)
241
+
242
+ if @@hStartEvent == 0
243
+ raise SystemCallError.new('CreateEvent', FFI.errno)
244
+ end
245
+
246
+ # Create the event to signal the service to stop.
247
+ @@hStopEvent = CreateEvent(nil, 1, 0, nil)
248
+
249
+ if @@hStopEvent == 0
250
+ raise SystemCallError.new('CreateEvent', FFI.errno)
251
+ end
252
+
253
+ # Create the event to signal the service that stop has completed
254
+ @@hStopCompletedEvent = CreateEvent(nil, 1, 0, nil)
255
+
256
+ if @@hStopCompletedEvent == 0
257
+ raise SystemCallError.new('CreateEvent', FFI.errno)
258
+ end
259
+
260
+ hThread = CreateThread(nil, 0, ThreadProc, Service_Main, 0, nil)
261
+
262
+ if hThread == 0
263
+ raise SystemCallError.new('CreateThread', FFI.errno)
264
+ end
265
+
266
+ events = FFI::MemoryPointer.new(:pointer, 2)
267
+ events.put_pointer(0, FFI::Pointer.new(hThread))
268
+ events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
269
+
270
+ while ((index = WaitForMultipleObjects(2, events, 0, 1000)) == WAIT_TIMEOUT) do
271
+ end
272
+
273
+ if index == WAIT_FAILED
274
+ raise SystemCallError.new('WaitForMultipleObjects', FFI.errno)
275
+ end
276
+
277
+ # The thread exited, so the show is off.
278
+ if index == WAIT_OBJECT_0
279
+ raise "Service_Main thread exited abnormally"
280
+ end
281
+
282
+ thr = Thread.new do
283
+ begin
284
+ while(WaitForSingleObject(@@hStopEvent, 1000) == WAIT_TIMEOUT)
285
+ # Check to see if anything interesting has been signaled
286
+ case @@waiting_control_code
287
+ when SERVICE_CONTROL_PAUSE
288
+ service_pause() if respond_to?('service_pause')
289
+ when SERVICE_CONTROL_CONTINUE
290
+ service_resume() if respond_to?('service_resume')
291
+ when SERVICE_CONTROL_INTERROGATE
292
+ service_interrogate() if respond_to?('service_interrogate')
293
+ when SERVICE_CONTROL_SHUTDOWN
294
+ service_shutdown() if respond_to?('service_shutdown')
295
+ when SERVICE_CONTROL_PARAMCHANGE
296
+ service_paramchange() if respond_to?('service_paramchange')
297
+ when SERVICE_CONTROL_NETBINDADD
298
+ service_netbindadd() if respond_to?('service_netbindadd')
299
+ when SERVICE_CONTROL_NETBINDREMOVE
300
+ service_netbindremove() if respond_to?('service_netbindremove')
301
+ when SERVICE_CONTROL_NETBINDENABLE
302
+ service_netbindenable() if respond_to?('service_netbindenable')
303
+ when SERVICE_CONTROL_NETBINDDISABLE
304
+ service_netbinddisable() if respond_to?('service_netbinddisable')
305
+ end
306
+ @@waiting_control_code = IDLE_CONTROL_CODE
307
+ end
308
+
309
+ service_stop() if respond_to?('service_stop')
310
+ ensure
311
+ SetEvent(@@hStopCompletedEvent)
312
+ end
313
+ end
314
+
315
+ if respond_to?('service_main')
316
+ service_main(*@@Argv)
317
+ end
318
+
319
+ thr.join
320
+ end
321
+
322
+ # Returns the state of the service (as an constant integer) which can be any
323
+ # of the service status constants, e.g. RUNNING, PAUSED, etc.
324
+ #
325
+ # This method is typically used within your service_main method to setup the
326
+ # loop. For example:
327
+ #
328
+ # class MyDaemon < Daemon
329
+ # def service_main
330
+ # while state == RUNNING || state == PAUSED || state == IDLE
331
+ # # Your main loop here
332
+ # end
333
+ # end
334
+ # end
335
+ #
336
+ # See the Daemon#running? method for an abstraction of the above code.
337
+ #
338
+ def state
339
+ @@dwServiceState
340
+ end
341
+
342
+ #
343
+ # Returns whether or not the service is in a running state, i.e. the service
344
+ # status is either RUNNING, PAUSED or IDLE.
345
+ #
346
+ # This is typically used within your service_main method to setup the main
347
+ # loop. For example:
348
+ #
349
+ # class MyDaemon < Daemon
350
+ # def service_main
351
+ # while running?
352
+ # # Your main loop here
353
+ # end
354
+ # end
355
+ # end
356
+ #
357
+ def running?
358
+ [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)
359
+ end
360
+ end # Daemon
361
+ end # Win32