win32-service 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
data/extconf.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'mkmf'
2
+ require 'fileutils'
3
+
4
+ FileUtils.cp('lib/win32/service.c', '.')
5
+ FileUtils.cp('lib/win32/service.h', '.')
6
+
7
+ have_func('RegisterServiceCtrlHandlerEx') # For future use
8
+
9
+ bool1 = have_func('EnumServicesStatusEx')
10
+ bool2 = have_func('QueryServiceStatusEx')
11
+
12
+ # This is used by the test suite to help setup a couple tests
13
+ File.open('test/tmp.yml', 'a+'){ |f|
14
+ f.puts 'HAVE_ENUMSERVICESTATUSEX: #{bool1}'
15
+ f.puts 'HAVE_QUERYSERVICESTATUSEX: #{bool2}'
16
+ }
17
+
18
+ create_makefile('win32/service')
@@ -0,0 +1,2131 @@
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
+ #include "service.h"
8
+
9
+ #ifndef UNICODE
10
+ #define UNICODE
11
+ #endif
12
+
13
+ static VALUE cServiceError;
14
+ static VALUE cDaemonError;
15
+ static VALUE v_service_struct, v_service_status_struct;
16
+
17
+ static HANDLE hStartEvent;
18
+ static HANDLE hStopEvent;
19
+ static HANDLE hStopCompletedEvent;
20
+ static SERVICE_STATUS_HANDLE ssh;
21
+ static DWORD dwServiceState;
22
+ static TCHAR error[1024];
23
+
24
+ static VALUE EventHookHash;
25
+ static VALUE thread_group;
26
+ static int cAdd;
27
+ static int cList;
28
+ static int cSize;
29
+
30
+ CRITICAL_SECTION csControlCode;
31
+ // I happen to know from looking in the header file
32
+ // that 0 is not a valid service control code
33
+ // so we will use it, the value does not matter
34
+ // as long as it will never show up in ServiceCtrl
35
+ // - Patrick Hurley
36
+ #define IDLE_CONTROL_CODE 0
37
+ static int waiting_control_code = IDLE_CONTROL_CODE;
38
+
39
+ static VALUE service_close(VALUE);
40
+ void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);
41
+ void WINAPI Service_Ctrl(DWORD dwCtrlCode);
42
+ void ErrorStopService();
43
+ void SetTheServiceStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,
44
+ DWORD dwCheckPoint, DWORD dwWaitHint);
45
+
46
+ // Called by the service control manager after the call to
47
+ // StartServiceCtrlDispatcher.
48
+ void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
49
+ {
50
+ int i;
51
+
52
+ // Obtain the name of the service.
53
+ LPTSTR lpszServiceName = lpszArgv[0];
54
+
55
+ // Register the service ctrl handler.
56
+ ssh = RegisterServiceCtrlHandler(lpszServiceName,
57
+ (LPHANDLER_FUNCTION)Service_Ctrl);
58
+
59
+ if(ssh == (SERVICE_STATUS_HANDLE)0){
60
+ ErrorStopService();
61
+ rb_raise(cDaemonError,"RegisterServiceCtrlHandler failed");
62
+ }
63
+
64
+ // wait for sevice initialization
65
+ for(i=1;TRUE;i++)
66
+ {
67
+ if(WaitForSingleObject(hStartEvent, 1000) == WAIT_OBJECT_0)
68
+ break;
69
+
70
+ SetTheServiceStatus(SERVICE_START_PENDING, 0, i, 1000);
71
+ }
72
+
73
+ // The service has started.
74
+ SetTheServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0);
75
+
76
+ // Main loop for the service.
77
+ while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
78
+ {
79
+ }
80
+
81
+ // Stop the service.
82
+ SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
83
+ }
84
+
85
+ VALUE Service_Event_Dispatch(VALUE val)
86
+ {
87
+ VALUE func,self;
88
+ VALUE result = Qnil;
89
+
90
+ if(val!=Qnil) {
91
+ self = RARRAY(val)->ptr[0];
92
+ func = NUM2INT(RARRAY(val)->ptr[1]);
93
+
94
+ result = rb_funcall(self,func,0);
95
+ }
96
+
97
+ return result;
98
+ }
99
+
100
+ VALUE Ruby_Service_Ctrl()
101
+ {
102
+ while (WaitForSingleObject(hStopEvent,0) == WAIT_TIMEOUT)
103
+ {
104
+ __try
105
+ {
106
+ EnterCriticalSection(&csControlCode);
107
+
108
+ // Check to see if anything interesting has been signaled
109
+ if (waiting_control_code != IDLE_CONTROL_CODE)
110
+ {
111
+ // if there is a code, create a ruby thread to deal with it
112
+ // this might be over engineering the solution, but I don't
113
+ // want to block Service_Ctrl longer than necessary and the
114
+ // critical section will block it.
115
+ VALUE val = rb_hash_aref(EventHookHash, INT2NUM(waiting_control_code));
116
+ if(val!=Qnil) {
117
+ VALUE thread = rb_thread_create(Service_Event_Dispatch, (void*) val);
118
+ rb_funcall(thread_group, cAdd, 1, thread);
119
+ }
120
+
121
+ // some seriously ugly flow control going on in here
122
+ if (waiting_control_code == SERVICE_CONTROL_STOP)
123
+ break;
124
+
125
+ waiting_control_code = IDLE_CONTROL_CODE;
126
+ }
127
+ }
128
+ __finally
129
+ {
130
+ LeaveCriticalSection(&csControlCode);
131
+ }
132
+
133
+ // This is an ugly polling loop, be as polite as possible
134
+ rb_thread_polling();
135
+ }
136
+
137
+ for (;;)
138
+ {
139
+ VALUE list = rb_funcall(thread_group, cList, 0);
140
+ VALUE size = rb_funcall(list, cSize, 0);
141
+ if (NUM2INT(size) == 0)
142
+ break;
143
+
144
+ // This is another ugly polling loop, be as polite as possible
145
+ rb_thread_polling();
146
+ }
147
+ SetEvent(hStopCompletedEvent);
148
+
149
+ return Qnil;
150
+ }
151
+
152
+ // Handles control signals from the service control manager.
153
+ void WINAPI Service_Ctrl(DWORD dwCtrlCode)
154
+ {
155
+ DWORD dwState = SERVICE_RUNNING;
156
+
157
+ // hard to image this code ever failing, so we probably
158
+ // don't need the __try/__finally wrapper
159
+ __try
160
+ {
161
+ EnterCriticalSection(&csControlCode);
162
+ waiting_control_code = dwCtrlCode;
163
+ }
164
+ __finally
165
+ {
166
+ LeaveCriticalSection(&csControlCode);
167
+ }
168
+
169
+ switch(dwCtrlCode)
170
+ {
171
+ case SERVICE_CONTROL_STOP:
172
+ dwState = SERVICE_STOP_PENDING;
173
+ break;
174
+
175
+ case SERVICE_CONTROL_SHUTDOWN:
176
+ dwState = SERVICE_STOP_PENDING;
177
+ break;
178
+
179
+ case SERVICE_CONTROL_PAUSE:
180
+ dwState = SERVICE_PAUSED;
181
+ break;
182
+
183
+ case SERVICE_CONTROL_CONTINUE:
184
+ dwState = SERVICE_RUNNING;
185
+ break;
186
+
187
+ case SERVICE_CONTROL_INTERROGATE:
188
+ break;
189
+
190
+ default:
191
+ break;
192
+ }
193
+
194
+ // Set the status of the service.
195
+ SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
196
+
197
+ // Tell service_main thread to stop.
198
+ if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
199
+ (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
200
+ {
201
+ // how long should we give ruby to clean up?
202
+ // right now we give it forever :-)
203
+ while (WaitForSingleObject(hStopCompletedEvent, 500) == WAIT_TIMEOUT)
204
+ {
205
+ SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
206
+ }
207
+
208
+ if (!SetEvent(hStopEvent))
209
+ ErrorStopService();
210
+ // Raise an error here?
211
+ }
212
+ }
213
+
214
+ // Wraps SetServiceStatus.
215
+ void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
216
+ DWORD dwCheckPoint, DWORD dwWaitHint)
217
+ {
218
+ SERVICE_STATUS ss; // Current status of the service.
219
+
220
+ // Disable control requests until the service is started.
221
+ if (dwCurrentState == SERVICE_START_PENDING){
222
+ ss.dwControlsAccepted = 0;
223
+ }
224
+ else{
225
+ ss.dwControlsAccepted =
226
+ SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
227
+ SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN;
228
+ }
229
+
230
+ // Initialize ss structure.
231
+ ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
232
+ ss.dwServiceSpecificExitCode = 0;
233
+ ss.dwCurrentState = dwCurrentState;
234
+ ss.dwWin32ExitCode = dwWin32ExitCode;
235
+ ss.dwCheckPoint = dwCheckPoint;
236
+ ss.dwWaitHint = dwWaitHint;
237
+
238
+ dwServiceState = dwCurrentState;
239
+
240
+ // Send status of the service to the Service Controller.
241
+ if(!SetServiceStatus(ssh, &ss)){
242
+ ErrorStopService();
243
+ }
244
+ }
245
+
246
+ // Handle API errors or other problems by ending the service
247
+ void ErrorStopService(){
248
+
249
+ // If you have threads running, tell them to stop. Something went
250
+ // wrong, and you need to stop them so you can inform the SCM.
251
+ SetEvent(hStopEvent);
252
+
253
+ // Stop the service.
254
+ SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
255
+ }
256
+
257
+ DWORD WINAPI ThreadProc(LPVOID lpParameter){
258
+ SERVICE_TABLE_ENTRY ste[] =
259
+ {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
260
+
261
+ if (!StartServiceCtrlDispatcher(ste)){
262
+ ErrorStopService();
263
+ strcpy(error,ErrorDescription(GetLastError()));
264
+ // Very questionable here, we should generate an event
265
+ // and be polling in a green thread for the event, but
266
+ // this really should not happen so here we go
267
+ rb_raise(cDaemonError,error);
268
+ }
269
+
270
+ return 0;
271
+ }
272
+
273
+ static VALUE daemon_allocate(VALUE klass){
274
+ EventHookHash = rb_hash_new();
275
+
276
+ thread_group = rb_class_new_instance(0, 0,
277
+ rb_const_get(rb_cObject, rb_intern("ThreadGroup")));
278
+
279
+ return Data_Wrap_Struct(klass, 0, 0, 0);
280
+ }
281
+
282
+ /*
283
+ * This is the method that actually puts your code into a loop and allows it
284
+ * to run as a service. The code that is actually run while in the mainloop
285
+ * is what you defined in your own Daemon#service_main method.
286
+ */
287
+ static VALUE
288
+ daemon_mainloop(VALUE self)
289
+ {
290
+ DWORD ThreadId;
291
+ HANDLE hThread;
292
+
293
+ dwServiceState = 0;
294
+
295
+ // Save a couple symbols
296
+ cAdd = rb_intern("add");
297
+ cList = rb_intern("list");
298
+ cSize = rb_intern("size");
299
+
300
+ // Event hooks
301
+ if(rb_respond_to(self,rb_intern("service_stop"))){
302
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_STOP),
303
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_stop"))));
304
+ }
305
+
306
+ if(rb_respond_to(self,rb_intern("service_pause"))){
307
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_PAUSE),
308
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_pause"))));
309
+ }
310
+
311
+ if(rb_respond_to(self,rb_intern("service_resume"))){
312
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_CONTINUE),
313
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_resume"))));
314
+ }
315
+
316
+ if(rb_respond_to(self,rb_intern("service_interrogate"))){
317
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_INTERROGATE),
318
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_interrogate"))));
319
+ }
320
+
321
+ if(rb_respond_to(self,rb_intern("service_shutdown"))){
322
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_SHUTDOWN),
323
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_shutdown"))));
324
+ }
325
+
326
+ #ifdef SERVICE_CONTROL_PARAMCHANGE
327
+ if(rb_respond_to(self,rb_intern("service_paramchange"))){
328
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
329
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_paramchange"))));
330
+ }
331
+ #endif
332
+
333
+ #ifdef SERVICE_CONTROL_NETBINDADD
334
+ if(rb_respond_to(self,rb_intern("service_netbindadd"))){
335
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_NETBINDADD),
336
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_netbindadd"))));
337
+ }
338
+ #endif
339
+
340
+ #ifdef SERVICE_CONTROL_NETBINDREMOVE
341
+ if(rb_respond_to(self,rb_intern("service_netbindremove"))){
342
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
343
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_netbindremove"))));
344
+ }
345
+ #endif
346
+
347
+ #ifdef SERVICE_CONTROL_NETBINDENABLE
348
+ if(rb_respond_to(self,rb_intern("service_netbindenable"))){
349
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
350
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_netbindenable"))));
351
+ }
352
+ #endif
353
+
354
+ #ifdef SERVICE_CONTROL_NETBINDDISABLE
355
+ if(rb_respond_to(self,rb_intern("service_netbinddisable"))){
356
+ rb_hash_aset(EventHookHash,INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
357
+ rb_ary_new3(2,self,INT2NUM(rb_intern("service_netbinddisable"))));
358
+ }
359
+ #endif
360
+
361
+ // Create the event to signal the service to start.
362
+ hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
363
+ if(hStartEvent == NULL){
364
+ strcpy(error,ErrorDescription(GetLastError()));
365
+ ErrorStopService();
366
+ rb_raise(cDaemonError,error);
367
+ }
368
+
369
+ // Create the event to signal the service to stop.
370
+ hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
371
+ if(hStopEvent == NULL){
372
+ strcpy(error,ErrorDescription(GetLastError()));
373
+ ErrorStopService();
374
+ rb_raise(cDaemonError,error);
375
+ }
376
+
377
+ // Create the event to signal the service that stop has completed
378
+ hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
379
+ if(hStopCompletedEvent == NULL){
380
+ strcpy(error,ErrorDescription(GetLastError()));
381
+ ErrorStopService();
382
+ rb_raise(cDaemonError,error);
383
+ }
384
+
385
+ // Create the green thread to poll for Service_Ctrl events
386
+ rb_thread_create(Ruby_Service_Ctrl, 0);
387
+
388
+ // Create Thread for service main
389
+ hThread = CreateThread(NULL,0,ThreadProc,0,0,&ThreadId);
390
+ if(hThread == INVALID_HANDLE_VALUE){
391
+ strcpy(error,ErrorDescription(GetLastError()));
392
+ ErrorStopService();
393
+ rb_raise(cDaemonError,error);
394
+ }
395
+
396
+ if(rb_respond_to(self,rb_intern("service_init"))){
397
+ rb_funcall(self,rb_intern("service_init"),0);
398
+ }
399
+
400
+ SetEvent(hStartEvent);
401
+
402
+ // Call service_main method
403
+ if(rb_respond_to(self,rb_intern("service_main"))){
404
+ rb_funcall(self,rb_intern("service_main"),0);
405
+ }
406
+
407
+ while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
408
+ {
409
+ }
410
+
411
+ // Close the event handle and the thread handle.
412
+ if(!CloseHandle(hStopEvent)){
413
+ strcpy(error,ErrorDescription(GetLastError()));
414
+ ErrorStopService();
415
+ rb_raise(cDaemonError,error);
416
+ }
417
+
418
+ // Wait for Thread service main
419
+ WaitForSingleObject(hThread, INFINITE);
420
+
421
+ return self;
422
+ }
423
+
424
+ /*
425
+ * Returns the state of the service (as an constant integer) which can be any
426
+ * of the service status constants, e.g. RUNNING, PAUSED, etc.
427
+ *
428
+ * This method is typically used within your service_main method to setup the
429
+ * loop. For example:
430
+ *
431
+ * class MyDaemon < Daemon
432
+ * def service_main
433
+ * while state == RUNNING || state == PAUSED || state == IDLE
434
+ * # Your main loop here
435
+ * end
436
+ * end
437
+ * end
438
+ *
439
+ * See the Daemon#running? method for an abstraction of the above code.
440
+ */
441
+ static VALUE daemon_state(VALUE self){
442
+ return UINT2NUM(dwServiceState);
443
+ }
444
+
445
+ /*
446
+ * Returns whether or not the service is in a running state, i.e. the service
447
+ * status is either RUNNING, PAUSED or IDLE.
448
+ *
449
+ * This is typically used within your service_main method to setup the main
450
+ * loop. For example:
451
+ *
452
+ * class MyDaemon < Daemon
453
+ * def service_main
454
+ * while running?
455
+ * # Your main loop here
456
+ * end
457
+ * end
458
+ * end
459
+ */
460
+ static VALUE daemon_is_running(VALUE self){
461
+ VALUE v_bool = Qfalse;
462
+ if(
463
+ (dwServiceState == SERVICE_RUNNING) ||
464
+ (dwServiceState == SERVICE_PAUSED) ||
465
+ (dwServiceState == 0)
466
+ ){
467
+ v_bool = Qtrue;
468
+ }
469
+
470
+ return v_bool;
471
+ }
472
+
473
+ static VALUE service_allocate(VALUE klass){
474
+ SvcStruct* ptr = malloc(sizeof(SvcStruct));
475
+ return Data_Wrap_Struct(klass,0,service_free,ptr);
476
+ }
477
+
478
+ /* call-seq:
479
+ * Service.new(host=nil, desired_access=nil)
480
+ * Service.new(host=nil, desired_access=nil){ |svc| ... }
481
+ *
482
+ * Creates and returns a new Win32::Service handle on +host+ with the
483
+ * +desired_access+. If no host is specified, your local machine is
484
+ * used. If no desired access is specified, then
485
+ * Service::MANAGER_CREATE_SERVICE is used.
486
+ *
487
+ * If a block is provided then the object is yielded back to the block and
488
+ * automatically closed at the end of the block.
489
+ */
490
+ static VALUE service_init(int argc, VALUE *argv, VALUE self){
491
+ VALUE v_machine_name, v_desired_access;
492
+ TCHAR* lpMachineName;
493
+ DWORD dwDesiredAccess;
494
+ SvcStruct* ptr;
495
+
496
+ Data_Get_Struct(self, SvcStruct, ptr);
497
+
498
+ rb_scan_args(argc, argv, "02", &v_machine_name, &v_desired_access);
499
+
500
+ if(NIL_P(v_machine_name)){
501
+ lpMachineName = NULL;
502
+ }
503
+ else{
504
+ SafeStringValue(v_machine_name);
505
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
506
+ }
507
+
508
+ if(NIL_P(v_desired_access))
509
+ dwDesiredAccess = SC_MANAGER_CREATE_SERVICE;
510
+ else
511
+ dwDesiredAccess = NUM2INT(v_desired_access);
512
+
513
+ ptr->hSCManager = OpenSCManager(
514
+ lpMachineName,
515
+ NULL,
516
+ dwDesiredAccess
517
+ );
518
+
519
+ if(!ptr->hSCManager)
520
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
521
+
522
+ rb_iv_set(self, "@machine_name", v_machine_name);
523
+ rb_iv_set(self, "@desired_access", v_desired_access);
524
+ rb_iv_set(self, "@service_type",
525
+ INT2FIX(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS));
526
+
527
+ rb_iv_set(self, "@start_type", INT2FIX(SERVICE_DEMAND_START));
528
+ rb_iv_set(self, "@error_control", INT2FIX(SERVICE_ERROR_NORMAL));
529
+
530
+ if(rb_block_given_p())
531
+ rb_ensure(rb_yield, self, service_close, self);
532
+
533
+ return self;
534
+ }
535
+
536
+ /*
537
+ * call-seq:
538
+ * Service#close
539
+ *
540
+ * Closes the service handle. This is the polite way to do things, although
541
+ * the service handle should automatically be closed when it goes out of
542
+ * scope.
543
+ */
544
+ static VALUE service_close(VALUE self){
545
+ SvcStruct* ptr;
546
+ int rv;
547
+
548
+ Data_Get_Struct(self, SvcStruct, ptr);
549
+
550
+ rv = CloseServiceHandle(ptr->hSCManager);
551
+
552
+ if(ptr->hSCManager){
553
+ if(0 == rv){
554
+ rb_raise(cServiceError, ErrorDescription(GetLastError()));
555
+ }
556
+ }
557
+
558
+ return self;
559
+ }
560
+
561
+ /*
562
+ * call-seq:
563
+ * Service#configure_service{ |service| ... }
564
+ *
565
+ * Configures the service object. Valid methods for the service object are
566
+ * as follows:
567
+ *
568
+ * * desired_access=
569
+ * * service_name=
570
+ * * display_name=
571
+ * * service_type=
572
+ * * start_type=
573
+ * * error_control=
574
+ * * tag_id=
575
+ * * binary_path_name=
576
+ * * load_order_group=
577
+ * * start_name=
578
+ * * password=
579
+ * * dependencies=
580
+ * * service_description=
581
+ *
582
+ * See the docs for individual instance methods for more details.
583
+ */
584
+ static VALUE service_configure(VALUE self){
585
+ SvcStruct* ptr;
586
+ SC_HANDLE hSCService;
587
+ DWORD dwServiceType, dwStartType, dwErrorControl;
588
+ TCHAR* lpServiceName;
589
+ TCHAR* lpDisplayName;
590
+ TCHAR* lpBinaryPathName;
591
+ TCHAR* lpLoadOrderGroup;
592
+ TCHAR* lpServiceStartName;
593
+ TCHAR* lpPassword;
594
+ TCHAR* lpDependencies;
595
+ int rv;
596
+
597
+ Data_Get_Struct(self,SvcStruct,ptr);
598
+
599
+ rb_yield(self); /* block is mandatory */
600
+
601
+ if(NIL_P(rb_iv_get(self, "@service_name"))){
602
+ rb_raise(cServiceError, "No service name specified");
603
+ }
604
+ else{
605
+ VALUE v_tmp = rb_iv_get(self, "@service_name");
606
+ lpServiceName = TEXT(StringValuePtr(v_tmp));
607
+ }
608
+
609
+ hSCService = OpenService(
610
+ ptr->hSCManager,
611
+ lpServiceName,
612
+ SERVICE_CHANGE_CONFIG
613
+ );
614
+
615
+ if(!hSCService)
616
+ rb_raise(cServiceError, ErrorDescription(GetLastError()));
617
+
618
+ if(NIL_P(rb_iv_get(self, "@service_type")))
619
+ dwServiceType = SERVICE_NO_CHANGE;
620
+ else
621
+ dwServiceType = NUM2INT(rb_iv_get(self, "@service_type"));
622
+
623
+ if(NIL_P(rb_iv_get(self, "@start_type")))
624
+ dwStartType = SERVICE_NO_CHANGE;
625
+ else
626
+ dwStartType = NUM2INT(rb_iv_get(self, "@start_type"));
627
+
628
+ if(NIL_P(rb_iv_get(self, "@error_control")))
629
+ dwErrorControl = SERVICE_NO_CHANGE;
630
+ else
631
+ dwErrorControl = NUM2INT(rb_iv_get(self, "@error_control"));
632
+
633
+ if(NIL_P(rb_iv_get(self, "@binary_path_name"))){
634
+ lpBinaryPathName = NULL;
635
+ }
636
+ else{
637
+ VALUE v_tmp = rb_iv_get(self, "@binary_path_name");
638
+ lpBinaryPathName = TEXT(StringValuePtr(v_tmp));
639
+ }
640
+
641
+ if(NIL_P(rb_iv_get(self, "@load_order_group"))){
642
+ lpLoadOrderGroup = NULL;
643
+ }
644
+ else{
645
+ VALUE v_tmp = rb_iv_get(self, "@load_order_group");
646
+ lpLoadOrderGroup = TEXT(StringValuePtr(v_tmp));
647
+ }
648
+
649
+ /* There are 3 possibilities for dependencies - Some, none, or unchanged:
650
+ *
651
+ * null => don't change
652
+ * empty array => no dependencies (deletes any existing dependencies)
653
+ * array => sets dependencies (deletes any existing dependencies)
654
+ */
655
+ if(NIL_P(rb_iv_get(self, "@dependencies"))){
656
+ lpDependencies = NULL;
657
+ }
658
+ else{
659
+ int i,size=1;
660
+ TCHAR* ptr;
661
+ VALUE rbDepArray = rb_iv_get(self, "@dependencies");
662
+
663
+ if(0 == RARRAY(rbDepArray)->len){
664
+ lpDependencies = TEXT("");
665
+ }
666
+ else{
667
+ for(i = 0; i< RARRAY(rbDepArray)->len; i++)
668
+ {
669
+ size += strlen(StringValueCStr(RARRAY(rbDepArray)->ptr[i]))+1;
670
+ }
671
+ lpDependencies = malloc(size);
672
+ memset(lpDependencies, 0x00, size);
673
+ ptr = lpDependencies;
674
+ for(i = 0; i < RARRAY(rbDepArray)->len; i++){
675
+ VALUE v_tmp = rb_ary_entry(rbDepArray,i);
676
+ TCHAR* string = TEXT(StringValuePtr(v_tmp));
677
+ memcpy(ptr,string,strlen(string));
678
+ ptr+=strlen(string)+1;
679
+ }
680
+ }
681
+ }
682
+
683
+ if(NIL_P(rb_iv_get(self, "@start_name"))){
684
+ lpServiceStartName = NULL;
685
+ }
686
+ else{
687
+ VALUE v_tmp = rb_iv_get(self, "@start_name");
688
+ lpServiceStartName = TEXT(StringValuePtr(v_tmp));
689
+ }
690
+
691
+ if(NIL_P(rb_iv_get(self, "@password"))){
692
+ lpPassword = NULL;
693
+ }
694
+ else{
695
+ VALUE v_tmp = rb_iv_get(self, "@password");
696
+ lpPassword = TEXT(StringValuePtr(v_tmp));
697
+ }
698
+
699
+ if(NIL_P(rb_iv_get(self, "@display_name"))){
700
+ lpDisplayName = NULL;
701
+ }
702
+ else{
703
+ VALUE v_tmp = rb_iv_get(self, "@display_name");
704
+ lpDisplayName = TEXT(StringValuePtr(v_tmp));
705
+ }
706
+
707
+ rv = ChangeServiceConfig(
708
+ hSCService,
709
+ dwServiceType,
710
+ dwStartType,
711
+ dwErrorControl,
712
+ lpBinaryPathName,
713
+ lpLoadOrderGroup,
714
+ NULL, // TagID
715
+ lpDependencies,
716
+ lpServiceStartName,
717
+ lpPassword,
718
+ lpDisplayName
719
+ );
720
+
721
+ if(lpDependencies)
722
+ free(lpDependencies);
723
+
724
+ if(0 == rv){
725
+ strcpy(error,ErrorDescription(GetLastError()));
726
+ CloseServiceHandle(hSCService);
727
+ rb_raise(cServiceError,error);
728
+ }
729
+
730
+ if(!NIL_P(rb_iv_get(self, "@service_description"))){
731
+ SERVICE_DESCRIPTION servDesc;
732
+ VALUE v_desc = rb_iv_get(self, "@service_description");
733
+
734
+ servDesc.lpDescription = TEXT(StringValuePtr(v_desc));
735
+
736
+ if(!ChangeServiceConfig2(
737
+ hSCService,
738
+ SERVICE_CONFIG_DESCRIPTION,
739
+ &servDesc
740
+ )){
741
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
742
+ }
743
+ }
744
+
745
+ CloseServiceHandle(hSCService);
746
+
747
+ return self;
748
+ }
749
+
750
+ /*
751
+ * call-seq:
752
+ * Service#create_service{ |service| ... }
753
+ *
754
+ * Creates the specified service. In order for this to work, the
755
+ * 'service_name' and 'binary_path_name' attributes must be defined
756
+ * or ServiceError will be raised.
757
+
758
+ * See the Service#configure_service method for a list of valid methods to
759
+ * pass to the service object. See the individual methods for more
760
+ * information, including default values.
761
+ */
762
+ static VALUE service_create(VALUE self){
763
+ VALUE v_tmp;
764
+ SvcStruct* ptr;
765
+ SC_HANDLE hSCService;
766
+ DWORD dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl;
767
+ TCHAR* lpMachineName;
768
+ TCHAR* lpServiceName;
769
+ TCHAR* lpDisplayName;
770
+ TCHAR* lpBinaryPathName;
771
+ TCHAR* lpLoadOrderGroup;
772
+ TCHAR* lpServiceStartName;
773
+ TCHAR* lpPassword;
774
+ TCHAR* lpDependencies;
775
+
776
+ if(rb_block_given_p())
777
+ rb_yield(self);
778
+
779
+ Data_Get_Struct(self, SvcStruct, ptr);
780
+
781
+ // The service name and exe name must be set to create a service
782
+ if(NIL_P(rb_iv_get(self, "@service_name")))
783
+ rb_raise(cServiceError, "Service Name must be defined");
784
+
785
+ if(NIL_P(rb_iv_get(self, "@binary_path_name")))
786
+ rb_raise(cServiceError, "Executable Name must be defined");
787
+
788
+ // If the display name is not set, set it to the same as the service name
789
+ if(NIL_P(rb_iv_get(self, "@display_name")))
790
+ rb_iv_set(self,"@display_name", rb_iv_get(self,"@service_name"));
791
+
792
+ v_tmp = rb_iv_get(self, "@service_name");
793
+ lpServiceName = TEXT(StringValuePtr(v_tmp));
794
+
795
+ v_tmp = rb_iv_get(self, "@display_name");
796
+ lpDisplayName = TEXT(StringValuePtr(v_tmp));
797
+
798
+ v_tmp = rb_iv_get(self, "@binary_path_name");
799
+ lpBinaryPathName = TEXT(StringValuePtr(v_tmp));
800
+
801
+ if(NIL_P(rb_iv_get(self, "@machine_name"))){
802
+ lpMachineName = NULL;
803
+ }
804
+ else{
805
+ v_tmp = rb_iv_get(self, "@machine_name");
806
+ lpMachineName = TEXT(StringValuePtr(v_tmp));
807
+ }
808
+
809
+ if(NIL_P(rb_iv_get(self, "@load_order_group"))){
810
+ lpLoadOrderGroup = NULL;
811
+ }
812
+ else{
813
+ v_tmp = rb_iv_get(self, "@load_order_group");
814
+ lpLoadOrderGroup = TEXT(StringValuePtr(v_tmp));
815
+ }
816
+
817
+ if(NIL_P(rb_iv_get(self, "@start_name"))){
818
+ lpServiceStartName = NULL;
819
+ }
820
+ else{
821
+ v_tmp = rb_iv_get(self,"@start_name");
822
+ lpServiceStartName =
823
+ TEXT(StringValuePtr(v_tmp));
824
+ }
825
+
826
+ if(NIL_P(rb_iv_get(self, "@password"))){
827
+ lpPassword = NULL;
828
+ }
829
+ else{
830
+ v_tmp = rb_iv_get(self,"@password");
831
+ lpPassword = TEXT(StringValuePtr(v_tmp));
832
+ }
833
+
834
+ // There are 3 possibilities for dependencies - Some, none, or unchanged
835
+ // null = don't change
836
+ // empty array = no dependencies (deletes any existing dependencies)
837
+ // array = sets dependencies (deletes any existing dependencies)
838
+ if(NIL_P(rb_iv_get(self, "@dependencies"))){
839
+ lpDependencies = NULL;
840
+ }
841
+ else{
842
+ int i,size=1;
843
+ TCHAR* ptr;
844
+ VALUE rbDepArray = rb_iv_get(self, "@dependencies");
845
+
846
+ if(0 == RARRAY(rbDepArray)->len){
847
+ lpDependencies = TEXT("");
848
+ }
849
+ else{
850
+ for(i = 0; i< RARRAY(rbDepArray)->len; i++)
851
+ {
852
+ size += strlen(StringValueCStr(RARRAY(rbDepArray)->ptr[i]))+1;
853
+ }
854
+ lpDependencies = malloc(size);
855
+ memset(lpDependencies,0x00,size);
856
+ ptr = lpDependencies;
857
+ for(i = 0; i < RARRAY(rbDepArray)->len; i++){
858
+ VALUE v_tmp = rb_ary_entry(rbDepArray,i);
859
+ TCHAR* string = TEXT(StringValuePtr(v_tmp));
860
+ memcpy(ptr,string,strlen(string));
861
+ ptr+=strlen(string)+1;
862
+ }
863
+ }
864
+ }
865
+
866
+ if(NIL_P(rb_iv_get(self, "@desired_access"))){
867
+ dwDesiredAccess = SERVICE_ALL_ACCESS;
868
+ }
869
+ else{
870
+ dwDesiredAccess = NUM2INT(rb_iv_get(self, "@desired_access"));
871
+ }
872
+
873
+ if(NIL_P(rb_iv_get(self,"@service_type"))){
874
+ dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
875
+ }
876
+ else{
877
+ dwServiceType = NUM2INT(rb_iv_get(self, "@service_type"));
878
+ }
879
+
880
+ if(NIL_P(rb_iv_get(self,"@start_type"))){
881
+ dwStartType = SERVICE_DEMAND_START;
882
+ }
883
+ else{
884
+ dwStartType = NUM2INT(rb_iv_get(self, "@start_type"));
885
+ }
886
+
887
+ if(NIL_P(rb_iv_get(self, "@error_control"))){
888
+ dwErrorControl = SERVICE_ERROR_NORMAL;
889
+ }
890
+ else{
891
+ dwErrorControl = NUM2INT(rb_iv_get(self, "@error_control"));
892
+ }
893
+
894
+ // Add support for tag id and dependencies
895
+ hSCService = CreateService(
896
+ ptr->hSCManager,
897
+ lpServiceName,
898
+ lpDisplayName,
899
+ dwDesiredAccess,
900
+ dwServiceType,
901
+ dwStartType,
902
+ dwErrorControl,
903
+ lpBinaryPathName,
904
+ lpLoadOrderGroup,
905
+ NULL, // Tag ID
906
+ lpDependencies,
907
+ lpServiceStartName,
908
+ lpPassword
909
+ );
910
+
911
+ if(lpDependencies)
912
+ free(lpDependencies);
913
+
914
+ if(!hSCService)
915
+ rb_raise(cServiceError, ErrorDescription(GetLastError()));
916
+
917
+ // Set the description after the fact if specified, since we can't set it
918
+ // in CreateService().
919
+ if(!NIL_P(rb_iv_get(self, "@service_description"))){
920
+ SERVICE_DESCRIPTION servDesc;
921
+ VALUE v_desc = rb_iv_get(self, "@service_description");
922
+
923
+ servDesc.lpDescription = TEXT(StringValuePtr(v_desc));
924
+
925
+ if(!ChangeServiceConfig2(
926
+ hSCService,
927
+ SERVICE_CONFIG_DESCRIPTION,
928
+ &servDesc
929
+ )){
930
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
931
+ }
932
+ }
933
+
934
+ CloseServiceHandle(hSCService);
935
+ return self;
936
+ }
937
+
938
+ // CLASS METHODS
939
+
940
+ /*
941
+ * call-seq:
942
+ * Service.delete(name, host=localhost)
943
+ *
944
+ * Deletes the service +name+ from +host+, or the localhost if none is
945
+ * provided.
946
+ */
947
+ static VALUE service_delete(int argc, VALUE *argv, VALUE klass)
948
+ {
949
+ SC_HANDLE hSCManager, hSCService;
950
+ TCHAR* lpMachineName;
951
+ TCHAR* lpServiceName;
952
+ VALUE v_service_name, v_machine_name;
953
+
954
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
955
+
956
+ SafeStringValue(v_service_name);
957
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
958
+
959
+ if(NIL_P(v_machine_name)){
960
+ lpMachineName = NULL;
961
+ }
962
+ else{
963
+ SafeStringValue(v_machine_name);
964
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
965
+ }
966
+
967
+ hSCManager = OpenSCManager(
968
+ lpMachineName,
969
+ NULL,
970
+ SC_MANAGER_CREATE_SERVICE
971
+ );
972
+
973
+ if(!hSCManager)
974
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
975
+
976
+ hSCService = OpenService(
977
+ hSCManager,
978
+ lpServiceName,
979
+ DELETE
980
+ );
981
+
982
+ if(!hSCService){
983
+ strcpy(error,ErrorDescription(GetLastError()));
984
+ CloseServiceHandle(hSCManager);
985
+ rb_raise(cServiceError,error);
986
+ }
987
+
988
+ if(!DeleteService(hSCService)){
989
+ strcpy(error,ErrorDescription(GetLastError()));
990
+ CloseServiceHandle(hSCService);
991
+ CloseServiceHandle(hSCManager);
992
+ rb_raise(cServiceError,error);
993
+ }
994
+
995
+ CloseServiceHandle(hSCService);
996
+ CloseServiceHandle(hSCManager);
997
+
998
+ return klass;
999
+ }
1000
+
1001
+ /*
1002
+ * call-seq:
1003
+ * Service.services(host=nil, group=nil){ |struct| ... }
1004
+ *
1005
+ * Enumerates over a list of service types on host, or the local
1006
+ * machine if no host is specified, yielding a Win32Service struct for each
1007
+ * service.
1008
+ *
1009
+ * If a 'group' is specified, then only those services that belong to
1010
+ * that group are enumerated. If an empty string is provided, then only
1011
+ * services that do not belong to any group are enumerated. If this parameter
1012
+ * is nil, group membership is ignored and all services are enumerated.
1013
+ *
1014
+ * The 'group' option is only available on Windows 2000 or later, and only
1015
+ * if compiled with VC++ 7.0 or later, or the .NET SDK.
1016
+ *
1017
+ * The Win32 service struct contains the following members.
1018
+ *
1019
+ * * service_name
1020
+ * * display_name
1021
+ * * service_type
1022
+ * * current_state
1023
+ * * controls_accepted
1024
+ * * win32_exit_code
1025
+ * * service_specific_exit_code
1026
+ * * check_point
1027
+ * * wait_hint
1028
+ * * binary_path_name
1029
+ * * start_type
1030
+ * * error_control
1031
+ * * load_order_group
1032
+ * * tag_id
1033
+ * * start_name
1034
+ * * dependencies
1035
+ * * description
1036
+ * * interactive
1037
+ * * pid (Win2k or later)
1038
+ * * service_flags (Win2k or later)
1039
+ */
1040
+ static VALUE service_services(int argc, VALUE *argv, VALUE klass)
1041
+ {
1042
+ SC_HANDLE hSCManager = NULL;
1043
+ SC_HANDLE hSCService = NULL;
1044
+ DWORD dwBytesNeeded = 0;
1045
+ DWORD dwServicesReturned = 0;
1046
+ DWORD dwResumeHandle = 0;
1047
+ LPQUERY_SERVICE_CONFIG lpqscConf;
1048
+ LPSERVICE_DESCRIPTION lpqscDesc;
1049
+ TCHAR* lpMachineName;
1050
+ VALUE v_machine_name = Qnil;
1051
+ VALUE v_dependencies = Qnil;
1052
+ VALUE v_struct;
1053
+ VALUE v_array = Qnil;
1054
+ int rv = 0;
1055
+
1056
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
1057
+ TCHAR* pszGroupName;
1058
+ VALUE v_group = Qnil;
1059
+ ENUM_SERVICE_STATUS_PROCESS svcArray[MAX_SERVICES];
1060
+ rb_scan_args(argc, argv, "02", &v_machine_name, &v_group);
1061
+ #else
1062
+ ENUM_SERVICE_STATUS svcArray[MAX_SERVICES];
1063
+ rb_scan_args(argc, argv, "01", &v_machine_name);
1064
+ #endif
1065
+
1066
+ // If no block is provided, return an array of struct's.
1067
+ if(!rb_block_given_p())
1068
+ v_array = rb_ary_new();
1069
+
1070
+ if(NIL_P(v_machine_name)){
1071
+ lpMachineName = NULL;
1072
+ }
1073
+ else{
1074
+ SafeStringValue(v_machine_name);
1075
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1076
+ }
1077
+
1078
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
1079
+ if(NIL_P(v_group)){
1080
+ pszGroupName = NULL;
1081
+ }
1082
+ else{
1083
+ SafeStringValue(v_group);
1084
+ pszGroupName = TEXT(StringValuePtr(v_group));
1085
+ }
1086
+ #endif
1087
+
1088
+ hSCManager = OpenSCManager(
1089
+ lpMachineName,
1090
+ NULL,
1091
+ SC_MANAGER_ENUMERATE_SERVICE
1092
+ );
1093
+
1094
+ if(NULL == hSCManager){
1095
+ sprintf(error, "OpenSCManager() call failed: %s",
1096
+ ErrorDescription(GetLastError()));
1097
+ rb_raise(cServiceError,error);
1098
+ }
1099
+
1100
+ lpqscConf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, MAX_BUF_SIZE);
1101
+ lpqscDesc = (LPSERVICE_DESCRIPTION) LocalAlloc(LPTR, MAX_BUF_SIZE);
1102
+
1103
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
1104
+ rv = EnumServicesStatusEx(
1105
+ hSCManager, // SC Manager
1106
+ SC_ENUM_PROCESS_INFO, // Info level (only possible value)
1107
+ SERVICE_WIN32 | SERVICE_DRIVER, // Service type
1108
+ SERVICE_STATE_ALL, // Service state
1109
+ (LPBYTE)svcArray, // Array of structs
1110
+ sizeof(svcArray),
1111
+ &dwBytesNeeded,
1112
+ &dwServicesReturned,
1113
+ &dwResumeHandle,
1114
+ pszGroupName
1115
+ );
1116
+ #else
1117
+ rv = EnumServicesStatus(
1118
+ hSCManager, // SC Manager
1119
+ SERVICE_WIN32 | SERVICE_DRIVER, // Service type
1120
+ SERVICE_STATE_ALL, // Service state
1121
+ svcArray, // Array of structs
1122
+ sizeof(svcArray),
1123
+ &dwBytesNeeded,
1124
+ &dwServicesReturned,
1125
+ &dwResumeHandle
1126
+ );
1127
+ #endif
1128
+
1129
+ if(rv != 0)
1130
+ {
1131
+ unsigned i;
1132
+ int rv;
1133
+ VALUE v_service_type, v_current_state, v_controls_accepted;
1134
+ VALUE v_binary_path_name, v_error_control, v_load_order_group;
1135
+ VALUE v_start_type, v_service_start_name, v_description, v_interactive;
1136
+
1137
+ for(i = 0; i < dwServicesReturned; i++){
1138
+ DWORD dwBytesNeeded;
1139
+ v_controls_accepted = rb_ary_new();
1140
+ v_interactive = Qfalse;
1141
+
1142
+ hSCService = OpenService(
1143
+ hSCManager,
1144
+ svcArray[i].lpServiceName,
1145
+ SERVICE_QUERY_CONFIG
1146
+ );
1147
+
1148
+ if(!hSCService){
1149
+ sprintf(error, "OpenService() call failed: %s",
1150
+ ErrorDescription(GetLastError()));
1151
+ CloseServiceHandle(hSCManager);
1152
+ rb_raise(cServiceError, error);
1153
+ }
1154
+
1155
+ // Retrieve a QUERY_SERVICE_CONFIG structure for the Service, from
1156
+ // which we can gather the service type, start type, etc.
1157
+ rv = QueryServiceConfig(
1158
+ hSCService,
1159
+ lpqscConf,
1160
+ MAX_BUF_SIZE,
1161
+ &dwBytesNeeded
1162
+ );
1163
+
1164
+ if(0 == rv){
1165
+ sprintf(error, "QueryServiceConfig() call failed: %s",
1166
+ ErrorDescription(GetLastError()));
1167
+ CloseServiceHandle(hSCManager);
1168
+ rb_raise(cServiceError, error);
1169
+ }
1170
+
1171
+ // Get the description for the Service
1172
+ rv = QueryServiceConfig2(
1173
+ hSCService,
1174
+ SERVICE_CONFIG_DESCRIPTION,
1175
+ (LPBYTE)lpqscDesc,
1176
+ MAX_BUF_SIZE,
1177
+ &dwBytesNeeded
1178
+ );
1179
+
1180
+ if(0 == rv){
1181
+ sprintf(error,"QueryServiceConfig2() call failed: %s",
1182
+ ErrorDescription(GetLastError()));
1183
+ CloseServiceHandle(hSCManager);
1184
+ rb_raise(cServiceError, error);
1185
+ }
1186
+
1187
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
1188
+ if(svcArray[i].ServiceStatusProcess.dwServiceType
1189
+ & SERVICE_INTERACTIVE_PROCESS){
1190
+ v_interactive = Qtrue;
1191
+ }
1192
+ #else
1193
+ if(svcArray[i].ServiceStatus.dwServiceType
1194
+ & SERVICE_INTERACTIVE_PROCESS){
1195
+ v_interactive = Qtrue;
1196
+ }
1197
+ #endif
1198
+
1199
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
1200
+ v_service_type =
1201
+ rb_get_service_type(svcArray[i].ServiceStatusProcess.dwServiceType);
1202
+
1203
+ v_current_state =
1204
+ rb_get_current_state(
1205
+ svcArray[i].ServiceStatusProcess.dwCurrentState);
1206
+
1207
+ v_controls_accepted =
1208
+ rb_get_controls_accepted(
1209
+ svcArray[i].ServiceStatusProcess.dwControlsAccepted);
1210
+ #else
1211
+ v_service_type =
1212
+ rb_get_service_type(svcArray[i].ServiceStatus.dwServiceType);
1213
+
1214
+ v_current_state =
1215
+ rb_get_current_state(svcArray[i].ServiceStatus.dwCurrentState);
1216
+
1217
+ v_controls_accepted =
1218
+ rb_get_controls_accepted(
1219
+ svcArray[i].ServiceStatus.dwControlsAccepted);
1220
+ #endif
1221
+
1222
+ if(_tcslen(lpqscConf->lpBinaryPathName) > 0)
1223
+ v_binary_path_name = rb_str_new2(lpqscConf->lpBinaryPathName);
1224
+ else
1225
+ v_binary_path_name = Qnil;
1226
+
1227
+ if(_tcslen(lpqscConf->lpLoadOrderGroup) > 0)
1228
+ v_load_order_group = rb_str_new2(lpqscConf->lpLoadOrderGroup);
1229
+ else
1230
+ v_load_order_group = Qnil;
1231
+
1232
+ if(_tcslen(lpqscConf->lpServiceStartName) > 0)
1233
+ v_service_start_name = rb_str_new2(lpqscConf->lpServiceStartName);
1234
+ else
1235
+ v_service_start_name = Qnil;
1236
+
1237
+ if(lpqscDesc->lpDescription != NULL)
1238
+ v_description = rb_str_new2(lpqscDesc->lpDescription);
1239
+ else
1240
+ v_description = Qnil;
1241
+
1242
+ v_start_type = rb_get_start_type(lpqscConf->dwStartType);
1243
+ v_error_control = rb_get_error_control(lpqscConf->dwErrorControl);
1244
+ v_dependencies = rb_get_dependencies(lpqscConf->lpDependencies);
1245
+
1246
+ CloseServiceHandle(hSCService);
1247
+
1248
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
1249
+ v_struct = rb_struct_new(v_service_struct,
1250
+ rb_str_new2(svcArray[i].lpServiceName),
1251
+ rb_str_new2(svcArray[i].lpDisplayName),
1252
+ v_service_type,
1253
+ v_current_state,
1254
+ v_controls_accepted,
1255
+ INT2FIX(svcArray[i].ServiceStatusProcess.dwWin32ExitCode),
1256
+ INT2FIX(svcArray[i].ServiceStatusProcess.dwServiceSpecificExitCode),
1257
+ INT2FIX(svcArray[i].ServiceStatusProcess.dwCheckPoint),
1258
+ INT2FIX(svcArray[i].ServiceStatusProcess.dwWaitHint),
1259
+ v_binary_path_name,
1260
+ v_start_type,
1261
+ v_error_control,
1262
+ v_load_order_group,
1263
+ INT2FIX(lpqscConf->dwTagId),
1264
+ v_service_start_name,
1265
+ v_dependencies,
1266
+ v_description,
1267
+ v_interactive,
1268
+ INT2FIX(svcArray[i].ServiceStatusProcess.dwProcessId),
1269
+ INT2FIX(svcArray[i].ServiceStatusProcess.dwServiceFlags)
1270
+ );
1271
+ #else
1272
+ v_struct = rb_struct_new(v_service_struct,
1273
+ rb_str_new2(svcArray[i].lpServiceName),
1274
+ rb_str_new2(svcArray[i].lpDisplayName),
1275
+ v_service_type,
1276
+ v_current_state,
1277
+ v_controls_accepted,
1278
+ INT2FIX(svcArray[i].ServiceStatus.dwWin32ExitCode),
1279
+ INT2FIX(svcArray[i].ServiceStatus.dwServiceSpecificExitCode),
1280
+ INT2FIX(svcArray[i].ServiceStatus.dwCheckPoint),
1281
+ INT2FIX(svcArray[i].ServiceStatus.dwWaitHint),
1282
+ v_binary_path_name,
1283
+ v_start_type,
1284
+ v_error_control,
1285
+ v_load_order_group,
1286
+ INT2FIX(lpqscConf->dwTagId),
1287
+ v_service_start_name,
1288
+ v_dependencies,
1289
+ v_description,
1290
+ v_interactive
1291
+ );
1292
+ #endif
1293
+ if(rb_block_given_p()){
1294
+ rb_yield(v_struct);
1295
+ }
1296
+ else{
1297
+ rb_ary_push(v_array, v_struct);
1298
+ }
1299
+ }
1300
+ }
1301
+ else{
1302
+ sprintf(error,"EnumServiceStatus() call failed: %s",
1303
+ ErrorDescription(GetLastError()));
1304
+ LocalFree(lpqscConf);
1305
+ LocalFree(lpqscDesc);
1306
+ CloseServiceHandle(hSCManager);
1307
+ rb_raise(cServiceError,error);
1308
+ }
1309
+
1310
+ LocalFree(lpqscConf);
1311
+ LocalFree(lpqscDesc);
1312
+ CloseServiceHandle(hSCManager);
1313
+ return v_array; // Nil if a block was given
1314
+ }
1315
+
1316
+ /*
1317
+ * call-seq:
1318
+ * Service.stop(name, host=localhost)
1319
+ *
1320
+ * Stop a service. Attempting to stop an already stopped service raises
1321
+ * a ServiceError.
1322
+ */
1323
+ static VALUE service_stop(int argc, VALUE *argv, VALUE klass)
1324
+ {
1325
+ SC_HANDLE hSCManager, hSCService;
1326
+ TCHAR* lpMachineName;
1327
+ TCHAR* lpServiceName;
1328
+ SERVICE_STATUS serviceStatus;
1329
+ VALUE v_service_name, v_machine_name;
1330
+ int rv;
1331
+
1332
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
1333
+
1334
+ SafeStringValue(v_service_name);
1335
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1336
+
1337
+ if(NIL_P(v_machine_name)){
1338
+ lpMachineName = NULL;
1339
+ }
1340
+ else{
1341
+ SafeStringValue(v_machine_name);
1342
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1343
+ }
1344
+
1345
+ hSCManager = OpenSCManager(
1346
+ lpMachineName,
1347
+ NULL,
1348
+ SC_MANAGER_CONNECT
1349
+ );
1350
+
1351
+ if(!hSCManager)
1352
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
1353
+
1354
+ hSCService = OpenService(
1355
+ hSCManager,
1356
+ lpServiceName,
1357
+ SERVICE_STOP
1358
+ );
1359
+
1360
+ if(!hSCService){
1361
+ strcpy(error,ErrorDescription(GetLastError()));
1362
+ CloseServiceHandle(hSCManager);
1363
+ rb_raise(cServiceError,error);
1364
+ }
1365
+
1366
+ rv = ControlService(
1367
+ hSCService,
1368
+ SERVICE_CONTROL_STOP,
1369
+ &serviceStatus
1370
+ );
1371
+
1372
+ if(0 == rv){
1373
+ strcpy(error,ErrorDescription(GetLastError()));
1374
+ CloseServiceHandle(hSCService);
1375
+ CloseServiceHandle(hSCManager);
1376
+ rb_raise(cServiceError,error);
1377
+ }
1378
+
1379
+ CloseServiceHandle(hSCService);
1380
+ CloseServiceHandle(hSCManager);
1381
+
1382
+ return klass;
1383
+ }
1384
+
1385
+ /*
1386
+ * call-seq:
1387
+ * Service.pause(name, host=localhost)
1388
+ *
1389
+ * Pause a service. Attempting to pause an already paused service will raise
1390
+ * a ServiceError.
1391
+ *
1392
+ * Note that not all services are configured to accept a pause (or resume)
1393
+ * command.
1394
+ */
1395
+ static VALUE service_pause(int argc, VALUE *argv, VALUE klass)
1396
+ {
1397
+ SC_HANDLE hSCManager, hSCService;
1398
+ TCHAR* lpMachineName;
1399
+ TCHAR* lpServiceName;
1400
+ SERVICE_STATUS serviceStatus;
1401
+ VALUE v_service_name, v_machine_name;
1402
+ int rv;
1403
+
1404
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
1405
+
1406
+ SafeStringValue(v_service_name);
1407
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1408
+
1409
+ if(NIL_P(v_machine_name)){
1410
+ lpMachineName = NULL;
1411
+ }
1412
+ else{
1413
+ SafeStringValue(v_machine_name);
1414
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1415
+ }
1416
+
1417
+ hSCManager = OpenSCManager(
1418
+ lpMachineName,
1419
+ NULL,
1420
+ SC_MANAGER_CONNECT
1421
+ );
1422
+
1423
+ if(!hSCManager)
1424
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
1425
+
1426
+ hSCService = OpenService(
1427
+ hSCManager,
1428
+ lpServiceName,
1429
+ SERVICE_PAUSE_CONTINUE
1430
+ );
1431
+
1432
+ if(!hSCService){
1433
+ strcpy(error,ErrorDescription(GetLastError()));
1434
+ CloseServiceHandle(hSCManager);
1435
+ rb_raise(cServiceError,error);
1436
+ }
1437
+
1438
+ rv = ControlService(
1439
+ hSCService,
1440
+ SERVICE_CONTROL_PAUSE,
1441
+ &serviceStatus
1442
+ );
1443
+
1444
+ if(0 == rv){
1445
+ strcpy(error,ErrorDescription(GetLastError()));
1446
+ CloseServiceHandle(hSCService);
1447
+ CloseServiceHandle(hSCManager);
1448
+ rb_raise(cServiceError,error);
1449
+ }
1450
+
1451
+ CloseServiceHandle(hSCService);
1452
+ CloseServiceHandle(hSCManager);
1453
+
1454
+ return klass;
1455
+ }
1456
+
1457
+ /*
1458
+ * call-seq:
1459
+ * Service.resume(name, host=localhost)
1460
+ *
1461
+ * Resume a service. Attempting to resume a service that isn't paused will
1462
+ * raise a ServiceError.
1463
+ *
1464
+ * Note that not all services are configured to accept a resume (or pause)
1465
+ * command. In that case, a ServiceError will be raised.
1466
+ */
1467
+ static VALUE service_resume(int argc, VALUE *argv, VALUE klass)
1468
+ {
1469
+ SC_HANDLE hSCManager, hSCService;
1470
+ TCHAR* lpMachineName;
1471
+ TCHAR* lpServiceName;
1472
+ SERVICE_STATUS serviceStatus;
1473
+ VALUE v_service_name, v_machine_name;
1474
+ int rv;
1475
+
1476
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
1477
+
1478
+ SafeStringValue(v_service_name);
1479
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1480
+
1481
+ if(NIL_P(v_machine_name)){
1482
+ lpMachineName = NULL;
1483
+ }
1484
+ else{
1485
+ SafeStringValue(v_machine_name);
1486
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1487
+ }
1488
+
1489
+ hSCManager = OpenSCManager(
1490
+ lpMachineName,
1491
+ NULL,
1492
+ SC_MANAGER_CONNECT
1493
+ );
1494
+
1495
+ if(!hSCManager){
1496
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
1497
+ }
1498
+
1499
+ hSCService = OpenService(
1500
+ hSCManager,
1501
+ lpServiceName,
1502
+ SERVICE_PAUSE_CONTINUE
1503
+ );
1504
+
1505
+ if(!hSCService){
1506
+ strcpy(error,ErrorDescription(GetLastError()));
1507
+ CloseServiceHandle(hSCManager);
1508
+ rb_raise(cServiceError,error);
1509
+ }
1510
+
1511
+ rv = ControlService(
1512
+ hSCService,
1513
+ SERVICE_CONTROL_CONTINUE,
1514
+ &serviceStatus
1515
+ );
1516
+
1517
+ if(0 == rv){
1518
+ strcpy(error,ErrorDescription(GetLastError()));
1519
+ CloseServiceHandle(hSCService);
1520
+ CloseServiceHandle(hSCManager);
1521
+ rb_raise(cServiceError,error);
1522
+ }
1523
+
1524
+ CloseServiceHandle(hSCService);
1525
+ CloseServiceHandle(hSCManager);
1526
+
1527
+ return klass;
1528
+ }
1529
+
1530
+ /*
1531
+ * call-seq:
1532
+ * Service.start(name, host=localhost, args=nil)
1533
+ *
1534
+ * Attempts to start service +name+ on +host+, or the local machine if no
1535
+ * host is provided. If +args+ are provided, they are passed to the service's
1536
+ * Service_Main() function.
1537
+ *
1538
+ *-- Note that the WMI interface does not allow you to pass arguments to the
1539
+ *-- Service_Main function.
1540
+ */
1541
+ static VALUE service_start(int argc, VALUE *argv, VALUE klass){
1542
+ SC_HANDLE hSCManager, hSCService;
1543
+ TCHAR* lpMachineName;
1544
+ TCHAR* lpServiceName;
1545
+ TCHAR** lpServiceArgVectors;
1546
+ VALUE v_service_name, v_machine_name, rbArgs;
1547
+ int rv;
1548
+
1549
+ rb_scan_args(argc, argv, "11*", &v_service_name, &v_machine_name, &rbArgs);
1550
+
1551
+ SafeStringValue(v_service_name);
1552
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1553
+
1554
+ if(NIL_P(v_machine_name)){
1555
+ lpMachineName = NULL;
1556
+ }
1557
+ else{
1558
+ SafeStringValue(v_machine_name);
1559
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1560
+ }
1561
+
1562
+ if( (NIL_P(rbArgs)) || (RARRAY(rbArgs)->len == 0) ){
1563
+ lpServiceArgVectors = NULL;
1564
+ }
1565
+ else{
1566
+ int i;
1567
+ lpServiceArgVectors =
1568
+ malloc(RARRAY(rbArgs)->len * sizeof(*lpServiceArgVectors));
1569
+
1570
+ for(i = 0; i < RARRAY(rbArgs)->len; i++){
1571
+ VALUE v_tmp = rb_ary_entry(rbArgs, i);
1572
+ TCHAR* string = TEXT(StringValuePtr(v_tmp));
1573
+ lpServiceArgVectors[i] = malloc(*string);
1574
+ lpServiceArgVectors[i] = string;
1575
+ }
1576
+ }
1577
+
1578
+ hSCManager = OpenSCManager(
1579
+ lpMachineName,
1580
+ NULL,
1581
+ SC_MANAGER_CONNECT
1582
+ );
1583
+
1584
+ if(!hSCManager)
1585
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
1586
+
1587
+ hSCService = OpenService(
1588
+ hSCManager,
1589
+ lpServiceName,
1590
+ SERVICE_START
1591
+ );
1592
+
1593
+ if(!hSCService){
1594
+ strcpy(error,ErrorDescription(GetLastError()));
1595
+ CloseServiceHandle(hSCManager);
1596
+ rb_raise(cServiceError,error);
1597
+ }
1598
+
1599
+ rv = StartService(
1600
+ hSCService,
1601
+ 0,
1602
+ lpServiceArgVectors
1603
+ );
1604
+
1605
+ if(0 == rv){
1606
+ strcpy(error,ErrorDescription(GetLastError()));
1607
+ CloseServiceHandle(hSCManager);
1608
+ CloseServiceHandle(hSCService);
1609
+ if(lpServiceArgVectors){
1610
+ free(lpServiceArgVectors);
1611
+ }
1612
+ rb_raise(cServiceError,error);
1613
+ }
1614
+
1615
+ CloseServiceHandle(hSCManager);
1616
+ CloseServiceHandle(hSCService);
1617
+
1618
+ if(lpServiceArgVectors)
1619
+ free(lpServiceArgVectors);
1620
+
1621
+ return klass;
1622
+ }
1623
+
1624
+ /*
1625
+ * call-seq:
1626
+ * Service.getservicename(display_name, host=localhost)
1627
+ *
1628
+ * Returns the service name for the corresponding +display_name+ on +host+, or
1629
+ * the local machine if no host is specified.
1630
+ */
1631
+ static VALUE service_get_service_name(int argc, VALUE *argv, VALUE klass)
1632
+ {
1633
+ SC_HANDLE hSCManager;
1634
+ TCHAR* lpMachineName;
1635
+ TCHAR* lpDisplayName;
1636
+ TCHAR szRegKey[MAX_PATH];
1637
+ DWORD dwKeySize = sizeof(szRegKey);
1638
+ VALUE v_machine_name, rbDisplayName;
1639
+ int rv;
1640
+
1641
+ rb_scan_args(argc, argv, "11", &rbDisplayName, &v_machine_name);
1642
+
1643
+ SafeStringValue(rbDisplayName);
1644
+ lpDisplayName = TEXT(StringValuePtr(rbDisplayName));
1645
+
1646
+ if(NIL_P(v_machine_name)){
1647
+ lpMachineName = NULL;
1648
+ }
1649
+ else{
1650
+ SafeStringValue(v_machine_name);
1651
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1652
+ }
1653
+
1654
+ hSCManager = OpenSCManager(
1655
+ lpMachineName,
1656
+ NULL,
1657
+ SC_MANAGER_CONNECT
1658
+ );
1659
+
1660
+ if(!hSCManager)
1661
+ rb_raise(rb_eArgError,ErrorDescription(GetLastError()));
1662
+
1663
+ rv = GetServiceKeyName(
1664
+ hSCManager,
1665
+ lpDisplayName,
1666
+ szRegKey,
1667
+ &dwKeySize
1668
+ );
1669
+
1670
+ if(0 == rv){
1671
+ strcpy(error,ErrorDescription(GetLastError()));
1672
+ CloseServiceHandle(hSCManager);
1673
+ rb_raise(rb_eArgError,error);
1674
+ }
1675
+
1676
+ CloseServiceHandle(hSCManager);
1677
+
1678
+ return rb_str_new2(szRegKey);
1679
+ }
1680
+
1681
+ /*
1682
+ * call-seq:
1683
+ * Service.getdisplayname(service_name, host=localhost)
1684
+ *
1685
+ * Returns the display name for the service +service_name+ on +host+, or the
1686
+ * localhost if no host is specified.
1687
+ */
1688
+ static VALUE service_get_display_name(int argc, VALUE *argv, VALUE klass)
1689
+ {
1690
+ SC_HANDLE hSCManager;
1691
+ TCHAR* lpMachineName;
1692
+ TCHAR* lpServiceName;
1693
+ TCHAR szRegKey[MAX_PATH];
1694
+ DWORD dwKeySize = sizeof(szRegKey);
1695
+ VALUE v_machine_name, v_service_name;
1696
+ int rv;
1697
+
1698
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
1699
+
1700
+ SafeStringValue(v_service_name);
1701
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1702
+
1703
+ if(NIL_P(v_machine_name)){
1704
+ lpMachineName = NULL;
1705
+ }
1706
+ else{
1707
+ SafeStringValue(v_machine_name);
1708
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1709
+ }
1710
+
1711
+ hSCManager = OpenSCManager(
1712
+ lpMachineName,
1713
+ NULL,
1714
+ SC_MANAGER_CONNECT
1715
+ );
1716
+
1717
+ if(!hSCManager)
1718
+ rb_raise(rb_eArgError,ErrorDescription(GetLastError()));
1719
+
1720
+ rv = GetServiceDisplayName(
1721
+ hSCManager,
1722
+ lpServiceName,
1723
+ szRegKey,
1724
+ &dwKeySize
1725
+ );
1726
+
1727
+ if(0 == rv){
1728
+ strcpy(error,ErrorDescription(GetLastError()));
1729
+ CloseServiceHandle(hSCManager);
1730
+ rb_raise(rb_eArgError,error);
1731
+ }
1732
+
1733
+ CloseServiceHandle(hSCManager);
1734
+
1735
+ return rb_str_new2(szRegKey);
1736
+ }
1737
+
1738
+ /*
1739
+ * Sets the dependencies for the given service. Use this when you call
1740
+ * Service.create_service, if desired.
1741
+ */
1742
+ static VALUE service_set_dependencies(VALUE self, VALUE array)
1743
+ {
1744
+ Check_Type(array, T_ARRAY);
1745
+ rb_iv_set(self, "@dependencies", array);
1746
+ return self;
1747
+ }
1748
+
1749
+ /*
1750
+ * Returns an array of dependencies for the given service, or nil if there
1751
+ * aren't any dependencies.
1752
+ */
1753
+ static VALUE service_get_dependencies(VALUE self){
1754
+ return rb_iv_get(self, "@dependencies");
1755
+ }
1756
+
1757
+ /*
1758
+ * call-seq:
1759
+ * Service.status(name, host=localhost)
1760
+ *
1761
+ * Returns a ServiceStatus struct indicating the status of service +name+ on
1762
+ * +host+, or the localhost if none is provided.
1763
+ *
1764
+ * The ServiceStatus struct contains the following members:
1765
+ *
1766
+ * * service_type
1767
+ * * current_state
1768
+ * * controls_accepted
1769
+ * * win32_exit_code
1770
+ * * service_specific_exit_code
1771
+ * * check_point
1772
+ * * wait_hint
1773
+ * * pid (Win2k or later)
1774
+ * * service_flags (Win2k or later)
1775
+ */
1776
+ static VALUE service_status(int argc, VALUE *argv, VALUE klass){
1777
+ SC_HANDLE hSCManager, hSCService;
1778
+ VALUE v_service_name, v_machine_name;
1779
+ VALUE v_service_type, v_current_state, v_controls_accepted;
1780
+ VALUE v_interactive = Qfalse;
1781
+ TCHAR* lpMachineName;
1782
+ TCHAR* lpServiceName;
1783
+ DWORD dwBytesNeeded;
1784
+ int rv;
1785
+
1786
+ #ifdef HAVE_QUERYSERVICESTATUSEX
1787
+ SERVICE_STATUS_PROCESS ssProcess;
1788
+ #else
1789
+ SERVICE_STATUS ssProcess;
1790
+ #endif
1791
+
1792
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
1793
+
1794
+ SafeStringValue(v_service_name);
1795
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1796
+
1797
+ if(NIL_P(v_machine_name)){
1798
+ lpMachineName = NULL;
1799
+ }
1800
+ else{
1801
+ SafeStringValue(v_machine_name);
1802
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1803
+ }
1804
+
1805
+ hSCManager = OpenSCManager(
1806
+ lpMachineName,
1807
+ NULL,
1808
+ SC_MANAGER_ENUMERATE_SERVICE
1809
+ );
1810
+
1811
+ if(!hSCManager)
1812
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
1813
+
1814
+ hSCService = OpenService(
1815
+ hSCManager,
1816
+ lpServiceName,
1817
+ SERVICE_QUERY_STATUS
1818
+ );
1819
+
1820
+ if(!hSCService){
1821
+ strcpy(error,ErrorDescription(GetLastError()));
1822
+ CloseServiceHandle(hSCManager);
1823
+ rb_raise(cServiceError,error);
1824
+ }
1825
+
1826
+ #ifdef HAVE_QUERYSERVICESTATUSEX
1827
+ rv = QueryServiceStatusEx(
1828
+ hSCService,
1829
+ SC_STATUS_PROCESS_INFO,
1830
+ (LPBYTE)&ssProcess,
1831
+ sizeof(SERVICE_STATUS_PROCESS),
1832
+ &dwBytesNeeded
1833
+ );
1834
+ #else
1835
+ rv = QueryServiceStatus(
1836
+ hSCService,
1837
+ &ssProcess
1838
+ );
1839
+ #endif
1840
+
1841
+ v_service_type = rb_get_service_type(ssProcess.dwServiceType);
1842
+ v_current_state = rb_get_current_state(ssProcess.dwCurrentState);
1843
+ v_controls_accepted = rb_get_controls_accepted(ssProcess.dwControlsAccepted);
1844
+
1845
+ if(ssProcess.dwServiceType & SERVICE_INTERACTIVE_PROCESS){
1846
+ v_interactive = Qtrue;
1847
+ }
1848
+
1849
+ CloseServiceHandle(hSCService);
1850
+ CloseServiceHandle(hSCManager);
1851
+
1852
+ return rb_struct_new(v_service_status_struct,
1853
+ v_service_type,
1854
+ v_current_state,
1855
+ v_controls_accepted,
1856
+ INT2FIX(ssProcess.dwWin32ExitCode),
1857
+ INT2FIX(ssProcess.dwServiceSpecificExitCode),
1858
+ INT2FIX(ssProcess.dwCheckPoint),
1859
+ INT2FIX(ssProcess.dwWaitHint),
1860
+ v_interactive
1861
+ #ifdef HAVE_QUERYSERVICESTATUSEX
1862
+ ,INT2FIX(ssProcess.dwProcessId)
1863
+ ,INT2FIX(ssProcess.dwServiceFlags)
1864
+ #endif
1865
+ );
1866
+ }
1867
+
1868
+ /* call-seq:
1869
+ * Service.exists?(name, host=localhost)
1870
+ *
1871
+ * Returns whether or not the service +name+ exists on +host+, or the localhost
1872
+ * if none is provided.
1873
+ */
1874
+ static VALUE service_exists(int argc, VALUE *argv, VALUE klass){
1875
+ SC_HANDLE hSCManager, hSCService;
1876
+ TCHAR* lpMachineName;
1877
+ TCHAR* lpServiceName;
1878
+ VALUE v_service_name, v_machine_name;
1879
+ VALUE rbExists = Qtrue;
1880
+
1881
+ rb_scan_args(argc, argv, "11", &v_service_name, &v_machine_name);
1882
+
1883
+ SafeStringValue(v_service_name);
1884
+ lpServiceName = TEXT(StringValuePtr(v_service_name));
1885
+
1886
+ if(NIL_P(v_machine_name)){
1887
+ lpMachineName = NULL;
1888
+ }
1889
+ else{
1890
+ SafeStringValue(v_machine_name);
1891
+ lpMachineName = TEXT(StringValuePtr(v_machine_name));
1892
+ }
1893
+
1894
+ hSCManager = OpenSCManager(
1895
+ lpMachineName,
1896
+ NULL,
1897
+ SC_MANAGER_ENUMERATE_SERVICE
1898
+ );
1899
+
1900
+ if(!hSCManager)
1901
+ rb_raise(cServiceError,ErrorDescription(GetLastError()));
1902
+
1903
+ hSCService = OpenService(
1904
+ hSCManager,
1905
+ lpServiceName,
1906
+ SERVICE_QUERY_STATUS
1907
+ );
1908
+
1909
+ if(!hSCService)
1910
+ rbExists = Qfalse;
1911
+
1912
+ CloseServiceHandle(hSCService);
1913
+ CloseServiceHandle(hSCManager);
1914
+
1915
+ return rbExists;
1916
+ }
1917
+
1918
+ /*
1919
+ * call-seq:
1920
+ * Service.open(service_name, host=nil, desired_access=nil)
1921
+ * Service.open(service_name, host=nil, desired_access=nil){ |svc| ... }
1922
+ *
1923
+ * Opens and returns a new Service object based on +service_name+ from +host+
1924
+ * or the local machine if no host is specified. If a block is provided, the
1925
+ * object is automatically closed at the end of the block.
1926
+ *
1927
+ * Note that the default desired access for the returned object is
1928
+ * Service::SERVICE_QUERY_CONFIG. You will probably need to change that in
1929
+ * order to configure or delete an existing service using the returned object.
1930
+ */
1931
+ static VALUE service_open(int argc, VALUE* argv, VALUE klass){
1932
+ VALUE self, v_service_name, v_host, v_desired_access;
1933
+ SvcStruct* ptr;
1934
+ SC_HANDLE hSCService;
1935
+ TCHAR* lpMachineName;
1936
+ LPQUERY_SERVICE_CONFIG lpConf;
1937
+ LPSERVICE_DESCRIPTION lpDesc;
1938
+ DWORD dwDesiredAccess = SERVICE_QUERY_CONFIG;
1939
+ DWORD dwBytesNeeded, rv;
1940
+
1941
+ rb_scan_args(argc, argv, "12", &v_service_name, &v_host, &v_desired_access);
1942
+
1943
+ self = Data_Make_Struct(klass, SvcStruct, 0, service_free, ptr);
1944
+
1945
+ /* Set the host name, or use the localhost if no host is specified */
1946
+ if(NIL_P(v_host)){
1947
+ TCHAR name[MAX_PATH];
1948
+ lpMachineName = NULL;
1949
+
1950
+ if(gethostname(name, MAX_PATH))
1951
+ rb_raise(cServiceError, "gethostname() failed");
1952
+
1953
+ v_host = rb_str_new2(name);
1954
+ }
1955
+ else{
1956
+ lpMachineName = TEXT(StringValuePtr(v_host));
1957
+ }
1958
+
1959
+ /* Set the desired access level. Default to SERVICE_QUERY_CONFIG */
1960
+ if(NIL_P(v_desired_access))
1961
+ v_desired_access = INT2FIX(SERVICE_QUERY_CONFIG);
1962
+ else
1963
+ dwDesiredAccess = NUM2INT(v_desired_access);
1964
+
1965
+ ptr->hSCManager = OpenSCManager(
1966
+ lpMachineName,
1967
+ NULL,
1968
+ dwDesiredAccess
1969
+ );
1970
+
1971
+ if(!ptr->hSCManager)
1972
+ rb_raise(cServiceError, ErrorDescription(GetLastError()));
1973
+
1974
+ lpConf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, MAX_BUF_SIZE);
1975
+ lpDesc = (LPSERVICE_DESCRIPTION) LocalAlloc(LPTR, MAX_BUF_SIZE);
1976
+
1977
+ hSCService = OpenService(
1978
+ ptr->hSCManager,
1979
+ TEXT(StringValuePtr(v_service_name)),
1980
+ SERVICE_QUERY_CONFIG
1981
+ );
1982
+
1983
+ if(!hSCService)
1984
+ rb_raise(cServiceError, ErrorDescription(GetLastError()));
1985
+
1986
+ rv = QueryServiceConfig(
1987
+ hSCService,
1988
+ lpConf,
1989
+ MAX_BUF_SIZE,
1990
+ &dwBytesNeeded
1991
+ );
1992
+
1993
+ if(0 == rv){
1994
+ sprintf(error, "QueryServiceConfig() call failed: %s",
1995
+ ErrorDescription(GetLastError()));
1996
+ CloseServiceHandle(ptr->hSCManager);
1997
+ rb_raise(cServiceError, error);
1998
+ }
1999
+
2000
+ rv = QueryServiceConfig2(
2001
+ hSCService,
2002
+ SERVICE_CONFIG_DESCRIPTION,
2003
+ (LPBYTE)lpDesc,
2004
+ MAX_BUF_SIZE,
2005
+ &dwBytesNeeded
2006
+ );
2007
+
2008
+ if(0 == rv){
2009
+ sprintf(error, "QueryServiceConfig2() call failed: %s",
2010
+ ErrorDescription(GetLastError()));
2011
+ CloseServiceHandle(ptr->hSCManager);
2012
+ rb_raise(cServiceError,error);
2013
+ }
2014
+
2015
+ CloseServiceHandle(hSCService);
2016
+
2017
+ /* Designer's note: the original plan to convert integer constants was
2018
+ * abandoned because methods like Service#configure expect a number.
2019
+ */
2020
+ rb_iv_set(self, "@machine_name", v_host);
2021
+ rb_iv_set(self, "@desired_access", v_desired_access);
2022
+ rb_iv_set(self, "@service_name", v_service_name);
2023
+ rb_iv_set(self, "@display_name", rb_str_new2(lpConf->lpDisplayName));
2024
+ rb_iv_set(self, "@service_type", INT2FIX(lpConf->dwServiceType));
2025
+ rb_iv_set(self, "@start_type", INT2FIX(lpConf->dwStartType));
2026
+ rb_iv_set(self, "@binary_path_name", rb_str_new2(lpConf->lpBinaryPathName));
2027
+ rb_iv_set(self, "@tag_id", INT2FIX(lpConf->dwTagId));
2028
+ rb_iv_set(self, "@start_name", rb_str_new2(lpConf->lpServiceStartName));
2029
+ rb_iv_set(self, "@service_description", rb_str_new2(lpDesc->lpDescription));
2030
+ rb_iv_set(self, "@error_control", INT2FIX(lpConf->dwErrorControl));
2031
+
2032
+ if(lpConf->lpLoadOrderGroup){
2033
+ rb_iv_set(self, "@load_order_group",
2034
+ rb_str_new2(lpConf->lpLoadOrderGroup));
2035
+ }
2036
+
2037
+ rb_iv_set(self, "@dependencies",
2038
+ rb_get_dependencies(lpConf->lpDependencies));
2039
+
2040
+ if(rb_block_given_p()){
2041
+ rb_ensure(rb_yield, self, service_close, self);
2042
+ return Qnil;
2043
+ }
2044
+ else{
2045
+ return self;
2046
+ }
2047
+ }
2048
+
2049
+ void Init_service()
2050
+ {
2051
+ VALUE mWin32, cService, cDaemon;
2052
+ int i = 0;
2053
+
2054
+ // Modules and classes
2055
+ mWin32 = rb_define_module("Win32");
2056
+ cService = rb_define_class_under(mWin32, "Service", rb_cObject);
2057
+ cDaemon = rb_define_class_under(mWin32, "Daemon", rb_cObject);
2058
+ cServiceError = rb_define_class_under(
2059
+ mWin32, "ServiceError", rb_eStandardError);
2060
+ cDaemonError = rb_define_class_under(
2061
+ mWin32, "DaemonError", rb_eStandardError);
2062
+
2063
+ // Service class and instance methods
2064
+ rb_define_alloc_func(cService,service_allocate);
2065
+ rb_define_method(cService, "initialize", service_init, -1);
2066
+ rb_define_method(cService, "close", service_close, 0);
2067
+ rb_define_method(cService, "create_service", service_create, 0);
2068
+ rb_define_method(cService, "configure_service", service_configure, 0);
2069
+
2070
+ // We do type checking for these two methods, so they're defined
2071
+ // indepedently.
2072
+ rb_define_method(cService, "dependencies=", service_set_dependencies, 1);
2073
+ rb_define_method(cService, "dependencies", service_get_dependencies, 0);
2074
+
2075
+ rb_define_singleton_method(cService, "open", service_open, -1);
2076
+ rb_define_singleton_method(cService, "delete", service_delete, -1);
2077
+ rb_define_singleton_method(cService, "start", service_start, -1);
2078
+ rb_define_singleton_method(cService, "stop", service_stop, -1);
2079
+ rb_define_singleton_method(cService, "pause", service_pause, -1);
2080
+ rb_define_singleton_method(cService, "resume", service_resume, -1);
2081
+ rb_define_singleton_method(cService, "services", service_services, -1);
2082
+ rb_define_singleton_method(cService, "status", service_status, -1);
2083
+ rb_define_singleton_method(cService, "exists?", service_exists, -1);
2084
+
2085
+ rb_define_singleton_method(cService, "getdisplayname",
2086
+ service_get_display_name, -1);
2087
+
2088
+ rb_define_singleton_method(cService, "getservicename",
2089
+ service_get_service_name, -1);
2090
+
2091
+ // Daemon class and instance methods
2092
+ rb_define_alloc_func(cDaemon, daemon_allocate);
2093
+ rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
2094
+ rb_define_method(cDaemon, "state", daemon_state, 0);
2095
+ rb_define_method(cDaemon, "running?", daemon_is_running, 0);
2096
+
2097
+ // Intialize critical section used by green polling thread
2098
+ InitializeCriticalSection(&csControlCode);
2099
+
2100
+ // Constants
2101
+ rb_define_const(cService, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
2102
+ rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
2103
+ set_service_constants(cService);
2104
+ set_daemon_constants(cDaemon);
2105
+
2106
+ // Structs
2107
+ v_service_status_struct = rb_struct_define("Win32ServiceStatus",
2108
+ "service_type", "current_state", "controls_accepted", "win32_exit_code",
2109
+ "service_specific_exit_code", "check_point", "wait_hint",
2110
+ "interactive"
2111
+ #ifdef HAVE_QUERYSERVICESTATUSEX
2112
+ ,"pid", "service_flags"
2113
+ #endif
2114
+ ,0);
2115
+
2116
+ v_service_struct = rb_struct_define("Win32Service", "service_name",
2117
+ "display_name", "service_type", "current_state", "controls_accepted",
2118
+ "win32_exit_code", "service_specific_exit_code", "check_point",
2119
+ "wait_hint", "binary_path_name", "start_type", "error_control",
2120
+ "load_order_group", "tag_id", "start_name", "dependencies",
2121
+ "description", "interactive"
2122
+ #ifdef HAVE_ENUMSERVICESSTATUSEX
2123
+ ,"pid", "service_flags"
2124
+ #endif
2125
+ ,0);
2126
+
2127
+ // Create an attr_accessor for each valid instance method
2128
+ for(i = 0; i < sizeof(keys)/sizeof(char*); i++){
2129
+ rb_define_attr(cService,keys[i],1,1);
2130
+ }
2131
+ }