win32-service 0.5.2-mswin32
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.
- data/CHANGES +174 -0
- data/MANIFEST +16 -0
- data/README +46 -0
- data/doc/daemon.txt +160 -0
- data/doc/service.txt +382 -0
- data/lib/win32/service.c +2131 -0
- data/lib/win32/service.h +416 -0
- data/lib/win32/service.so +0 -0
- data/test/tc_daemon.rb +59 -0
- data/test/tc_service.rb +406 -0
- metadata +59 -0
data/lib/win32/service.c
ADDED
@@ -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
|
+
}
|