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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }