win32-service 0.6.1-x86-mswin32-60 → 0.7.0-x86-mswin32-60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
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
+ require 'win32/service'
8
+ include Win32
9
+
10
+ puts "VERSION: " + Service::VERSION
11
+
12
+ p Service.exists?("ClipSrv")
13
+ p Service.exists?("foo")
14
+
15
+ status = Service.status("ClipSrv")
16
+ p status
17
+
18
+ info = Service.config_info("ClipSrv")
19
+ p info
20
+
21
+ Service.services{ |struct|
22
+ p struct
23
+ }
@@ -0,0 +1,596 @@
1
+ #include "ruby.h"
2
+ #include <windows.h>
3
+ #include <string.h>
4
+ #include <stdlib.h>
5
+ #include <malloc.h>
6
+ #include <tchar.h>
7
+
8
+ #define WIN32_SERVICE_VERSION "0.7.0"
9
+
10
+ static VALUE cDaemonError;
11
+
12
+ static HANDLE hThread;
13
+ static HANDLE hStartEvent;
14
+ static HANDLE hStopEvent;
15
+ static HANDLE hStopCompletedEvent;
16
+ static SERVICE_STATUS_HANDLE ssh;
17
+ static DWORD dwServiceState;
18
+ static TCHAR error[1024];
19
+ static int Argc;
20
+ static VALUE* Argv;
21
+
22
+ CRITICAL_SECTION csControlCode;
23
+ // I happen to know from looking in the header file
24
+ // that 0 is not a valid service control code
25
+ // so we will use it, the value does not matter
26
+ // as long as it will never show up in ServiceCtrl
27
+ // - Patrick Hurley
28
+ #define IDLE_CONTROL_CODE 0
29
+ static int waiting_control_code = IDLE_CONTROL_CODE;
30
+
31
+ static VALUE service_close(VALUE);
32
+ void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);
33
+ void WINAPI Service_Ctrl(DWORD dwCtrlCode);
34
+ void SetTheServiceStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,
35
+ DWORD dwCheckPoint, DWORD dwWaitHint);
36
+
37
+ // Return an error code as a string
38
+ LPTSTR ErrorDescription(DWORD p_dwError)
39
+ {
40
+ HLOCAL hLocal = NULL;
41
+ static TCHAR ErrStr[1024];
42
+ int len;
43
+
44
+ if (!(len=FormatMessage(
45
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
46
+ FORMAT_MESSAGE_FROM_SYSTEM |
47
+ FORMAT_MESSAGE_IGNORE_INSERTS,
48
+ NULL,
49
+ p_dwError,
50
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
51
+ (LPTSTR)&hLocal,
52
+ 0,
53
+ NULL)))
54
+ {
55
+ rb_raise(rb_eStandardError, "unable to format error message");
56
+ }
57
+ memset(ErrStr, 0, sizeof(ErrStr));
58
+ strncpy(ErrStr, (LPTSTR)hLocal, len-2); // remove \r\n
59
+ LocalFree(hLocal);
60
+ return ErrStr;
61
+ }
62
+
63
+ // Called by the service control manager after the call to
64
+ // StartServiceCtrlDispatcher.
65
+ void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
66
+ {
67
+ // Obtain the name of the service.
68
+ LPTSTR lpszServiceName = lpszArgv[0];
69
+
70
+ // Args passed to Service.start
71
+ if(dwArgc > 1){
72
+ unsigned int i;
73
+ Argc = dwArgc - 1;
74
+ Argv = malloc(sizeof(VALUE)*Argc);
75
+
76
+ for(i=1; i < dwArgc; i++)
77
+ Argv[i-1] = rb_str_new2(lpszArgv[i]);
78
+ }
79
+
80
+ // Register the service ctrl handler.
81
+ ssh = RegisterServiceCtrlHandler(lpszServiceName,
82
+ (LPHANDLER_FUNCTION)Service_Ctrl);
83
+
84
+ // no service to stop, no service handle to notify, nothing to do but exit
85
+ if(ssh == (SERVICE_STATUS_HANDLE)0)
86
+ return;
87
+
88
+ // The service has started.
89
+ SetTheServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0);
90
+
91
+ SetEvent(hStartEvent);
92
+
93
+ // Main loop for the service.
94
+ while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
95
+ {
96
+ }
97
+
98
+ // Main loop for the service.
99
+ while(WaitForSingleObject(hStopCompletedEvent, 1000) != WAIT_OBJECT_0)
100
+ {
101
+ }
102
+
103
+ // Stop the service.
104
+ SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
105
+ }
106
+
107
+ VALUE Service_Event_Dispatch(VALUE val)
108
+ {
109
+ VALUE func,self;
110
+ VALUE result = Qnil;
111
+
112
+ if(val!=Qnil) {
113
+ self = RARRAY(val)->ptr[0];
114
+ func = NUM2INT(RARRAY(val)->ptr[1]);
115
+
116
+ result = rb_funcall(self,func,0);
117
+ }
118
+
119
+ return result;
120
+ }
121
+
122
+ VALUE Ruby_Service_Ctrl(VALUE self)
123
+ {
124
+ while (WaitForSingleObject(hStopEvent,0) == WAIT_TIMEOUT)
125
+ {
126
+ __try
127
+ {
128
+ EnterCriticalSection(&csControlCode);
129
+
130
+ // Check to see if anything interesting has been signaled
131
+ if (waiting_control_code != IDLE_CONTROL_CODE)
132
+ {
133
+ if (waiting_control_code != SERVICE_CONTROL_STOP) {
134
+ // if there is a code, create a ruby thread to deal with it
135
+ // this might be over engineering the solution, but I don't
136
+ // want to block Service_Ctrl longer than necessary and the
137
+ // critical section will block it.
138
+ VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
139
+
140
+ if(EventHookHash != Qnil){
141
+ VALUE val = rb_hash_aref(EventHookHash, INT2NUM(waiting_control_code));
142
+
143
+ if(val != Qnil)
144
+ rb_thread_create(Service_Event_Dispatch, (void*) val);
145
+ }
146
+ }
147
+ else {
148
+ break;
149
+ }
150
+ waiting_control_code = IDLE_CONTROL_CODE;
151
+ }
152
+ }
153
+ __finally
154
+ {
155
+ LeaveCriticalSection(&csControlCode);
156
+ }
157
+
158
+ // This is an ugly polling loop, be as polite as possible
159
+ rb_thread_polling();
160
+ }
161
+
162
+ // force service_stop call
163
+ {
164
+ VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
165
+
166
+ if(EventHookHash != Qnil){
167
+ VALUE val = rb_hash_aref(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP));
168
+
169
+ if(val!=Qnil)
170
+ rb_thread_create(Service_Event_Dispatch, (void*) val);
171
+ }
172
+ }
173
+
174
+ return Qnil;
175
+ }
176
+
177
+ // Handles control signals from the service control manager.
178
+ void WINAPI Service_Ctrl(DWORD dwCtrlCode)
179
+ {
180
+ DWORD dwState = SERVICE_RUNNING;
181
+
182
+ // hard to image this code ever failing, so we probably
183
+ // don't need the __try/__finally wrapper
184
+ __try
185
+ {
186
+ EnterCriticalSection(&csControlCode);
187
+ waiting_control_code = dwCtrlCode;
188
+ }
189
+ __finally
190
+ {
191
+ LeaveCriticalSection(&csControlCode);
192
+ }
193
+
194
+ switch(dwCtrlCode)
195
+ {
196
+ case SERVICE_CONTROL_STOP:
197
+ dwState = SERVICE_STOP_PENDING;
198
+ break;
199
+
200
+ case SERVICE_CONTROL_SHUTDOWN:
201
+ dwState = SERVICE_STOP_PENDING;
202
+ break;
203
+
204
+ case SERVICE_CONTROL_PAUSE:
205
+ dwState = SERVICE_PAUSED;
206
+ break;
207
+
208
+ case SERVICE_CONTROL_CONTINUE:
209
+ dwState = SERVICE_RUNNING;
210
+ break;
211
+
212
+ case SERVICE_CONTROL_INTERROGATE:
213
+ break;
214
+
215
+ default:
216
+ break;
217
+ }
218
+
219
+ // Set the status of the service.
220
+ SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
221
+
222
+ // Tell service_main thread to stop.
223
+ if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
224
+ (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
225
+ {
226
+ if(!SetEvent(hStopEvent))
227
+ SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
228
+ }
229
+ }
230
+
231
+ // Wraps SetServiceStatus.
232
+ void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
233
+ DWORD dwCheckPoint, DWORD dwWaitHint)
234
+ {
235
+ SERVICE_STATUS ss; // Current status of the service.
236
+
237
+ // Disable control requests until the service is started.
238
+ if(dwCurrentState == SERVICE_START_PENDING){
239
+ ss.dwControlsAccepted = 0;
240
+ }
241
+ else{
242
+ ss.dwControlsAccepted =
243
+ SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
244
+ SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN;
245
+ }
246
+
247
+ // Initialize ss structure.
248
+ ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
249
+ ss.dwServiceSpecificExitCode = 0;
250
+ ss.dwCurrentState = dwCurrentState;
251
+ ss.dwWin32ExitCode = dwWin32ExitCode;
252
+ ss.dwCheckPoint = dwCheckPoint;
253
+ ss.dwWaitHint = dwWaitHint;
254
+
255
+ dwServiceState = dwCurrentState;
256
+
257
+ // Send status of the service to the Service Controller.
258
+ if(!SetServiceStatus(ssh, &ss))
259
+ SetEvent(hStopEvent);
260
+ }
261
+
262
+ DWORD WINAPI ThreadProc(LPVOID lpParameter){
263
+ SERVICE_TABLE_ENTRY ste[] =
264
+ {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
265
+
266
+ // No service to step, no service handle, no ruby exceptions, just
267
+ // terminate the thread.
268
+ if(!StartServiceCtrlDispatcher(ste))
269
+ return 1;
270
+
271
+ return 0;
272
+ }
273
+
274
+ static VALUE daemon_allocate(VALUE klass){
275
+ return Data_Wrap_Struct(klass, 0, 0, 0);
276
+ }
277
+
278
+
279
+ // Call service_main method
280
+ static VALUE daemon_mainloop_protect(VALUE self)
281
+ {
282
+ if(rb_respond_to(self,rb_intern("service_main"))){
283
+ if(Argc == 0)
284
+ rb_funcall(self, rb_intern("service_main"), 0);
285
+ else
286
+ rb_funcall2(self, rb_intern("service_main"), Argc, Argv);
287
+ }
288
+
289
+ return self;
290
+ }
291
+
292
+ static VALUE daemon_mainloop_ensure(VALUE self)
293
+ {
294
+ int i;
295
+
296
+ // Signal both the ruby thread and service_main thread to terminate
297
+ SetEvent(hStopEvent);
298
+
299
+ // Wait for ALL ruby threads to exit
300
+ for(i=1; TRUE; i++)
301
+ {
302
+ VALUE list = rb_funcall(rb_cThread, rb_intern("list"), 0);
303
+
304
+ if(RARRAY(list)->len <= 1)
305
+ break;
306
+
307
+ // This is another ugly polling loop, be as polite as possible
308
+ rb_thread_polling();
309
+
310
+ SetTheServiceStatus(SERVICE_STOP_PENDING, 0, i, 1000);
311
+ }
312
+
313
+ // Only one ruby thread
314
+ SetEvent(hStopCompletedEvent);
315
+
316
+ // Wait for the thread to stop BEFORE we close the hStopEvent handle
317
+ WaitForSingleObject(hThread, INFINITE);
318
+
319
+ // Close the event handle, ignoring failures. We may be cleaning up
320
+ // after an exception, so let that exception fall through.
321
+ CloseHandle(hStopEvent);
322
+
323
+ return self;
324
+ }
325
+
326
+ /*
327
+ * This is the method that actually puts your code into a loop and allows it
328
+ * to run as a service. The code that is actually run while in the mainloop
329
+ * is what you defined in your own Daemon#service_main method.
330
+ */
331
+ static VALUE daemon_mainloop(VALUE self)
332
+ {
333
+ DWORD ThreadId;
334
+ HANDLE events[2];
335
+ DWORD index;
336
+ VALUE result, EventHookHash;
337
+ int status = 0;
338
+
339
+ dwServiceState = 0;
340
+
341
+ // Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
342
+ // associated with a tty. This helps newbs avoid Errno::EBADF errors.
343
+ if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
344
+ rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
345
+
346
+ if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
347
+ rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
348
+
349
+ if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
350
+ rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
351
+
352
+ // Use a markable instance variable to prevent the garbage collector
353
+ // from freeing the hash before Ruby_Service_Ctrl exits, or just
354
+ // at any ole time while running the service
355
+ EventHookHash = rb_hash_new();
356
+ rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);
357
+
358
+ // Event hooks
359
+ if(rb_respond_to(self, rb_intern("service_stop"))){
360
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
361
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
362
+ }
363
+
364
+ if(rb_respond_to(self, rb_intern("service_pause"))){
365
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
366
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
367
+ }
368
+
369
+ if(rb_respond_to(self, rb_intern("service_resume"))){
370
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
371
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
372
+ }
373
+
374
+ if(rb_respond_to(self, rb_intern("service_interrogate"))){
375
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
376
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
377
+ }
378
+
379
+ if(rb_respond_to(self, rb_intern("service_shutdown"))){
380
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
381
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
382
+ }
383
+
384
+ #ifdef SERVICE_CONTROL_PARAMCHANGE
385
+ if(rb_respond_to(self, rb_intern("service_paramchange"))){
386
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
387
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
388
+ }
389
+ #endif
390
+
391
+ #ifdef SERVICE_CONTROL_NETBINDADD
392
+ if(rb_respond_to(self, rb_intern("service_netbindadd"))){
393
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
394
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
395
+ }
396
+ #endif
397
+
398
+ #ifdef SERVICE_CONTROL_NETBINDREMOVE
399
+ if(rb_respond_to(self, rb_intern("service_netbindremove"))){
400
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
401
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
402
+ }
403
+ #endif
404
+
405
+ #ifdef SERVICE_CONTROL_NETBINDENABLE
406
+ if(rb_respond_to(self, rb_intern("service_netbindenable"))){
407
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
408
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
409
+ }
410
+ #endif
411
+
412
+ #ifdef SERVICE_CONTROL_NETBINDDISABLE
413
+ if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
414
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
415
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
416
+ }
417
+ #endif
418
+
419
+ // Calling init here so that init failures never even tries to
420
+ // start the service... of course that means that init methods
421
+ // must be very quick, because the SCM will be receiving no
422
+ // START_PENDING messages while init's running - I may fix this
423
+ // later
424
+ if(rb_respond_to(self, rb_intern("service_init")))
425
+ rb_funcall(self, rb_intern("service_init"),0);
426
+
427
+ // Create the event to signal the service to start.
428
+ hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
429
+
430
+ if(hStartEvent == NULL)
431
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
432
+
433
+ // Create the event to signal the service to stop.
434
+ hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
435
+
436
+ if(hStopEvent == NULL)
437
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
438
+
439
+ // Create the event to signal the service that stop has completed
440
+ hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
441
+
442
+ if(hStopCompletedEvent == NULL)
443
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
444
+
445
+ // Create Thread for service main
446
+ hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
447
+
448
+ if(hThread == INVALID_HANDLE_VALUE)
449
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
450
+
451
+ events[0] = hThread;
452
+ events[1] = hStartEvent;
453
+
454
+ // wait for Service_Main function to either start the service OR terminate
455
+ while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
456
+ {
457
+ }
458
+
459
+ // thread exited, so the show is off
460
+ if(index == WAIT_OBJECT_0)
461
+ rb_raise(cDaemonError, "Service_Main thread exited abnormally");
462
+
463
+ // from this point onward, stopevent must be triggered!
464
+
465
+ // Create the green thread to poll for Service_Ctrl events
466
+ rb_thread_create(Ruby_Service_Ctrl, (void *)self);
467
+
468
+ result = rb_protect(daemon_mainloop_protect, self, &status);
469
+
470
+ // service_main raised an exception
471
+ if(status){
472
+ daemon_mainloop_ensure(self);
473
+ rb_jump_tag(status);
474
+ }
475
+
476
+ // service_main exited cleanly
477
+ return daemon_mainloop_ensure(self);
478
+ }
479
+
480
+ /*
481
+ * Returns the state of the service (as an constant integer) which can be any
482
+ * of the service status constants, e.g. RUNNING, PAUSED, etc.
483
+ *
484
+ * This method is typically used within your service_main method to setup the
485
+ * loop. For example:
486
+ *
487
+ * class MyDaemon < Daemon
488
+ * def service_main
489
+ * while state == RUNNING || state == PAUSED || state == IDLE
490
+ * # Your main loop here
491
+ * end
492
+ * end
493
+ * end
494
+ *
495
+ * See the Daemon#running? method for an abstraction of the above code.
496
+ */
497
+ static VALUE daemon_state(VALUE self){
498
+ return UINT2NUM(dwServiceState);
499
+ }
500
+
501
+ /*
502
+ * Returns whether or not the service is in a running state, i.e. the service
503
+ * status is either RUNNING, PAUSED or IDLE.
504
+ *
505
+ * This is typically used within your service_main method to setup the main
506
+ * loop. For example:
507
+ *
508
+ * class MyDaemon < Daemon
509
+ * def service_main
510
+ * while running?
511
+ * # Your main loop here
512
+ * end
513
+ * end
514
+ * end
515
+ */
516
+ static VALUE daemon_is_running(VALUE self){
517
+ VALUE v_bool = Qfalse;
518
+ if(
519
+ (dwServiceState == SERVICE_RUNNING) ||
520
+ (dwServiceState == SERVICE_PAUSED) ||
521
+ (dwServiceState == 0)
522
+ ){
523
+ v_bool = Qtrue;
524
+ }
525
+
526
+ return v_bool;
527
+ }
528
+
529
+ /*
530
+ * This is a shortcut for Daemon.new + Daemon#mainloop.
531
+ */
532
+ static VALUE daemon_c_mainloop(VALUE klass){
533
+ VALUE v_args[1];
534
+ VALUE v_daemon = rb_class_new_instance(0, v_args, klass);
535
+ return rb_funcall(v_daemon, rb_intern("mainloop"), 0, 0);
536
+ }
537
+
538
+ void Init_daemon()
539
+ {
540
+ /* The Win32 module serves as a namespace only. */
541
+ VALUE mWin32 = rb_define_module("Win32");
542
+
543
+ /* The Daemon class encapsulates a Windows service through the use
544
+ * of callback methods and a main loop.
545
+ */
546
+ VALUE cDaemon = rb_define_class_under(mWin32, "Daemon", rb_cObject);
547
+
548
+ /* Error typically raised if something goes wrong with your daemon. */
549
+ cDaemonError = rb_define_class_under(cDaemon, "Error", rb_eStandardError);
550
+
551
+ rb_define_alloc_func(cDaemon, daemon_allocate);
552
+ rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
553
+ rb_define_method(cDaemon, "state", daemon_state, 0);
554
+ rb_define_method(cDaemon, "running?", daemon_is_running, 0);
555
+
556
+ rb_define_singleton_method(cDaemon, "mainloop", daemon_c_mainloop, 0);
557
+
558
+ // Intialize critical section used by green polling thread
559
+ InitializeCriticalSection(&csControlCode);
560
+
561
+ // Constants
562
+
563
+ /* 0.7.0: The version of this library */
564
+ rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
565
+
566
+ /* Service has received a signal to resume but is not yet running */
567
+ rb_define_const(cDaemon, "CONTINUE_PENDING",
568
+ INT2NUM(SERVICE_CONTINUE_PENDING));
569
+
570
+ /* Service has received a signal to pause but is not yet paused */
571
+ rb_define_const(cDaemon, "PAUSE_PENDING",
572
+ INT2NUM(SERVICE_PAUSE_PENDING));
573
+
574
+ /* Service is in a paused state */
575
+ rb_define_const(cDaemon, "PAUSED",
576
+ INT2NUM(SERVICE_PAUSED));
577
+
578
+ /* Service is running */
579
+ rb_define_const(cDaemon, "RUNNING",
580
+ INT2NUM(SERVICE_RUNNING));
581
+
582
+ /* Service has received a signal to start but is not yet running */
583
+ rb_define_const(cDaemon, "START_PENDING",
584
+ INT2NUM(SERVICE_START_PENDING));
585
+
586
+ /* Service has received a signal to stop but has not yet stopped */
587
+ rb_define_const(cDaemon, "STOP_PENDING",
588
+ INT2NUM(SERVICE_STOP_PENDING));
589
+
590
+ /* Service is stopped. */
591
+ rb_define_const(cDaemon, "STOPPED",
592
+ INT2NUM(SERVICE_STOPPED));
593
+
594
+ /* Service is in an idle state */
595
+ rb_define_const(cDaemon, "IDLE", INT2NUM(0));
596
+ }