Command arguments no longer called options.
[nssm.git] / service.cpp
1 #include "nssm.h"\r
2 \r
3 bool is_admin;\r
4 bool use_critical_section;\r
5 \r
6 extern imports_t imports;\r
7 extern settings_t settings[];\r
8 \r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };\r
11 const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 };\r
12 \r
13 typedef struct {\r
14   int first;\r
15   int last;\r
16 } list_t;\r
17 \r
18 /*\r
19   Check the status in response to a control.\r
20   Returns:  1 if the status is expected, eg STOP following CONTROL_STOP.\r
21             0 if the status is desired, eg STOPPED following CONTROL_STOP.\r
22            -1 if the status is undesired, eg STOPPED following CONTROL_START.\r
23 */\r
24 static inline int service_control_response(unsigned long control, unsigned long status) {\r
25   switch (control) {\r
26     case NSSM_SERVICE_CONTROL_START:\r
27       switch (status) {\r
28         case SERVICE_START_PENDING:\r
29           return 1;\r
30 \r
31         case SERVICE_RUNNING:\r
32           return 0;\r
33 \r
34         default:\r
35           return -1;\r
36       }\r
37 \r
38     case SERVICE_CONTROL_STOP:\r
39     case SERVICE_CONTROL_SHUTDOWN:\r
40       switch (status) {\r
41         case SERVICE_STOP_PENDING:\r
42           return 1;\r
43 \r
44         case SERVICE_STOPPED:\r
45           return 0;\r
46 \r
47         default:\r
48           return -1;\r
49       }\r
50 \r
51     case SERVICE_CONTROL_PAUSE:\r
52       switch (status) {\r
53         case SERVICE_PAUSE_PENDING:\r
54           return 1;\r
55 \r
56         case SERVICE_PAUSED:\r
57           return 0;\r
58 \r
59         default:\r
60           return -1;\r
61       }\r
62 \r
63     case SERVICE_CONTROL_CONTINUE:\r
64       switch (status) {\r
65         case SERVICE_CONTINUE_PENDING:\r
66           return 1;\r
67 \r
68         case SERVICE_RUNNING:\r
69           return 0;\r
70 \r
71         default:\r
72           return -1;\r
73       }\r
74 \r
75     case SERVICE_CONTROL_INTERROGATE:\r
76       return 0;\r
77   }\r
78 \r
79   return 0;\r
80 }\r
81 \r
82 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {\r
83   int tries = 0;\r
84   while (QueryServiceStatus(service_handle, service_status)) {\r
85     int response = service_control_response(control, service_status->dwCurrentState);\r
86     /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */\r
87     if (! response) return response;\r
88     if (response > 0 || service_status->dwCurrentState == initial_status) {\r
89       if (++tries > 10) return response;\r
90       Sleep(50 * tries);\r
91     }\r
92     else return response;\r
93   }\r
94   return -1;\r
95 }\r
96 \r
97 int affinity_mask_to_string(__int64 mask, TCHAR **string) {\r
98   if (! string) return 1;\r
99   if (! mask) {\r
100     *string = 0;\r
101     return 0;\r
102   }\r
103 \r
104   __int64 i, n;\r
105 \r
106   /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */\r
107   list_t set[64];\r
108   for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;\r
109 \r
110   for (i = 0, n = 0; i < _countof(set); i++) {\r
111     if (mask & (1LL << i)) {\r
112       if (set[n].first == -1) set[n].first = set[n].last = (int) i;\r
113       else if (set[n].last == (int) i - 1) set[n].last = (int) i;\r
114       else {\r
115         n++;\r
116         set[n].first = set[n].last = (int) i;\r
117       }\r
118     }\r
119   }\r
120 \r
121   /* Worst case is 2x2 characters for first and last CPU plus - and/or , */\r
122   size_t len = (size_t) (n + 1) * 6;\r
123   *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));\r
124   if (! string) return 2;\r
125 \r
126   size_t s = 0;\r
127   int ret;\r
128   for (i = 0; i <= n; i++) {\r
129     if (i) (*string)[s++] = _T(',');\r
130     ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);\r
131     if (ret < 0) {\r
132       HeapFree(GetProcessHeap(), 0, *string);\r
133       *string = 0;\r
134       return 3;\r
135     }\r
136     else s += ret;\r
137     if (set[i].last != set[i].first) {\r
138       ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);\r
139       if (ret < 0) {\r
140         HeapFree(GetProcessHeap(), 0, *string);\r
141         *string = 0;\r
142         return 4;\r
143       }\r
144       else s += ret;\r
145     }\r
146   }\r
147 \r
148   return 0;\r
149 }\r
150 \r
151 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {\r
152   if (! mask) return 1;\r
153 \r
154   *mask = 0LL;\r
155   if (! string) return 0;\r
156 \r
157   list_t set[64];\r
158 \r
159   TCHAR *s = string;\r
160   TCHAR *end;\r
161   int ret;\r
162   int i;\r
163   int n = 0;\r
164   unsigned long number;\r
165 \r
166   for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;\r
167   n = 0;\r
168 \r
169   while (*s) {\r
170     ret = str_number(s, &number, &end);\r
171     s = end;\r
172     if (ret == 0 || ret == 2) {\r
173       if (number >= _countof(set)) return 2;\r
174       set[n].first = set[n].last = (int) number;\r
175 \r
176       switch (*s) {\r
177         case 0:\r
178           break;\r
179 \r
180         case _T(','):\r
181           n++;\r
182           s++;\r
183           break;\r
184 \r
185         case _T('-'):\r
186           if (! *(++s)) return 3;\r
187           ret = str_number(s, &number, &end);\r
188           if (ret == 0 || ret == 2) {\r
189             s = end;\r
190             if (! *s || *s == _T(',')) {\r
191               set[n].last = (int) number;\r
192               if (! *s) break;\r
193               n++;\r
194               s++;\r
195             }\r
196             else return 3;\r
197           }\r
198           else return 3;\r
199           break;\r
200 \r
201         default:\r
202           return 3;\r
203       }\r
204     }\r
205     else return 4;\r
206   }\r
207 \r
208   for (i = 0; i <= n; i++) {\r
209     for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);\r
210   }\r
211 \r
212   return 0;\r
213 }\r
214 \r
215 inline unsigned long priority_mask() {\r
216  return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;\r
217 }\r
218 \r
219 int priority_constant_to_index(unsigned long constant) {\r
220   switch (constant & priority_mask()) {\r
221     case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;\r
222     case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;\r
223     case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;\r
224     case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;\r
225     case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;\r
226   }\r
227   return NSSM_NORMAL_PRIORITY;\r
228 }\r
229 \r
230 unsigned long priority_index_to_constant(int index) {\r
231   switch (index) {\r
232     case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;\r
233     case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;\r
234     case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;\r
235     case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;\r
236     case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;\r
237   }\r
238   return NORMAL_PRIORITY_CLASS;\r
239 }\r
240 \r
241 static inline unsigned long throttle_milliseconds(unsigned long throttle) {\r
242   /* pow() operates on doubles. */\r
243   unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;\r
244   return ret * 1000;\r
245 }\r
246 \r
247 /*\r
248   Wrapper to be called in a new thread so that we can acknowledge a STOP\r
249   control immediately.\r
250 */\r
251 static unsigned long WINAPI shutdown_service(void *arg) {\r
252   return stop_service((nssm_service_t *) arg, 0, true, true);\r
253 }\r
254 \r
255 /* Connect to the service manager */\r
256 SC_HANDLE open_service_manager() {\r
257   SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);\r
258   if (! ret) {\r
259     if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);\r
260     return 0;\r
261   }\r
262 \r
263   return ret;\r
264 }\r
265 \r
266 /* Open a service by name or display name. */\r
267 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, TCHAR *canonical_name, unsigned long canonical_namelen) {\r
268   SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_ACCESS);\r
269   if (service_handle) {\r
270     if (canonical_name && canonical_name != service_name) {\r
271       if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {\r
272         print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));\r
273         return 0;\r
274       }\r
275     }\r
276     return service_handle;\r
277   }\r
278 \r
279   unsigned long error = GetLastError();\r
280   if (error != ERROR_SERVICE_DOES_NOT_EXIST) {\r
281     print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));\r
282     return 0;\r
283   }\r
284 \r
285   /* We can't look for a display name because there's no buffer to store it. */\r
286   if (! canonical_name) {\r
287     print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));\r
288     return 0;\r
289   }\r
290 \r
291   unsigned long bufsize, required, count, i;\r
292   unsigned long resume = 0;\r
293   EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);\r
294   error = GetLastError();\r
295   if (error != ERROR_MORE_DATA) {\r
296     print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));\r
297     return 0;\r
298   }\r
299 \r
300   ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);\r
301   if (! status) {\r
302     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));\r
303     return 0;\r
304   }\r
305 \r
306   bufsize = required;\r
307   while (true) {\r
308     /*\r
309       EnumServicesStatus() returns:\r
310       1 when it retrieved data and there's no more data to come.\r
311       0 and sets last error to ERROR_MORE_DATA when it retrieved data and\r
312         there's more data to come.\r
313       0 and sets last error to something else on error.\r
314     */\r
315     int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);\r
316     if (! ret) {\r
317       error = GetLastError();\r
318       if (error != ERROR_MORE_DATA) {\r
319         HeapFree(GetProcessHeap(), 0, status);\r
320         print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));\r
321         return 0;\r
322       }\r
323     }\r
324 \r
325     for (i = 0; i < count; i++) {\r
326       if (str_equiv(status[i].lpDisplayName, service_name)) {\r
327         if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {\r
328           HeapFree(GetProcessHeap(), 0, status);\r
329           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));\r
330           return 0;\r
331         }\r
332 \r
333         HeapFree(GetProcessHeap(), 0, status);\r
334         return open_service(services, canonical_name, 0, 0);\r
335       }\r
336     }\r
337 \r
338     if (ret) break;\r
339   }\r
340 \r
341   /* Recurse so we can get an error message. */\r
342   return open_service(services, service_name, 0, 0);\r
343 }\r
344 \r
345 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {\r
346   QUERY_SERVICE_CONFIG *qsc;\r
347   unsigned long bufsize;\r
348   unsigned long error;\r
349 \r
350   QueryServiceConfig(service_handle, 0, 0, &bufsize);\r
351   error = GetLastError();\r
352   if (error == ERROR_INSUFFICIENT_BUFFER) {\r
353     qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);\r
354     if (! qsc) {\r
355       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);\r
356       return 0;\r
357     }\r
358   }\r
359   else {\r
360     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);\r
361     return 0;\r
362   }\r
363 \r
364   if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {\r
365     HeapFree(GetProcessHeap(), 0, qsc);\r
366     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);\r
367     return 0;\r
368   }\r
369 \r
370   return qsc;\r
371 }\r
372 \r
373 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {\r
374   SERVICE_DESCRIPTION description;\r
375   ZeroMemory(&description, sizeof(description));\r
376   /*\r
377     lpDescription must be NULL if we aren't changing, the new description\r
378     or "".\r
379   */\r
380   if (buffer && buffer[0]) description.lpDescription = buffer;\r
381   else description.lpDescription = _T("");\r
382 \r
383   if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;\r
384 \r
385   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);\r
386   return 1;\r
387 }\r
388 \r
389 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {\r
390   if (! buffer) return 1;\r
391 \r
392   unsigned long bufsize;\r
393   QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);\r
394   unsigned long error = GetLastError();\r
395   if (error == ERROR_INSUFFICIENT_BUFFER) {\r
396     SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);\r
397     if (! description) {\r
398       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));\r
399       return 2;\r
400     }\r
401 \r
402     if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {\r
403       if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);\r
404       else ZeroMemory(buffer, len * sizeof(TCHAR));\r
405       HeapFree(GetProcessHeap(), 0, description);\r
406       return 0;\r
407     }\r
408     else {\r
409       HeapFree(GetProcessHeap(), 0, description);\r
410       print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));\r
411       return 3;\r
412     }\r
413   }\r
414   else {\r
415     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));\r
416     return 4;\r
417   }\r
418 \r
419   return 0;\r
420 }\r
421 \r
422 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {\r
423   if (! qsc) return 1;\r
424 \r
425   switch (qsc->dwStartType) {\r
426     case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;\r
427     case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;\r
428     default: *startup = NSSM_STARTUP_AUTOMATIC;\r
429   }\r
430 \r
431   if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;\r
432 \r
433   /* Check for delayed start. */\r
434   unsigned long bufsize;\r
435   unsigned long error;\r
436   QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);\r
437   error = GetLastError();\r
438   if (error == ERROR_INSUFFICIENT_BUFFER) {\r
439     SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);\r
440     if (! info) {\r
441       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));\r
442       return 2;\r
443     }\r
444 \r
445     if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {\r
446       if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;\r
447       HeapFree(GetProcessHeap(), 0, info);\r
448       return 0;\r
449     }\r
450     else {\r
451       error = GetLastError();\r
452       if (error != ERROR_INVALID_LEVEL) {\r
453         print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));\r
454         return 3;\r
455       }\r
456     }\r
457   }\r
458   else if (error != ERROR_INVALID_LEVEL) {\r
459     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));\r
460     return 3;\r
461   }\r
462 \r
463   return 0;\r
464 }\r
465 \r
466 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {\r
467   if (! username) return 1;\r
468   if (! usernamelen) return 1;\r
469 \r
470   *username = 0;\r
471   *usernamelen = 0;\r
472 \r
473   if (! qsc) return 1;\r
474 \r
475   if (is_localsystem(qsc->lpServiceStartName)) return 0;\r
476 \r
477   size_t len = _tcslen(qsc->lpServiceStartName);\r
478   *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));\r
479   if (! *username) {\r
480     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));\r
481     return 2;\r
482   }\r
483 \r
484   memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));\r
485   *usernamelen = len;\r
486 \r
487   return 0;\r
488 }\r
489 \r
490 /* Set default values which aren't zero. */\r
491 void set_nssm_service_defaults(nssm_service_t *service) {\r
492   if (! service) return;\r
493 \r
494   service->type = SERVICE_WIN32_OWN_PROCESS;\r
495   service->priority = NORMAL_PRIORITY_CLASS;\r
496   service->stdin_sharing = NSSM_STDIN_SHARING;\r
497   service->stdin_disposition = NSSM_STDIN_DISPOSITION;\r
498   service->stdin_flags = NSSM_STDIN_FLAGS;\r
499   service->stdout_sharing = NSSM_STDOUT_SHARING;\r
500   service->stdout_disposition = NSSM_STDOUT_DISPOSITION;\r
501   service->stdout_flags = NSSM_STDOUT_FLAGS;\r
502   service->stderr_sharing = NSSM_STDERR_SHARING;\r
503   service->stderr_disposition = NSSM_STDERR_DISPOSITION;\r
504   service->stderr_flags = NSSM_STDERR_FLAGS;\r
505   service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;\r
506   service->stop_method = ~0;\r
507   service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
508   service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
509   service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
510 }\r
511 \r
512 /* Allocate and zero memory for a service. */\r
513 nssm_service_t *alloc_nssm_service() {\r
514   nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));\r
515   if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);\r
516   return service;\r
517 }\r
518 \r
519 /* Free memory for a service. */\r
520 void cleanup_nssm_service(nssm_service_t *service) {\r
521   if (! service) return;\r
522   if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
523   if (service->password) {\r
524     SecureZeroMemory(service->password, service->passwordlen);\r
525     HeapFree(GetProcessHeap(), 0, service->password);\r
526   }\r
527   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
528   if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);\r
529   if (service->handle) CloseServiceHandle(service->handle);\r
530   if (service->process_handle) CloseHandle(service->process_handle);\r
531   if (service->wait_handle) UnregisterWait(service->process_handle);\r
532   if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);\r
533   if (service->throttle_timer) CloseHandle(service->throttle_timer);\r
534   if (service->initial_env) FreeEnvironmentStrings(service->initial_env);\r
535   HeapFree(GetProcessHeap(), 0, service);\r
536 }\r
537 \r
538 /* About to install the service */\r
539 int pre_install_service(int argc, TCHAR **argv) {\r
540   nssm_service_t *service = alloc_nssm_service();\r
541   set_nssm_service_defaults(service);\r
542   if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);\r
543 \r
544   /* Show the dialogue box if we didn't give the service name and path */\r
545   if (argc < 2) return nssm_gui(IDD_INSTALL, service);\r
546 \r
547   if (! service) {\r
548     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));\r
549     return 1;\r
550   }\r
551   _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);\r
552 \r
553   /* Arguments are optional */\r
554   size_t flagslen = 0;\r
555   size_t s = 0;\r
556   int i;\r
557   for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;\r
558   if (! flagslen) flagslen = 1;\r
559   if (flagslen > _countof(service->flags)) {\r
560     print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);\r
561     return 2;\r
562   }\r
563 \r
564   for (i = 2; i < argc; i++) {\r
565     size_t len = _tcslen(argv[i]);\r
566     memmove(service->flags + s, argv[i], len * sizeof(TCHAR));\r
567     s += len;\r
568     if (i < argc - 1) service->flags[s++] = _T(' ');\r
569   }\r
570 \r
571   /* Work out directory name */\r
572   _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
573   strip_basename(service->dir);\r
574 \r
575   int ret = install_service(service);\r
576   cleanup_nssm_service(service);\r
577   return ret;\r
578 }\r
579 \r
580 /* About to edit the service. */\r
581 int pre_edit_service(int argc, TCHAR **argv) {\r
582   /* Require service name. */\r
583   if (argc < 2) return usage(1);\r
584 \r
585   /* Are we editing on the command line? */\r
586   enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;\r
587   const TCHAR *verb = argv[0];\r
588   const TCHAR *service_name = argv[1];\r
589   bool getting = false;\r
590   bool unsetting = false;\r
591 \r
592   /* Minimum number of arguments. */\r
593   int mandatory = 2;\r
594   /* Index of first value. */\r
595   int remainder = 3;\r
596   int i;\r
597   if (str_equiv(verb, _T("get"))) {\r
598     mandatory = 3;\r
599     mode = MODE_GETTING;\r
600   }\r
601   else if (str_equiv(verb, _T("set"))) {\r
602     mandatory = 4;\r
603     mode = MODE_SETTING;\r
604   }\r
605   else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {\r
606     mandatory = 3;\r
607     mode = MODE_RESETTING;\r
608   }\r
609   if (argc < mandatory) return usage(1);\r
610 \r
611   const TCHAR *parameter = 0;\r
612   settings_t *setting = 0;\r
613   TCHAR *additional;\r
614 \r
615   /* Validate the parameter. */\r
616   if (mandatory > 2) {\r
617     bool additional_mandatory = false;\r
618 \r
619     parameter = argv[2];\r
620     for (i = 0; settings[i].name; i++) {\r
621       setting = &settings[i];\r
622       if (! str_equiv(setting->name, parameter)) continue;\r
623       if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {\r
624         additional_mandatory = true;\r
625         mandatory++;\r
626       }\r
627       break;\r
628     }\r
629     if (! settings[i].name) {\r
630       print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);\r
631       for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);\r
632       return 1;\r
633     }\r
634 \r
635     additional = 0;\r
636     if (additional_mandatory) {\r
637       if (argc < mandatory) {\r
638         print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);\r
639         return 1;\r
640       }\r
641       additional = argv[3];\r
642       remainder = 4;\r
643     }\r
644     else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {\r
645       additional = argv[3];\r
646       remainder = 4;\r
647     }\r
648     else {\r
649       additional = argv[remainder];\r
650       if (argc < mandatory) return usage(1);\r
651     }\r
652   }\r
653 \r
654   nssm_service_t *service = alloc_nssm_service();\r
655   _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);\r
656 \r
657   /* Open service manager */\r
658   SC_HANDLE services = open_service_manager();\r
659   if (! services) {\r
660     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
661     return 2;\r
662   }\r
663 \r
664   /* Try to open the service */\r
665   service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
666   if (! service->handle) {\r
667     CloseServiceHandle(services);\r
668     return 3;\r
669   }\r
670 \r
671   /* Get system details. */\r
672   QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);\r
673   if (! qsc) {\r
674     CloseHandle(service->handle);\r
675     CloseServiceHandle(services);\r
676     return 4;\r
677   }\r
678 \r
679   service->type = qsc->dwServiceType;\r
680   if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
681     if (mode != MODE_GETTING) {\r
682       HeapFree(GetProcessHeap(), 0, qsc);\r
683       CloseHandle(service->handle);\r
684       CloseServiceHandle(services);\r
685       print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);\r
686       return 3;\r
687     }\r
688   }\r
689 \r
690   if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
691     if (mode != MODE_GETTING) {\r
692       HeapFree(GetProcessHeap(), 0, qsc);\r
693       CloseHandle(service->handle);\r
694       CloseServiceHandle(services);\r
695       return 4;\r
696     }\r
697   }\r
698 \r
699   if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
700     if (mode != MODE_GETTING) {\r
701       HeapFree(GetProcessHeap(), 0, qsc);\r
702       CloseHandle(service->handle);\r
703       CloseServiceHandle(services);\r
704       return 5;\r
705     }\r
706   }\r
707 \r
708   _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);\r
709 \r
710   /* Get the canonical service name. We open it case insensitively. */\r
711   unsigned long bufsize = _countof(service->name);\r
712   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
713 \r
714   /* Remember the executable in case it isn't NSSM. */\r
715   _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);\r
716   HeapFree(GetProcessHeap(), 0, qsc);\r
717 \r
718   /* Get extended system details. */\r
719   if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
720     if (mode != MODE_GETTING) {\r
721       CloseHandle(service->handle);\r
722       CloseServiceHandle(services);\r
723       return 6;\r
724     }\r
725   }\r
726 \r
727   /* Get NSSM details. */\r
728   get_parameters(service, 0);\r
729 \r
730   CloseServiceHandle(services);\r
731 \r
732   if (! service->exe[0]) {\r
733     service->native = true;\r
734     if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);\r
735   }\r
736 \r
737   /* Editing with the GUI. */\r
738   if (mode == MODE_EDITING) {\r
739     nssm_gui(IDD_EDIT, service);\r
740     return 0;\r
741   }\r
742 \r
743   /* Trying to manage App* parameters for a non-NSSM service. */\r
744   if (! setting->native && service->native) {\r
745     CloseHandle(service->handle);\r
746     print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);\r
747     return 1;\r
748   }\r
749 \r
750   HKEY key;\r
751   value_t value;\r
752   int ret;\r
753 \r
754   if (mode == MODE_GETTING) {\r
755     if (! service->native) {\r
756       key = open_registry(service->name, KEY_READ);\r
757       if (! key) return 4;\r
758     }\r
759 \r
760     if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);\r
761     else ret = get_setting(service->name, key, setting, &value, additional);\r
762     if (ret < 0) {\r
763       CloseHandle(service->handle);\r
764       return 5;\r
765     }\r
766 \r
767     switch (setting->type) {\r
768       case REG_EXPAND_SZ:\r
769       case REG_MULTI_SZ:\r
770       case REG_SZ:\r
771         _tprintf(_T("%s\n"), value.string ? value.string : _T(""));\r
772         HeapFree(GetProcessHeap(), 0, value.string);\r
773         break;\r
774 \r
775       case REG_DWORD:\r
776         _tprintf(_T("%u\n"), value.numeric);\r
777         break;\r
778     }\r
779 \r
780     if (! service->native) RegCloseKey(key);\r
781     CloseHandle(service->handle);\r
782     return 0;\r
783   }\r
784 \r
785   /* Build the value. */\r
786   if (mode == MODE_RESETTING) {\r
787     /* Unset the parameter. */\r
788     value.string = 0;\r
789   }\r
790   else if (remainder == argc) {\r
791     value.string = 0;\r
792   }\r
793   else {\r
794     /* Set the parameter. */\r
795     size_t len = 0;\r
796     size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;\r
797     for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;\r
798     len++;\r
799 \r
800     value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
801     if (! value.string) {\r
802       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));\r
803       CloseHandle(service->handle);\r
804       return 2;\r
805     }\r
806 \r
807     size_t s = 0;\r
808     for (i = remainder; i < argc; i++) {\r
809       size_t len = _tcslen(argv[i]);\r
810       memmove(value.string + s, argv[i], len * sizeof(TCHAR));\r
811       s += len;\r
812       if (i < argc - 1) {\r
813         if (setting->additional & ADDITIONAL_CRLF) {\r
814           value.string[s++] = _T('\r');\r
815           value.string[s++] = _T('\n');\r
816         }\r
817         else value.string[s++] = _T(' ');\r
818       }\r
819     }\r
820     value.string[s] = _T('\0');\r
821   }\r
822 \r
823   if (! service->native) {\r
824     key = open_registry(service->name, KEY_WRITE);\r
825     if (! key) {\r
826       if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
827       return 4;\r
828     }\r
829   }\r
830 \r
831   if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);\r
832   else ret = set_setting(service->name, key, setting, &value, additional);\r
833   if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
834   if (ret < 0) {\r
835     if (! service->native) RegCloseKey(key);\r
836     CloseHandle(service->handle);\r
837     return 6;\r
838   }\r
839 \r
840   if (! service->native) RegCloseKey(key);\r
841   CloseHandle(service->handle);\r
842 \r
843   return 0;\r
844 }\r
845 \r
846 /* About to remove the service */\r
847 int pre_remove_service(int argc, TCHAR **argv) {\r
848   nssm_service_t *service = alloc_nssm_service();\r
849   set_nssm_service_defaults(service);\r
850   if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);\r
851 \r
852   /* Show dialogue box if we didn't pass service name and "confirm" */\r
853   if (argc < 2) return nssm_gui(IDD_REMOVE, service);\r
854   if (str_equiv(argv[1], _T("confirm"))) {\r
855     int ret = remove_service(service);\r
856     cleanup_nssm_service(service);\r
857     return ret;\r
858   }\r
859   print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);\r
860   return 100;\r
861 }\r
862 \r
863 /* Install the service */\r
864 int install_service(nssm_service_t *service) {\r
865   if (! service) return 1;\r
866 \r
867   /* Open service manager */\r
868   SC_HANDLE services = open_service_manager();\r
869   if (! services) {\r
870     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
871     cleanup_nssm_service(service);\r
872     return 2;\r
873   }\r
874 \r
875   /* Get path of this program */\r
876   GetModuleFileName(0, service->image, _countof(service->image));\r
877 \r
878   /* Create the service - settings will be changed in edit_service() */\r
879   service->handle = CreateService(services, service->name, service->name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0);\r
880   if (! service->handle) {\r
881     print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));\r
882     CloseServiceHandle(services);\r
883     return 5;\r
884   }\r
885 \r
886   if (edit_service(service, false)) {\r
887     DeleteService(service->handle);\r
888     CloseServiceHandle(services);\r
889     return 6;\r
890   }\r
891 \r
892   print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
893 \r
894   /* Cleanup */\r
895   CloseServiceHandle(services);\r
896 \r
897   return 0;\r
898 }\r
899 \r
900 /* Edit the service. */\r
901 int edit_service(nssm_service_t *service, bool editing) {\r
902   if (! service) return 1;\r
903 \r
904   /*\r
905     The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS\r
906     and SERVICE_INTERACTIVE_PROCESS.\r
907   */\r
908   service->type &= SERVICE_INTERACTIVE_PROCESS;\r
909   service->type |= SERVICE_WIN32_OWN_PROCESS;\r
910 \r
911   /* Startup type. */\r
912   unsigned long startup;\r
913   switch (service->startup) {\r
914     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
915     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
916     default: startup = SERVICE_AUTO_START;\r
917   }\r
918 \r
919   /* Display name. */\r
920   if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);\r
921 \r
922   /*\r
923     Username must be NULL if we aren't changing or an account name.\r
924     We must explicitly use LOCALSYSTEM to change it when we are editing.\r
925     Password must be NULL if we aren't changing, a password or "".\r
926     Empty passwords are valid but we won't allow them in the GUI.\r
927   */\r
928   TCHAR *username = 0;\r
929   TCHAR *password = 0;\r
930   if (service->usernamelen) {\r
931     username = service->username;\r
932     if (service->passwordlen) password = service->password;\r
933     else password = _T("");\r
934   }\r
935   else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;\r
936 \r
937   if (well_known_username(username)) password = _T("");\r
938   else {\r
939     if (grant_logon_as_service(username)) {\r
940       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
941       return 5;\r
942     }\r
943   }\r
944 \r
945   if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {\r
946     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
947     return 5;\r
948   }\r
949 \r
950   if (service->description[0] || editing) {\r
951     set_service_description(service->name, service->handle, service->description);\r
952   }\r
953 \r
954   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
955   ZeroMemory(&delayed, sizeof(delayed));\r
956   if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
957   else delayed.fDelayedAutostart = 0;\r
958   /* Delayed startup isn't supported until Vista. */\r
959   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
960     unsigned long error = GetLastError();\r
961     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
962     if (error != ERROR_INVALID_LEVEL) {\r
963       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);\r
964     }\r
965   }\r
966 \r
967   /* Don't mess with parameters which aren't ours. */\r
968   if (! service->native) {\r
969     /* Now we need to put the parameters into the registry */\r
970     if (create_parameters(service, editing)) {\r
971       print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);\r
972       return 6;\r
973     }\r
974 \r
975     set_service_recovery(service);\r
976   }\r
977 \r
978   return 0;\r
979 }\r
980 \r
981 /* Control a service. */\r
982 int control_service(unsigned long control, int argc, TCHAR **argv) {\r
983   if (argc < 1) return usage(1);\r
984   TCHAR *service_name = argv[0];\r
985   TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
986 \r
987   SC_HANDLE services = open_service_manager();\r
988   if (! services) {\r
989     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
990     return 2;\r
991   }\r
992 \r
993   SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));\r
994   if (! service_handle) {\r
995     CloseServiceHandle(services);\r
996     return 3;\r
997   }\r
998 \r
999   int ret;\r
1000   unsigned long error;\r
1001   SERVICE_STATUS service_status;\r
1002   if (control == NSSM_SERVICE_CONTROL_START) {\r
1003     unsigned long initial_status = SERVICE_STOPPED;\r
1004     ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);\r
1005     error = GetLastError();\r
1006     CloseServiceHandle(services);\r
1007 \r
1008     if (error == ERROR_IO_PENDING) {\r
1009       /*\r
1010         Older versions of Windows return immediately with ERROR_IO_PENDING\r
1011         indicate that the operation is still in progress.  Newer versions\r
1012         will return it if there really is a delay.\r
1013       */\r
1014       ret = 1;\r
1015       error = ERROR_SUCCESS;\r
1016     }\r
1017 \r
1018     if (ret) {\r
1019       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1020       CloseHandle(service_handle);\r
1021 \r
1022       if (response) {\r
1023         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1024         return 1;\r
1025       }\r
1026       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1027       return 0;\r
1028     }\r
1029     else {\r
1030       CloseHandle(service_handle);\r
1031       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1032       return 1;\r
1033     }\r
1034   }\r
1035   else if (control == SERVICE_CONTROL_INTERROGATE) {\r
1036     /*\r
1037       We could actually send an INTERROGATE control but that won't return\r
1038       any information if the service is stopped and we don't care about\r
1039       the extra details it might give us in any case.  So we'll fake it.\r
1040     */\r
1041     ret = QueryServiceStatus(service_handle, &service_status);\r
1042     error = GetLastError();\r
1043 \r
1044     if (ret) {\r
1045       _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));\r
1046       return 0;\r
1047     }\r
1048     else {\r
1049       _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));\r
1050       return 1;\r
1051     }\r
1052   }\r
1053   else {\r
1054     ret = ControlService(service_handle, control, &service_status);\r
1055     unsigned long initial_status = service_status.dwCurrentState;\r
1056     error = GetLastError();\r
1057     CloseServiceHandle(services);\r
1058 \r
1059     if (error == ERROR_IO_PENDING) {\r
1060       ret = 1;\r
1061       error = ERROR_SUCCESS;\r
1062     }\r
1063 \r
1064     if (ret) {\r
1065       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1066       CloseHandle(service_handle);\r
1067 \r
1068       if (response) {\r
1069         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1070         return 1;\r
1071       }\r
1072       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1073       return 0;\r
1074     }\r
1075     else {\r
1076       CloseHandle(service_handle);\r
1077       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1078       if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
1079         if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
1080       }\r
1081       return 1;\r
1082     }\r
1083   }\r
1084 }\r
1085 \r
1086 /* Remove the service */\r
1087 int remove_service(nssm_service_t *service) {\r
1088   if (! service) return 1;\r
1089 \r
1090   /* Open service manager */\r
1091   SC_HANDLE services = open_service_manager();\r
1092   if (! services) {\r
1093     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1094     return 2;\r
1095   }\r
1096 \r
1097   /* Try to open the service */\r
1098   service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
1099   if (! service->handle) {\r
1100     CloseServiceHandle(services);\r
1101     return 3;\r
1102   }\r
1103 \r
1104   /* Get the canonical service name. We open it case insensitively. */\r
1105   unsigned long bufsize = _countof(service->displayname);\r
1106   GetServiceDisplayName(services, service->name, service->displayname, &bufsize);\r
1107   bufsize = _countof(service->name);\r
1108   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
1109 \r
1110   /* Try to delete the service */\r
1111   if (! DeleteService(service->handle)) {\r
1112     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);\r
1113     CloseServiceHandle(services);\r
1114     return 4;\r
1115   }\r
1116 \r
1117   /* Cleanup */\r
1118   CloseServiceHandle(services);\r
1119 \r
1120   print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
1121   return 0;\r
1122 }\r
1123 \r
1124 /* Service initialisation */\r
1125 void WINAPI service_main(unsigned long argc, TCHAR **argv) {\r
1126   nssm_service_t *service = alloc_nssm_service();\r
1127   if (! service) return;\r
1128 \r
1129   if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {\r
1130     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);\r
1131     return;\r
1132   }\r
1133 \r
1134   /* We can use a condition variable in a critical section on Vista or later. */\r
1135   if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;\r
1136   else use_critical_section = false;\r
1137 \r
1138   /* Initialise status */\r
1139   ZeroMemory(&service->status, sizeof(service->status));\r
1140   service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
1141   service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
1142   service->status.dwWin32ExitCode = NO_ERROR;\r
1143   service->status.dwServiceSpecificExitCode = 0;\r
1144   service->status.dwCheckPoint = 0;\r
1145   service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1146 \r
1147   /* Signal we AREN'T running the server */\r
1148   service->process_handle = 0;\r
1149   service->pid = 0;\r
1150 \r
1151   /* Register control handler */\r
1152   service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);\r
1153   if (! service->status_handle) {\r
1154     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);\r
1155     return;\r
1156   }\r
1157 \r
1158   log_service_control(service->name, 0, true);\r
1159 \r
1160   service->status.dwCurrentState = SERVICE_START_PENDING;\r
1161   service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;\r
1162   SetServiceStatus(service->status_handle, &service->status);\r
1163 \r
1164   if (is_admin) {\r
1165     /* Try to create the exit action parameters; we don't care if it fails */\r
1166     create_exit_action(service->name, exit_action_strings[0], false);\r
1167 \r
1168     SC_HANDLE services = open_service_manager();\r
1169     if (services) {\r
1170       service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);\r
1171       set_service_recovery(service);\r
1172       CloseServiceHandle(services);\r
1173     }\r
1174   }\r
1175 \r
1176   /* Used for signalling a resume if the service pauses when throttled. */\r
1177   if (use_critical_section) {\r
1178     InitializeCriticalSection(&service->throttle_section);\r
1179     service->throttle_section_initialised = true;\r
1180   }\r
1181   else {\r
1182     service->throttle_timer = CreateWaitableTimer(0, 1, 0);\r
1183     if (! service->throttle_timer) {\r
1184       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);\r
1185     }\r
1186   }\r
1187 \r
1188   /* Remember our initial environment. */\r
1189   service->initial_env = GetEnvironmentStrings();\r
1190 \r
1191   monitor_service(service);\r
1192 }\r
1193 \r
1194 /* Make sure service recovery actions are taken where necessary */\r
1195 void set_service_recovery(nssm_service_t *service) {\r
1196   SERVICE_FAILURE_ACTIONS_FLAG flag;\r
1197   ZeroMemory(&flag, sizeof(flag));\r
1198   flag.fFailureActionsOnNonCrashFailures = true;\r
1199 \r
1200   /* This functionality was added in Vista so the call may fail */\r
1201   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
1202     unsigned long error = GetLastError();\r
1203     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1204     if (error != ERROR_INVALID_LEVEL) {\r
1205       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);\r
1206     }\r
1207   }\r
1208 }\r
1209 \r
1210 int monitor_service(nssm_service_t *service) {\r
1211   /* Set service status to started */\r
1212   int ret = start_service(service);\r
1213   if (ret) {\r
1214     TCHAR code[16];\r
1215     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);\r
1216     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);\r
1217     return ret;\r
1218   }\r
1219   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);\r
1220 \r
1221   /* Monitor service */\r
1222   if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
1223     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);\r
1224   }\r
1225 \r
1226   return 0;\r
1227 }\r
1228 \r
1229 TCHAR *service_control_text(unsigned long control) {\r
1230   switch (control) {\r
1231     /* HACK: there is no SERVICE_CONTROL_START constant */\r
1232     case NSSM_SERVICE_CONTROL_START: return _T("START");\r
1233     case SERVICE_CONTROL_STOP: return _T("STOP");\r
1234     case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");\r
1235     case SERVICE_CONTROL_PAUSE: return _T("PAUSE");\r
1236     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
1237     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
1238     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
1239     default: return 0;\r
1240   }\r
1241 }\r
1242 \r
1243 TCHAR *service_status_text(unsigned long status) {\r
1244   switch (status) {\r
1245     case SERVICE_STOPPED: return _T("SERVICE_STOPPED");\r
1246     case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");\r
1247     case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");\r
1248     case SERVICE_RUNNING: return _T("SERVICE_RUNNING");\r
1249     case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");\r
1250     case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");\r
1251     case SERVICE_PAUSED: return _T("SERVICE_PAUSED");\r
1252     default: return 0;\r
1253   }\r
1254 }\r
1255 \r
1256 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {\r
1257   TCHAR *text = service_control_text(control);\r
1258   unsigned long event;\r
1259 \r
1260   if (! text) {\r
1261     /* "0x" + 8 x hex + NULL */\r
1262     text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));\r
1263     if (! text) {\r
1264       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1265       return;\r
1266     }\r
1267     if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {\r
1268       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1269       HeapFree(GetProcessHeap(), 0, text);\r
1270       return;\r
1271     }\r
1272 \r
1273     event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;\r
1274   }\r
1275   else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;\r
1276   else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;\r
1277 \r
1278   log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);\r
1279 \r
1280   if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {\r
1281     HeapFree(GetProcessHeap(), 0, text);\r
1282   }\r
1283 }\r
1284 \r
1285 /* Service control handler */\r
1286 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {\r
1287   nssm_service_t *service = (nssm_service_t *) context;\r
1288 \r
1289   switch (control) {\r
1290     case SERVICE_CONTROL_INTERROGATE:\r
1291       /* We always keep the service status up-to-date so this is a no-op. */\r
1292       return NO_ERROR;\r
1293 \r
1294     case SERVICE_CONTROL_SHUTDOWN:\r
1295     case SERVICE_CONTROL_STOP:\r
1296       log_service_control(service->name, control, true);\r
1297       /*\r
1298         We MUST acknowledge the stop request promptly but we're committed to\r
1299         waiting for the application to exit.  Spawn a new thread to wait\r
1300         while we acknowledge the request.\r
1301       */\r
1302       if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {\r
1303         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
1304 \r
1305         /*\r
1306           We couldn't create a thread to tidy up so we'll have to force the tidyup\r
1307           to complete in time in this thread.\r
1308         */\r
1309         service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
1310         service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
1311         service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
1312 \r
1313         stop_service(service, 0, true, true);\r
1314       }\r
1315       return NO_ERROR;\r
1316 \r
1317     case SERVICE_CONTROL_CONTINUE:\r
1318       log_service_control(service->name, control, true);\r
1319       service->throttle = 0;\r
1320       if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);\r
1321       else {\r
1322         if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
1323         ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1324         SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1325       }\r
1326       /* We can't continue if the application is running! */\r
1327       if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
1328       service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;\r
1329       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);\r
1330       SetServiceStatus(service->status_handle, &service->status);\r
1331       return NO_ERROR;\r
1332 \r
1333     case SERVICE_CONTROL_PAUSE:\r
1334       /*\r
1335         We don't accept pause messages but it isn't possible to register\r
1336         only for continue messages so we have to handle this case.\r
1337       */\r
1338       log_service_control(service->name, control, false);\r
1339       return ERROR_CALL_NOT_IMPLEMENTED;\r
1340 \r
1341     case NSSM_SERVICE_CONTROL_ROTATE:\r
1342       log_service_control(service->name, control, true);\r
1343       if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
1344       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
1345       return NO_ERROR;\r
1346   }\r
1347 \r
1348   /* Unknown control */\r
1349   log_service_control(service->name, control, false);\r
1350   return ERROR_CALL_NOT_IMPLEMENTED;\r
1351 }\r
1352 \r
1353 /* Start the service */\r
1354 int start_service(nssm_service_t *service) {\r
1355   service->stopping = false;\r
1356   service->allow_restart = true;\r
1357 \r
1358   if (service->process_handle) return 0;\r
1359 \r
1360   /* Allocate a STARTUPINFO structure for a new process */\r
1361   STARTUPINFO si;\r
1362   ZeroMemory(&si, sizeof(si));\r
1363   si.cb = sizeof(si);\r
1364 \r
1365   /* Allocate a PROCESSINFO structure for the process */\r
1366   PROCESS_INFORMATION pi;\r
1367   ZeroMemory(&pi, sizeof(pi));\r
1368 \r
1369   /* Get startup parameters */\r
1370   int ret = get_parameters(service, &si);\r
1371   if (ret) {\r
1372     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);\r
1373     return stop_service(service, 2, true, true);\r
1374   }\r
1375 \r
1376   /* Launch executable with arguments */\r
1377   TCHAR cmd[CMD_LENGTH];\r
1378   if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {\r
1379     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);\r
1380     return stop_service(service, 2, true, true);\r
1381   }\r
1382 \r
1383   throttle_restart(service);\r
1384 \r
1385   /* Set the environment. */\r
1386   if (service->env) duplicate_environment(service->env);\r
1387   if (service->env_extra) set_environment_block(service->env_extra);\r
1388 \r
1389   /* Set up I/O redirection. */\r
1390   if (get_output_handles(service, &si)) {\r
1391     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
1392     if (! service->no_console) FreeConsole();\r
1393     close_output_handles(&si);\r
1394     return stop_service(service, 4, true, true);\r
1395   }\r
1396 \r
1397   bool inherit_handles = false;\r
1398   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
1399   unsigned long flags = service->priority & priority_mask();\r
1400   if (service->affinity) flags |= CREATE_SUSPENDED;\r
1401   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
1402     unsigned long exitcode = 3;\r
1403     unsigned long error = GetLastError();\r
1404     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1405     close_output_handles(&si);\r
1406     duplicate_environment(service->initial_env);\r
1407     return stop_service(service, exitcode, true, true);\r
1408   }\r
1409   service->process_handle = pi.hProcess;\r
1410   service->pid = pi.dwProcessId;\r
1411 \r
1412   if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1413 \r
1414   close_output_handles(&si);\r
1415 \r
1416   if (! service->no_console) FreeConsole();\r
1417 \r
1418   /* Restore our environment. */\r
1419   duplicate_environment(service->initial_env);\r
1420 \r
1421   if (service->affinity) {\r
1422     /*\r
1423       We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1424       so that we can parse it regardless of whether we're running in 32-bit\r
1425       or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1426       defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1427       (or when running the 32-bit NSSM).\r
1428 \r
1429       The result is a lot of seemingly-unnecessary casting throughout the code\r
1430       and potentially confusion when we actually try to start the service.\r
1431       Having said that, however, it's unlikely that we're actually going to\r
1432       run in 32-bit mode on a system which has more than 32 CPUs so the\r
1433       likelihood of seeing a confusing situation is somewhat diminished.\r
1434     */\r
1435     DWORD_PTR affinity, system_affinity;\r
1436 \r
1437     if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1438     else {\r
1439       affinity = (DWORD_PTR) service->affinity;\r
1440       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1441     }\r
1442 \r
1443     if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1444       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1445     }\r
1446 \r
1447     ResumeThread(pi.hThread);\r
1448   }\r
1449 \r
1450   /*\r
1451     Wait for a clean startup before changing the service status to RUNNING\r
1452     but be mindful of the fact that we are blocking the service control manager\r
1453     so abandon the wait before too much time has elapsed.\r
1454   */\r
1455   unsigned long delay = service->throttle_delay;\r
1456   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
1457     TCHAR delay_milliseconds[16];\r
1458     _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
1459     TCHAR deadline_milliseconds[16];\r
1460     _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
1461     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
1462     delay = NSSM_SERVICE_STATUS_DEADLINE;\r
1463   }\r
1464   unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
1465 \r
1466   /* Signal successful start */\r
1467   service->status.dwCurrentState = SERVICE_RUNNING;\r
1468   SetServiceStatus(service->status_handle, &service->status);\r
1469 \r
1470   /* Continue waiting for a clean startup. */\r
1471   if (deadline == WAIT_TIMEOUT) {\r
1472     if (service->throttle_delay > delay) {\r
1473       if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
1474     }\r
1475     else service->throttle = 0;\r
1476   }\r
1477 \r
1478   /* Ensure the restart delay is always applied. */\r
1479   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1480 \r
1481   return 0;\r
1482 }\r
1483 \r
1484 /* Stop the service */\r
1485 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1486   service->allow_restart = false;\r
1487   if (service->wait_handle) {\r
1488     UnregisterWait(service->wait_handle);\r
1489     service->wait_handle = 0;\r
1490   }\r
1491 \r
1492   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1493 \r
1494   if (default_action && ! exitcode && ! graceful) {\r
1495     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service->name, service->exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY], 0);\r
1496     graceful = true;\r
1497   }\r
1498 \r
1499   /* Signal we are stopping */\r
1500   if (graceful) {\r
1501     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1502     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1503     SetServiceStatus(service->status_handle, &service->status);\r
1504   }\r
1505 \r
1506   /* Nothing to do if service isn't running */\r
1507   if (service->pid) {\r
1508     /* Shut down service */\r
1509     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1510     kill_process(service, service->process_handle, service->pid, 0);\r
1511   }\r
1512   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1513 \r
1514   end_service((void *) service, true);\r
1515 \r
1516   /* Signal we stopped */\r
1517   if (graceful) {\r
1518     service->status.dwCurrentState = SERVICE_STOPPED;\r
1519     if (exitcode) {\r
1520       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1521       service->status.dwServiceSpecificExitCode = exitcode;\r
1522     }\r
1523     else {\r
1524       service->status.dwWin32ExitCode = NO_ERROR;\r
1525       service->status.dwServiceSpecificExitCode = 0;\r
1526     }\r
1527     SetServiceStatus(service->status_handle, &service->status);\r
1528   }\r
1529 \r
1530   return exitcode;\r
1531 }\r
1532 \r
1533 /* Callback function triggered when the server exits */\r
1534 void CALLBACK end_service(void *arg, unsigned char why) {\r
1535   nssm_service_t *service = (nssm_service_t *) arg;\r
1536 \r
1537   if (service->stopping) return;\r
1538 \r
1539   service->stopping = true;\r
1540 \r
1541   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1542 \r
1543   /* Use now as a dummy exit time. */\r
1544   GetSystemTimeAsFileTime(&service->exit_time);\r
1545 \r
1546   /* Check exit code */\r
1547   unsigned long exitcode = 0;\r
1548   TCHAR code[16];\r
1549   if (service->process_handle) {\r
1550     GetExitCodeProcess(service->process_handle, &exitcode);\r
1551     /* Check real exit time. */\r
1552     if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
1553     CloseHandle(service->process_handle);\r
1554   }\r
1555 \r
1556   service->process_handle = 0;\r
1557 \r
1558   /*\r
1559     Log that the service ended BEFORE logging about killing the process\r
1560     tree.  See below for the possible values of the why argument.\r
1561   */\r
1562   if (! why) {\r
1563     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1564     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1565   }\r
1566 \r
1567   /* Clean up. */\r
1568   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1569   if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
1570   service->pid = 0;\r
1571 \r
1572   /*\r
1573     The why argument is true if our wait timed out or false otherwise.\r
1574     Our wait is infinite so why will never be true when called by the system.\r
1575     If it is indeed true, assume we were called from stop_service() because\r
1576     this is a controlled shutdown, and don't take any restart action.\r
1577   */\r
1578   if (why) return;\r
1579   if (! service->allow_restart) return;\r
1580 \r
1581   /* What action should we take? */\r
1582   int action = NSSM_EXIT_RESTART;\r
1583   TCHAR action_string[ACTION_LEN];\r
1584   bool default_action;\r
1585   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1586     for (int i = 0; exit_action_strings[i]; i++) {\r
1587       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1588         action = i;\r
1589         break;\r
1590       }\r
1591     }\r
1592   }\r
1593 \r
1594   switch (action) {\r
1595     /* Try to restart the service or return failure code to service manager */\r
1596     case NSSM_EXIT_RESTART:\r
1597       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1598       while (monitor_service(service)) {\r
1599         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1600         Sleep(30000);\r
1601       }\r
1602     break;\r
1603 \r
1604     /* Do nothing, just like srvany would */\r
1605     case NSSM_EXIT_IGNORE:\r
1606       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1607       Sleep(INFINITE);\r
1608     break;\r
1609 \r
1610     /* Tell the service manager we are finished */\r
1611     case NSSM_EXIT_REALLY:\r
1612       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1613       stop_service(service, exitcode, true, default_action);\r
1614     break;\r
1615 \r
1616     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1617     case NSSM_EXIT_UNCLEAN:\r
1618       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1619       stop_service(service, exitcode, false, default_action);\r
1620       free_imports();\r
1621       exit(exitcode);\r
1622     break;\r
1623   }\r
1624 }\r
1625 \r
1626 void throttle_restart(nssm_service_t *service) {\r
1627   /* This can't be a restart if the service is already running. */\r
1628   if (! service->throttle++) return;\r
1629 \r
1630   unsigned long ms;\r
1631   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1632   TCHAR threshold[8], milliseconds[8];\r
1633 \r
1634   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1635   else ms = throttle_ms;\r
1636 \r
1637   if (service->throttle > 7) service->throttle = 8;\r
1638 \r
1639   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1640 \r
1641   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
1642   else {\r
1643     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
1644     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
1645   }\r
1646 \r
1647   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
1648   else if (service->throttle_timer) {\r
1649     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1650     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
1651     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1652   }\r
1653 \r
1654   service->status.dwCurrentState = SERVICE_PAUSED;\r
1655   SetServiceStatus(service->status_handle, &service->status);\r
1656 \r
1657   if (use_critical_section) {\r
1658     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
1659     LeaveCriticalSection(&service->throttle_section);\r
1660   }\r
1661   else {\r
1662     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
1663     else Sleep(ms);\r
1664   }\r
1665 }\r
1666 \r
1667 /*\r
1668   When responding to a stop (or any other) request we need to set dwWaitHint to\r
1669   the number of milliseconds we expect the operation to take, and optionally\r
1670   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
1671   operation completing or dwCheckPoint increasing, the system will consider the\r
1672   service to be hung.\r
1673 \r
1674   However the system will consider the service to be hung after 30000\r
1675   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
1676   changed.  Therefore if we want to wait longer than that we must periodically\r
1677   increase dwCheckPoint.\r
1678 \r
1679   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
1680   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
1681   time dwCheckPoint is also increased.\r
1682 \r
1683   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
1684   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
1685   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
1686   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
1687   dwWaitHint.\r
1688 \r
1689   Only doing both these things will prevent the system from killing the service.\r
1690 \r
1691   Returns: 1 if the wait timed out.\r
1692            0 if the wait completed.\r
1693           -1 on error.\r
1694 */\r
1695 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
1696   unsigned long interval;\r
1697   unsigned long waithint;\r
1698   unsigned long ret;\r
1699   unsigned long waited;\r
1700   TCHAR interval_milliseconds[16];\r
1701   TCHAR timeout_milliseconds[16];\r
1702   TCHAR waited_milliseconds[16];\r
1703   TCHAR *function = function_name;\r
1704 \r
1705   /* Add brackets to function name. */\r
1706   size_t funclen = _tcslen(function_name) + 3;\r
1707   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
1708   if (func) {\r
1709     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
1710   }\r
1711 \r
1712   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
1713 \r
1714   waithint = service->status.dwWaitHint;\r
1715   waited = 0;\r
1716   while (waited < timeout) {\r
1717     interval = timeout - waited;\r
1718     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
1719 \r
1720     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1721     service->status.dwWaitHint += interval;\r
1722     service->status.dwCheckPoint++;\r
1723     SetServiceStatus(service->status_handle, &service->status);\r
1724 \r
1725     if (waited) {\r
1726       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
1727       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
1728       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
1729     }\r
1730 \r
1731     switch (WaitForSingleObject(service->process_handle, interval)) {\r
1732       case WAIT_OBJECT_0:\r
1733         ret = 0;\r
1734         goto awaited;\r
1735 \r
1736       case WAIT_TIMEOUT:\r
1737         ret = 1;\r
1738       break;\r
1739 \r
1740       default:\r
1741         ret = -1;\r
1742         goto awaited;\r
1743     }\r
1744 \r
1745     waited += interval;\r
1746   }\r
1747 \r
1748 awaited:\r
1749   if (func) HeapFree(GetProcessHeap(), 0, func);\r
1750 \r
1751   return ret;\r
1752 }\r