win32-service 0.7.1 → 0.7.2

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.
@@ -1,89 +1,95 @@
1
- LOG_FILE = 'C:\\test.log'
1
+ LOG_FILE = 'C:\\win32_daemon_test.log'
2
2
 
3
3
  begin
4
- require 'win32/daemon'
5
- include Win32
4
+ require 'rubygems'
5
+ require 'win32/daemon'
6
+ include Win32
6
7
 
7
- class DemoDaemon < Daemon
8
- # This method fires off before the +service_main+ mainloop is entered.
9
- # Any pre-setup code you need to run before your service's mainloop
10
- # starts should be put here. Otherwise the service might fail with a
11
- # timeout error when you try to start it.
12
- #
13
- def service_init
14
- 10.times{ |i|
15
- File.open(LOG_FILE , 'a'){ |f| f.puts("#{i}") }
16
- sleep 1
17
- }
18
- end
8
+ class DemoDaemon < Daemon
9
+ # This method fires off before the +service_main+ mainloop is entered.
10
+ # Any pre-setup code you need to run before your service's mainloop
11
+ # starts should be put here. Otherwise the service might fail with a
12
+ # timeout error when you try to start it.
13
+ #
14
+ def service_init
15
+ 10.times{ |i|
16
+ File.open(LOG_FILE , 'a'){ |f| f.puts("#{i}") }
17
+ sleep 1
18
+ }
19
+ end
19
20
 
20
- # This is the daemon's mainloop. In other words, whatever runs here
21
- # is the code that runs while your service is running. Note that the
22
- # loop is not implicit.
23
- #
24
- # You must setup a loop as I've done here with the 'while running?'
25
- # code, or setup your own loop. Otherwise your service will exit and
26
- # won't be especially useful.
27
- #
28
- # In this particular case, I've setup a loop to append a short message
29
- # and timestamp to a file on your C: drive every 20 seconds. Be sure
30
- # to stop the service when you're done!
31
- #
32
- def service_main(*args)
33
- msg = 'service_main entered at: ' + Time.now.to_s
34
- File.open(LOG_FILE, 'a'){ |f|
35
- f.puts msg
36
- f.puts "Args: " + args.join(',')
37
- }
21
+ # This is the daemon's mainloop. In other words, whatever runs here
22
+ # is the code that runs while your service is running. Note that the
23
+ # loop is not implicit.
24
+ #
25
+ # You must setup a loop as I've done here with the 'while running?'
26
+ # code, or setup your own loop. Otherwise your service will exit and
27
+ # won't be especially useful.
28
+ #
29
+ # In this particular case, I've setup a loop to append a short message
30
+ # and timestamp to a file on your C: drive every 20 seconds. Be sure
31
+ # to stop the service when you're done!
32
+ #
33
+ def service_main(*args)
34
+ msg = 'service_main entered at: ' + Time.now.to_s
35
+
36
+ File.open(LOG_FILE, 'a'){ |f|
37
+ f.puts msg
38
+ f.puts "Args: " + args.join(',')
39
+ }
38
40
 
39
- while running?
40
- if state == RUNNING
41
- sleep 20
42
- msg = 'Service is running as of: ' + Time.now.to_s
43
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
44
- else
45
- sleep 0.5
46
- end
47
- end
41
+ # While we're in here the daemon is running.
42
+ while running?
43
+ if state == RUNNING
44
+ sleep 20
45
+ msg = 'Service is running as of: ' + Time.now.to_s
46
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
47
+ else # PAUSED or IDLE
48
+ sleep 0.5
49
+ end
50
+ end
51
+
52
+ # We've left the loop, the daemon is about to exit.
48
53
 
49
- File.open(LOG_FILE, 'a'){ |f| f.puts "STATE: #{state}" }
54
+ File.open(LOG_FILE, 'a'){ |f| f.puts "STATE: #{state}" }
50
55
 
51
- msg = 'service_main left at: ' + Time.now.to_s
52
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
53
- end
56
+ msg = 'service_main left at: ' + Time.now.to_s
57
+
58
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
59
+ end
54
60
 
55
- # This event triggers when the service receives a signal to stop. I've
56
- # added an explicit "exit!" here to ensure that the Ruby interpreter exits
57
- # properly. I use 'exit!' instead of 'exit' because otherwise Ruby will
58
- # raise a SystemExitError, which I don't want.
59
- #
60
- def service_stop
61
- msg = 'Received stop signal at: ' + Time.now.to_s
62
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
63
- exit!
64
- end
61
+ # This event triggers when the service receives a signal to stop. I've
62
+ # added an explicit "exit!" here to ensure that the Ruby interpreter exits
63
+ # properly. I use 'exit!' instead of 'exit' because otherwise Ruby will
64
+ # raise a SystemExitError, which I don't want.
65
+ #
66
+ def service_stop
67
+ msg = 'Received stop signal at: ' + Time.now.to_s
68
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
69
+ exit!
70
+ end
65
71
 
66
- # This event triggers when the service receives a signal to pause.
67
- #
68
- def service_pause
69
- msg = 'Received pause signal at: ' + Time.now.to_s
70
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
71
- end
72
+ # This event triggers when the service receives a signal to pause.
73
+ #
74
+ def service_pause
75
+ msg = 'Received pause signal at: ' + Time.now.to_s
76
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
77
+ end
72
78
 
73
- # This event triggers when the service receives a signal to resume
74
- # from a paused state.
75
- #
76
- def service_resume
77
- msg = 'Received resume signal at: ' + Time.now.to_s
78
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
79
- end
80
- end
79
+ # This event triggers when the service receives a signal to resume
80
+ # from a paused state.
81
+ #
82
+ def service_resume
83
+ msg = 'Received resume signal at: ' + Time.now.to_s
84
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
85
+ end
86
+ end
81
87
 
82
- # Create an instance of the Daemon and put it into a loop. I borrowed the
83
- # method name 'mainloop' from Tk, btw.
84
- #
85
- DemoDaemon.mainloop
88
+ # Create an instance of the Daemon and put it into a loop. I borrowed the
89
+ # method name 'mainloop' from Tk, btw.
90
+ #
91
+ DemoDaemon.mainloop
86
92
  rescue Exception => err
87
- File.open(LOG_FILE, 'a'){ |fh| fh.puts 'Daemon failure: ' + err }
88
- raise
93
+ File.open(LOG_FILE, 'a'){ |fh| fh.puts 'Daemon failure: ' + err }
94
+ raise
89
95
  end
@@ -3,21 +3,28 @@
3
3
  #
4
4
  # Test script for general futzing that shows off the basic
5
5
  # capabilities of this library. Modify as you see fit.
6
+ #
7
+ # You can run this sample program via the "example:services" task.
6
8
  #######################################################################
7
9
  require 'win32/service'
8
10
  include Win32
9
11
 
10
12
  puts "VERSION: " + Service::VERSION
11
13
 
12
- p Service.exists?("ClipSrv")
13
- p Service.exists?("foo")
14
+ p Service.exists?('Schedule')
15
+ p Service.exists?('bogusxxx')
14
16
 
15
- status = Service.status("ClipSrv")
17
+ status = Service.status('Schedule')
16
18
  p status
17
19
 
18
- info = Service.config_info("ClipSrv")
20
+ info = Service.config_info('Schedule')
21
+
22
+ print "\n\nShowing config info for Schedule service\n\n"
19
23
  p info
20
24
 
25
+ print "\n\nAbout to show all services\n\n"
26
+ sleep 10
27
+
21
28
  Service.services{ |struct|
22
- p struct
29
+ p struct
23
30
  }
data/ext/extconf.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require 'mkmf'
2
2
 
3
3
  dir_config('win32-daemon')
4
+ dir_config('seh')
5
+
4
6
  have_func('RegisterServiceCtrlHandlerEx')
7
+ have_header('seh.h')
8
+
5
9
  create_makefile('win32/daemon', 'win32')
data/ext/win32/daemon.c CHANGED
@@ -5,7 +5,11 @@
5
5
  #include <malloc.h>
6
6
  #include <tchar.h>
7
7
 
8
- #define WIN32_SERVICE_VERSION "0.7.1"
8
+ #ifdef HAVE_SEH_H
9
+ #include <seh.h>
10
+ #endif
11
+
12
+ #define WIN32_SERVICE_VERSION "0.7.2"
9
13
 
10
14
  // Ruby 1.9.x
11
15
  #ifndef RSTRING_PTR
@@ -52,71 +56,73 @@ void SetTheServiceStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,
52
56
  // Return an error code as a string
53
57
  LPTSTR ErrorDescription(DWORD p_dwError)
54
58
  {
55
- HLOCAL hLocal = NULL;
56
- static TCHAR ErrStr[1024];
57
- int len;
58
-
59
- if (!(len=FormatMessage(
60
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
61
- FORMAT_MESSAGE_FROM_SYSTEM |
62
- FORMAT_MESSAGE_IGNORE_INSERTS,
63
- NULL,
64
- p_dwError,
65
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
66
- (LPTSTR)&hLocal,
67
- 0,
68
- NULL)))
69
- {
70
- rb_raise(rb_eStandardError, "unable to format error message");
71
- }
72
- memset(ErrStr, 0, sizeof(ErrStr));
73
- strncpy(ErrStr, (LPTSTR)hLocal, len-2); // remove \r\n
74
- LocalFree(hLocal);
75
- return ErrStr;
59
+ HLOCAL hLocal = NULL;
60
+ static TCHAR ErrStr[1024];
61
+ int len;
62
+
63
+ if (!(len=FormatMessage(
64
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
65
+ FORMAT_MESSAGE_FROM_SYSTEM |
66
+ FORMAT_MESSAGE_IGNORE_INSERTS,
67
+ NULL,
68
+ p_dwError,
69
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
70
+ (LPTSTR)&hLocal,
71
+ 0,
72
+ NULL)))
73
+ {
74
+ rb_raise(rb_eStandardError, "unable to format error message");
75
+ }
76
+ memset(ErrStr, 0, sizeof(ErrStr));
77
+ strncpy(ErrStr, (LPTSTR)hLocal, len-2); // remove \r\n
78
+ LocalFree(hLocal);
79
+ return ErrStr;
76
80
  }
77
81
 
78
82
  // Called by the service control manager after the call to
79
83
  // StartServiceCtrlDispatcher.
80
84
  void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
81
85
  {
82
- // Obtain the name of the service.
83
- LPTSTR lpszServiceName = lpszArgv[0];
86
+ // Obtain the name of the service.
87
+ LPTSTR lpszServiceName = lpszArgv[0];
84
88
 
85
- // Args passed to Service.start
86
- if(dwArgc > 1){
87
- unsigned int i;
88
- Argc = dwArgc - 1;
89
- Argv = malloc(sizeof(VALUE)*Argc);
89
+ // Args passed to Service.start
90
+ if(dwArgc > 1){
91
+ unsigned int i;
92
+ Argc = dwArgc - 1;
93
+ Argv = malloc(sizeof(VALUE)*Argc);
90
94
 
91
- for(i=1; i < dwArgc; i++)
92
- Argv[i-1] = rb_str_new2(lpszArgv[i]);
93
- }
95
+ for(i=1; i < dwArgc; i++)
96
+ Argv[i-1] = rb_str_new2(lpszArgv[i]);
97
+ }
94
98
 
95
- // Register the service ctrl handler.
96
- ssh = RegisterServiceCtrlHandler(lpszServiceName,
97
- (LPHANDLER_FUNCTION)Service_Ctrl);
99
+ // Register the service ctrl handler.
100
+ ssh = RegisterServiceCtrlHandler(
101
+ lpszServiceName,
102
+ (LPHANDLER_FUNCTION)Service_Ctrl
103
+ );
98
104
 
99
- // no service to stop, no service handle to notify, nothing to do but exit
100
- if(ssh == (SERVICE_STATUS_HANDLE)0)
101
- return;
105
+ // no service to stop, no service handle to notify, nothing to do but exit
106
+ if(ssh == (SERVICE_STATUS_HANDLE)0)
107
+ return;
102
108
 
103
- // The service has started.
104
- SetTheServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0);
109
+ // The service has started.
110
+ SetTheServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0);
105
111
 
106
- SetEvent(hStartEvent);
112
+ SetEvent(hStartEvent);
107
113
 
108
- // Main loop for the service.
109
- while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
110
- {
111
- }
114
+ // Main loop for the service.
115
+ while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
116
+ {
117
+ }
112
118
 
113
- // Main loop for the service.
114
- while(WaitForSingleObject(hStopCompletedEvent, 1000) != WAIT_OBJECT_0)
115
- {
116
- }
119
+ // Main loop for the service.
120
+ while(WaitForSingleObject(hStopCompletedEvent, 1000) != WAIT_OBJECT_0)
121
+ {
122
+ }
117
123
 
118
- // Stop the service.
119
- SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
124
+ // Stop the service.
125
+ SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
120
126
  }
121
127
 
122
128
  VALUE Service_Event_Dispatch(VALUE val)
@@ -134,174 +140,174 @@ VALUE Service_Event_Dispatch(VALUE val)
134
140
  return result;
135
141
  }
136
142
 
137
- VALUE Ruby_Service_Ctrl(VALUE self)
138
- {
139
- while (WaitForSingleObject(hStopEvent,0) == WAIT_TIMEOUT)
140
- {
141
- __try
142
- {
143
- EnterCriticalSection(&csControlCode);
144
-
145
- // Check to see if anything interesting has been signaled
146
- if (waiting_control_code != IDLE_CONTROL_CODE)
147
- {
148
- if (waiting_control_code != SERVICE_CONTROL_STOP) {
149
- // if there is a code, create a ruby thread to deal with it
150
- // this might be over engineering the solution, but I don't
151
- // want to block Service_Ctrl longer than necessary and the
152
- // critical section will block it.
153
- VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
154
-
155
- if(EventHookHash != Qnil){
156
- VALUE val = rb_hash_aref(EventHookHash, INT2NUM(waiting_control_code));
157
-
158
- if(val != Qnil)
159
- rb_thread_create(Service_Event_Dispatch, (void*) val);
160
- }
161
- }
162
- else {
163
- break;
164
- }
165
- waiting_control_code = IDLE_CONTROL_CODE;
166
- }
167
- }
168
- __finally
169
- {
170
- LeaveCriticalSection(&csControlCode);
171
- }
143
+ VALUE Ruby_Service_Ctrl(VALUE self){
144
+ while(WaitForSingleObject(hStopEvent,0) == WAIT_TIMEOUT){
145
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
146
+ __try{
147
+ #endif
148
+ EnterCriticalSection(&csControlCode);
172
149
 
173
- // This is an ugly polling loop, be as polite as possible
174
- rb_thread_polling();
175
- }
150
+ // Check to see if anything interesting has been signaled
151
+ if(waiting_control_code != IDLE_CONTROL_CODE){
152
+ if(waiting_control_code != SERVICE_CONTROL_STOP){
153
+ // If there is a code, create a ruby thread to deal with it
154
+ // this might be over engineering the solution, but I don't
155
+ // want to block Service_Ctrl longer than necessary and the
156
+ // critical section will block it.
157
+ VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
158
+
159
+ if(EventHookHash != Qnil){
160
+ VALUE val = rb_hash_aref(
161
+ EventHookHash,
162
+ INT2NUM(waiting_control_code)
163
+ );
164
+
165
+ if(val != Qnil)
166
+ rb_thread_create(Service_Event_Dispatch, (void*) val);
167
+ }
168
+ }
169
+ else{
170
+ break;
171
+ }
172
+
173
+ waiting_control_code = IDLE_CONTROL_CODE;
174
+ }
175
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
176
+ }
177
+ __finally {
178
+ #endif
179
+ LeaveCriticalSection(&csControlCode);
180
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
181
+ }
182
+ #endif
183
+ // This is an ugly polling loop, be as polite as possible
184
+ rb_thread_polling();
185
+ }
176
186
 
177
- // force service_stop call
178
- {
179
- VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
187
+ // Force service_stop call
188
+ {
189
+ VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
180
190
 
181
- if(EventHookHash != Qnil){
182
- VALUE val = rb_hash_aref(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP));
191
+ if(EventHookHash != Qnil){
192
+ VALUE val = rb_hash_aref(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP));
183
193
 
184
- if(val!=Qnil)
185
- rb_thread_create(Service_Event_Dispatch, (void*) val);
186
- }
187
- }
194
+ if(val!=Qnil)
195
+ rb_thread_create(Service_Event_Dispatch, (void*) val);
196
+ }
197
+ }
188
198
 
189
- return Qnil;
199
+ return Qnil;
190
200
  }
191
201
 
192
202
  // Handles control signals from the service control manager.
193
203
  void WINAPI Service_Ctrl(DWORD dwCtrlCode)
194
204
  {
195
- DWORD dwState = SERVICE_RUNNING;
205
+ DWORD dwState = SERVICE_RUNNING;
196
206
 
197
- // hard to image this code ever failing, so we probably
198
- // don't need the __try/__finally wrapper
199
- __try
200
- {
201
- EnterCriticalSection(&csControlCode);
202
- waiting_control_code = dwCtrlCode;
203
- }
204
- __finally
205
- {
206
- LeaveCriticalSection(&csControlCode);
207
- }
208
-
209
- switch(dwCtrlCode)
210
- {
211
- case SERVICE_CONTROL_STOP:
212
- dwState = SERVICE_STOP_PENDING;
213
- break;
214
-
215
- case SERVICE_CONTROL_SHUTDOWN:
216
- dwState = SERVICE_STOP_PENDING;
217
- break;
218
-
219
- case SERVICE_CONTROL_PAUSE:
220
- dwState = SERVICE_PAUSED;
221
- break;
222
-
223
- case SERVICE_CONTROL_CONTINUE:
224
- dwState = SERVICE_RUNNING;
225
- break;
226
-
227
- case SERVICE_CONTROL_INTERROGATE:
228
- break;
229
-
230
- default:
231
- break;
232
- }
233
-
234
- // Set the status of the service.
235
- SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
236
-
237
- // Tell service_main thread to stop.
238
- if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
239
- (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
240
- {
241
- if(!SetEvent(hStopEvent))
242
- SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
243
- }
207
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
208
+ __try{
209
+ #endif
210
+ EnterCriticalSection(&csControlCode);
211
+ waiting_control_code = dwCtrlCode;
212
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
213
+ }
214
+ __finally{
215
+ #endif
216
+ LeaveCriticalSection(&csControlCode);
217
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
218
+ }
219
+ #endif
220
+
221
+ switch(dwCtrlCode)
222
+ {
223
+ case SERVICE_CONTROL_STOP:
224
+ dwState = SERVICE_STOP_PENDING;
225
+ break;
226
+ case SERVICE_CONTROL_SHUTDOWN:
227
+ dwState = SERVICE_STOP_PENDING;
228
+ break;
229
+ case SERVICE_CONTROL_PAUSE:
230
+ dwState = SERVICE_PAUSED;
231
+ break;
232
+ case SERVICE_CONTROL_CONTINUE:
233
+ dwState = SERVICE_RUNNING;
234
+ break;
235
+ case SERVICE_CONTROL_INTERROGATE:
236
+ break;
237
+ default:
238
+ break;
239
+ }
240
+
241
+ // Set the status of the service.
242
+ SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
243
+
244
+ // Tell service_main thread to stop.
245
+ if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
246
+ (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
247
+ {
248
+ if(!SetEvent(hStopEvent))
249
+ SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
250
+ }
244
251
  }
245
252
 
246
253
  // Wraps SetServiceStatus.
247
254
  void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
248
255
  DWORD dwCheckPoint, DWORD dwWaitHint)
249
256
  {
250
- SERVICE_STATUS ss; // Current status of the service.
251
-
252
- // Disable control requests until the service is started.
253
- if(dwCurrentState == SERVICE_START_PENDING){
254
- ss.dwControlsAccepted = 0;
255
- }
256
- else{
257
- ss.dwControlsAccepted =
258
- SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
259
- SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN;
260
- }
261
-
262
- // Initialize ss structure.
263
- ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
264
- ss.dwServiceSpecificExitCode = 0;
265
- ss.dwCurrentState = dwCurrentState;
266
- ss.dwWin32ExitCode = dwWin32ExitCode;
267
- ss.dwCheckPoint = dwCheckPoint;
268
- ss.dwWaitHint = dwWaitHint;
269
-
270
- dwServiceState = dwCurrentState;
271
-
272
- // Send status of the service to the Service Controller.
273
- if(!SetServiceStatus(ssh, &ss))
274
- SetEvent(hStopEvent);
257
+ SERVICE_STATUS ss; // Current status of the service.
258
+
259
+ // Disable control requests until the service is started.
260
+ if(dwCurrentState == SERVICE_START_PENDING){
261
+ ss.dwControlsAccepted = 0;
262
+ }
263
+ else{
264
+ ss.dwControlsAccepted =
265
+ SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
266
+ SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN;
267
+ }
268
+
269
+ // Initialize ss structure.
270
+ ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
271
+ ss.dwServiceSpecificExitCode = 0;
272
+ ss.dwCurrentState = dwCurrentState;
273
+ ss.dwWin32ExitCode = dwWin32ExitCode;
274
+ ss.dwCheckPoint = dwCheckPoint;
275
+ ss.dwWaitHint = dwWaitHint;
276
+
277
+ dwServiceState = dwCurrentState;
278
+
279
+ // Send status of the service to the Service Controller.
280
+ if(!SetServiceStatus(ssh, &ss))
281
+ SetEvent(hStopEvent);
275
282
  }
276
283
 
277
284
  DWORD WINAPI ThreadProc(LPVOID lpParameter){
278
- SERVICE_TABLE_ENTRY ste[] =
279
- {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
285
+ SERVICE_TABLE_ENTRY ste[] =
286
+ {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
280
287
 
281
- // No service to step, no service handle, no ruby exceptions, just
282
- // terminate the thread.
283
- if(!StartServiceCtrlDispatcher(ste))
284
- return 1;
288
+ // No service to step, no service handle, no ruby exceptions, just
289
+ // terminate the thread.
290
+ if(!StartServiceCtrlDispatcher(ste))
291
+ return 1;
285
292
 
286
- return 0;
293
+ return 0;
287
294
  }
288
295
 
289
296
  static VALUE daemon_allocate(VALUE klass){
290
- return Data_Wrap_Struct(klass, 0, 0, 0);
297
+ return Data_Wrap_Struct(klass, 0, 0, 0);
291
298
  }
292
299
 
293
-
294
- // Call service_main method
300
+ // Call service_main method
295
301
  static VALUE daemon_mainloop_protect(VALUE self)
296
302
  {
297
- if(rb_respond_to(self,rb_intern("service_main"))){
298
- if(Argc == 0)
299
- rb_funcall(self, rb_intern("service_main"), 0);
300
- else
301
- rb_funcall2(self, rb_intern("service_main"), Argc, Argv);
302
- }
303
-
304
- return self;
303
+ if(rb_respond_to(self,rb_intern("service_main"))){
304
+ if(Argc == 0)
305
+ rb_funcall(self, rb_intern("service_main"), 0);
306
+ else
307
+ rb_funcall2(self, rb_intern("service_main"), Argc, Argv);
308
+ }
309
+
310
+ return self;
305
311
  }
306
312
 
307
313
  static VALUE daemon_mainloop_ensure(VALUE self)
@@ -345,151 +351,151 @@ static VALUE daemon_mainloop_ensure(VALUE self)
345
351
  */
346
352
  static VALUE daemon_mainloop(VALUE self)
347
353
  {
348
- DWORD ThreadId;
349
- HANDLE events[2];
350
- DWORD index;
351
- VALUE result, EventHookHash;
352
- int status = 0;
353
-
354
- dwServiceState = 0;
355
-
356
- // Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
357
- // associated with a tty. This helps newbs avoid Errno::EBADF errors.
358
- if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
359
- rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
360
-
361
- if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
362
- rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
363
-
364
- if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
365
- rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
366
-
367
- // Use a markable instance variable to prevent the garbage collector
368
- // from freeing the hash before Ruby_Service_Ctrl exits, or just
369
- // at any ole time while running the service
370
- EventHookHash = rb_hash_new();
371
- rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);
372
-
373
- // Event hooks
374
- if(rb_respond_to(self, rb_intern("service_stop"))){
375
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
376
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
377
- }
378
-
379
- if(rb_respond_to(self, rb_intern("service_pause"))){
380
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
381
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
382
- }
383
-
384
- if(rb_respond_to(self, rb_intern("service_resume"))){
385
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
386
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
387
- }
388
-
389
- if(rb_respond_to(self, rb_intern("service_interrogate"))){
390
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
391
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
392
- }
393
-
394
- if(rb_respond_to(self, rb_intern("service_shutdown"))){
395
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
396
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
397
- }
354
+ DWORD ThreadId;
355
+ HANDLE events[2];
356
+ DWORD index;
357
+ VALUE result, EventHookHash;
358
+ int status = 0;
359
+
360
+ dwServiceState = 0;
361
+
362
+ // Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
363
+ // associated with a tty. This helps newbs avoid Errno::EBADF errors.
364
+ if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
365
+ rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
366
+
367
+ if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
368
+ rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
369
+
370
+ if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
371
+ rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
372
+
373
+ // Use a markable instance variable to prevent the garbage collector
374
+ // from freeing the hash before Ruby_Service_Ctrl exits, or just
375
+ // at any ole time while running the service
376
+ EventHookHash = rb_hash_new();
377
+ rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);
378
+
379
+ // Event hooks
380
+ if(rb_respond_to(self, rb_intern("service_stop"))){
381
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
382
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
383
+ }
384
+
385
+ if(rb_respond_to(self, rb_intern("service_pause"))){
386
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
387
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
388
+ }
389
+
390
+ if(rb_respond_to(self, rb_intern("service_resume"))){
391
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
392
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
393
+ }
394
+
395
+ if(rb_respond_to(self, rb_intern("service_interrogate"))){
396
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
397
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
398
+ }
399
+
400
+ if(rb_respond_to(self, rb_intern("service_shutdown"))){
401
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
402
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
403
+ }
398
404
 
399
405
  #ifdef SERVICE_CONTROL_PARAMCHANGE
400
- if(rb_respond_to(self, rb_intern("service_paramchange"))){
401
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
402
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
403
- }
406
+ if(rb_respond_to(self, rb_intern("service_paramchange"))){
407
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
408
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
409
+ }
404
410
  #endif
405
411
 
406
412
  #ifdef SERVICE_CONTROL_NETBINDADD
407
- if(rb_respond_to(self, rb_intern("service_netbindadd"))){
408
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
409
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
410
- }
413
+ if(rb_respond_to(self, rb_intern("service_netbindadd"))){
414
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
415
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
416
+ }
411
417
  #endif
412
418
 
413
419
  #ifdef SERVICE_CONTROL_NETBINDREMOVE
414
- if(rb_respond_to(self, rb_intern("service_netbindremove"))){
415
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
416
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
417
- }
420
+ if(rb_respond_to(self, rb_intern("service_netbindremove"))){
421
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
422
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
423
+ }
418
424
  #endif
419
425
 
420
426
  #ifdef SERVICE_CONTROL_NETBINDENABLE
421
- if(rb_respond_to(self, rb_intern("service_netbindenable"))){
422
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
423
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
424
- }
427
+ if(rb_respond_to(self, rb_intern("service_netbindenable"))){
428
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
429
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
430
+ }
425
431
  #endif
426
432
 
427
433
  #ifdef SERVICE_CONTROL_NETBINDDISABLE
428
- if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
429
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
430
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
431
- }
434
+ if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
435
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
436
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
437
+ }
432
438
  #endif
433
439
 
434
- // Calling init here so that init failures never even tries to
435
- // start the service... of course that means that init methods
436
- // must be very quick, because the SCM will be receiving no
437
- // START_PENDING messages while init's running - I may fix this
438
- // later
439
- if(rb_respond_to(self, rb_intern("service_init")))
440
- rb_funcall(self, rb_intern("service_init"),0);
440
+ // Calling init here so that init failures never even tries to
441
+ // start the service... of course that means that init methods
442
+ // must be very quick, because the SCM will be receiving no
443
+ // START_PENDING messages while init's running - I may fix this
444
+ // later
445
+ if(rb_respond_to(self, rb_intern("service_init")))
446
+ rb_funcall(self, rb_intern("service_init"),0);
441
447
 
442
- // Create the event to signal the service to start.
443
- hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
448
+ // Create the event to signal the service to start.
449
+ hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
444
450
 
445
- if(hStartEvent == NULL)
446
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
451
+ if(hStartEvent == NULL)
452
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
447
453
 
448
- // Create the event to signal the service to stop.
449
- hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
454
+ // Create the event to signal the service to stop.
455
+ hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
450
456
 
451
- if(hStopEvent == NULL)
452
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
457
+ if(hStopEvent == NULL)
458
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
453
459
 
454
- // Create the event to signal the service that stop has completed
455
- hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
460
+ // Create the event to signal the service that stop has completed
461
+ hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
456
462
 
457
- if(hStopCompletedEvent == NULL)
458
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
463
+ if(hStopCompletedEvent == NULL)
464
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
459
465
 
460
- // Create Thread for service main
461
- hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
466
+ // Create Thread for service main
467
+ hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
462
468
 
463
- if(hThread == INVALID_HANDLE_VALUE)
464
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
469
+ if(hThread == INVALID_HANDLE_VALUE)
470
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
465
471
 
466
- events[0] = hThread;
467
- events[1] = hStartEvent;
472
+ events[0] = hThread;
473
+ events[1] = hStartEvent;
468
474
 
469
- // wait for Service_Main function to either start the service OR terminate
470
- while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
471
- {
472
- }
475
+ // wait for Service_Main function to either start the service OR terminate
476
+ while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
477
+ {
478
+ }
473
479
 
474
- // thread exited, so the show is off
475
- if(index == WAIT_OBJECT_0)
476
- rb_raise(cDaemonError, "Service_Main thread exited abnormally");
480
+ // thread exited, so the show is off
481
+ if(index == WAIT_OBJECT_0)
482
+ rb_raise(cDaemonError, "Service_Main thread exited abnormally");
477
483
 
478
- // from this point onward, stopevent must be triggered!
484
+ // from this point onward, stopevent must be triggered!
479
485
 
480
- // Create the green thread to poll for Service_Ctrl events
481
- rb_thread_create(Ruby_Service_Ctrl, (void *)self);
486
+ // Create the green thread to poll for Service_Ctrl events
487
+ rb_thread_create(Ruby_Service_Ctrl, (void *)self);
482
488
 
483
- result = rb_protect(daemon_mainloop_protect, self, &status);
489
+ result = rb_protect(daemon_mainloop_protect, self, &status);
484
490
 
485
- // service_main raised an exception
486
- if(status){
487
- daemon_mainloop_ensure(self);
488
- rb_jump_tag(status);
489
- }
491
+ // service_main raised an exception
492
+ if(status){
493
+ daemon_mainloop_ensure(self);
494
+ rb_jump_tag(status);
495
+ }
490
496
 
491
- // service_main exited cleanly
492
- return daemon_mainloop_ensure(self);
497
+ // service_main exited cleanly
498
+ return daemon_mainloop_ensure(self);
493
499
  }
494
500
 
495
501
  /*
@@ -510,7 +516,7 @@ static VALUE daemon_mainloop(VALUE self)
510
516
  * See the Daemon#running? method for an abstraction of the above code.
511
517
  */
512
518
  static VALUE daemon_state(VALUE self){
513
- return UINT2NUM(dwServiceState);
519
+ return UINT2NUM(dwServiceState);
514
520
  }
515
521
 
516
522
  /*
@@ -529,83 +535,78 @@ static VALUE daemon_state(VALUE self){
529
535
  * end
530
536
  */
531
537
  static VALUE daemon_is_running(VALUE self){
532
- VALUE v_bool = Qfalse;
533
- if(
534
- (dwServiceState == SERVICE_RUNNING) ||
535
- (dwServiceState == SERVICE_PAUSED) ||
536
- (dwServiceState == 0)
537
- ){
538
- v_bool = Qtrue;
539
- }
540
-
541
- return v_bool;
538
+ VALUE v_bool = Qfalse;
539
+
540
+ if(
541
+ (dwServiceState == SERVICE_RUNNING) ||
542
+ (dwServiceState == SERVICE_PAUSED) ||
543
+ (dwServiceState == 0)
544
+ ){
545
+ v_bool = Qtrue;
546
+ }
547
+
548
+ return v_bool;
542
549
  }
543
550
 
544
551
  /*
545
552
  * This is a shortcut for Daemon.new + Daemon#mainloop.
546
553
  */
547
554
  static VALUE daemon_c_mainloop(VALUE klass){
548
- VALUE v_args[1];
549
- VALUE v_daemon = rb_class_new_instance(0, v_args, klass);
550
- return rb_funcall(v_daemon, rb_intern("mainloop"), 0, 0);
555
+ VALUE v_args[1];
556
+ VALUE v_daemon = rb_class_new_instance(0, v_args, klass);
557
+ return rb_funcall(v_daemon, rb_intern("mainloop"), 0, 0);
551
558
  }
552
559
 
553
560
  void Init_daemon()
554
561
  {
555
- /* The Win32 module serves as a namespace only. */
556
- VALUE mWin32 = rb_define_module("Win32");
562
+ /* The Win32 module serves as a namespace only. */
563
+ VALUE mWin32 = rb_define_module("Win32");
557
564
 
558
- /* The Daemon class encapsulates a Windows service through the use
559
- * of callback methods and a main loop.
560
- */
561
- VALUE cDaemon = rb_define_class_under(mWin32, "Daemon", rb_cObject);
565
+ /* The Daemon class encapsulates a Windows service through the use
566
+ * of callback methods and a main loop.
567
+ */
568
+ VALUE cDaemon = rb_define_class_under(mWin32, "Daemon", rb_cObject);
562
569
 
563
- /* Error typically raised if something goes wrong with your daemon. */
564
- cDaemonError = rb_define_class_under(cDaemon, "Error", rb_eStandardError);
570
+ /* Error typically raised if something goes wrong with your daemon. */
571
+ cDaemonError = rb_define_class_under(cDaemon, "Error", rb_eStandardError);
565
572
 
566
- rb_define_alloc_func(cDaemon, daemon_allocate);
567
- rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
568
- rb_define_method(cDaemon, "state", daemon_state, 0);
569
- rb_define_method(cDaemon, "running?", daemon_is_running, 0);
573
+ rb_define_alloc_func(cDaemon, daemon_allocate);
574
+ rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
575
+ rb_define_method(cDaemon, "state", daemon_state, 0);
576
+ rb_define_method(cDaemon, "running?", daemon_is_running, 0);
570
577
 
571
- rb_define_singleton_method(cDaemon, "mainloop", daemon_c_mainloop, 0);
578
+ rb_define_singleton_method(cDaemon, "mainloop", daemon_c_mainloop, 0);
572
579
 
573
- // Intialize critical section used by green polling thread
574
- InitializeCriticalSection(&csControlCode);
580
+ // Intialize critical section used by green polling thread
581
+ InitializeCriticalSection(&csControlCode);
575
582
 
576
- // Constants
583
+ // Constants
577
584
 
578
- /* 0.7.0: The version of this library */
579
- rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
585
+ /* 0.7.2: The version of this library */
586
+ rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
580
587
 
581
- /* Service has received a signal to resume but is not yet running */
582
- rb_define_const(cDaemon, "CONTINUE_PENDING",
583
- INT2NUM(SERVICE_CONTINUE_PENDING));
588
+ /* Service has received a signal to resume but is not yet running */
589
+ rb_define_const(cDaemon, "CONTINUE_PENDING",
590
+ INT2NUM(SERVICE_CONTINUE_PENDING));
584
591
 
585
- /* Service has received a signal to pause but is not yet paused */
586
- rb_define_const(cDaemon, "PAUSE_PENDING",
587
- INT2NUM(SERVICE_PAUSE_PENDING));
592
+ /* Service has received a signal to pause but is not yet paused */
593
+ rb_define_const(cDaemon, "PAUSE_PENDING", INT2NUM(SERVICE_PAUSE_PENDING));
588
594
 
589
- /* Service is in a paused state */
590
- rb_define_const(cDaemon, "PAUSED",
591
- INT2NUM(SERVICE_PAUSED));
595
+ /* Service is in a paused state */
596
+ rb_define_const(cDaemon, "PAUSED", INT2NUM(SERVICE_PAUSED));
592
597
 
593
- /* Service is running */
594
- rb_define_const(cDaemon, "RUNNING",
595
- INT2NUM(SERVICE_RUNNING));
598
+ /* Service is running */
599
+ rb_define_const(cDaemon, "RUNNING", INT2NUM(SERVICE_RUNNING));
596
600
 
597
- /* Service has received a signal to start but is not yet running */
598
- rb_define_const(cDaemon, "START_PENDING",
599
- INT2NUM(SERVICE_START_PENDING));
601
+ /* Service has received a signal to start but is not yet running */
602
+ rb_define_const(cDaemon, "START_PENDING", INT2NUM(SERVICE_START_PENDING));
600
603
 
601
- /* Service has received a signal to stop but has not yet stopped */
602
- rb_define_const(cDaemon, "STOP_PENDING",
603
- INT2NUM(SERVICE_STOP_PENDING));
604
+ /* Service has received a signal to stop but has not yet stopped */
605
+ rb_define_const(cDaemon, "STOP_PENDING", INT2NUM(SERVICE_STOP_PENDING));
604
606
 
605
- /* Service is stopped. */
606
- rb_define_const(cDaemon, "STOPPED",
607
- INT2NUM(SERVICE_STOPPED));
607
+ /* Service is stopped. */
608
+ rb_define_const(cDaemon, "STOPPED", INT2NUM(SERVICE_STOPPED));
608
609
 
609
- /* Service is in an idle state */
610
- rb_define_const(cDaemon, "IDLE", INT2NUM(0));
610
+ /* Service is in an idle state */
611
+ rb_define_const(cDaemon, "IDLE", INT2NUM(0));
611
612
  }