win32-service 0.8.10 → 2.1.6

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