fe6726216bc3513c39301233e7db04060b5decd8
[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 (qsc->lpServiceStartName[0]) {\r
476     if (is_localsystem(qsc->lpServiceStartName)) return 0;\r
477 \r
478     size_t len = _tcslen(qsc->lpServiceStartName);\r
479     *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));\r
480     if (! *username) {\r
481       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));\r
482       return 2;\r
483     }\r
484 \r
485     memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));\r
486     *usernamelen = len;\r
487   }\r
488 \r
489   return 0;\r
490 }\r
491 \r
492 /* Set default values which aren't zero. */\r
493 void set_nssm_service_defaults(nssm_service_t *service) {\r
494   if (! service) return;\r
495 \r
496   service->type = SERVICE_WIN32_OWN_PROCESS;\r
497   service->priority = NORMAL_PRIORITY_CLASS;\r
498   service->stdin_sharing = NSSM_STDIN_SHARING;\r
499   service->stdin_disposition = NSSM_STDIN_DISPOSITION;\r
500   service->stdin_flags = NSSM_STDIN_FLAGS;\r
501   service->stdout_sharing = NSSM_STDOUT_SHARING;\r
502   service->stdout_disposition = NSSM_STDOUT_DISPOSITION;\r
503   service->stdout_flags = NSSM_STDOUT_FLAGS;\r
504   service->stderr_sharing = NSSM_STDERR_SHARING;\r
505   service->stderr_disposition = NSSM_STDERR_DISPOSITION;\r
506   service->stderr_flags = NSSM_STDERR_FLAGS;\r
507   service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;\r
508   service->stop_method = ~0;\r
509   service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
510   service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
511   service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
512 }\r
513 \r
514 /* Allocate and zero memory for a service. */\r
515 nssm_service_t *alloc_nssm_service() {\r
516   nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));\r
517   if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);\r
518   return service;\r
519 }\r
520 \r
521 /* Free memory for a service. */\r
522 void cleanup_nssm_service(nssm_service_t *service) {\r
523   if (! service) return;\r
524   if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
525   if (service->password) {\r
526     SecureZeroMemory(service->password, service->passwordlen);\r
527     HeapFree(GetProcessHeap(), 0, service->password);\r
528   }\r
529   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
530   if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);\r
531   if (service->handle) CloseServiceHandle(service->handle);\r
532   if (service->process_handle) CloseHandle(service->process_handle);\r
533   if (service->wait_handle) UnregisterWait(service->process_handle);\r
534   if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);\r
535   if (service->throttle_timer) CloseHandle(service->throttle_timer);\r
536   if (service->initial_env) FreeEnvironmentStrings(service->initial_env);\r
537   HeapFree(GetProcessHeap(), 0, service);\r
538 }\r
539 \r
540 /* About to install the service */\r
541 int pre_install_service(int argc, TCHAR **argv) {\r
542   nssm_service_t *service = alloc_nssm_service();\r
543   set_nssm_service_defaults(service);\r
544   if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);\r
545 \r
546   /* Show the dialogue box if we didn't give the service name and path */\r
547   if (argc < 2) return nssm_gui(IDD_INSTALL, service);\r
548 \r
549   if (! service) {\r
550     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));\r
551     return 1;\r
552   }\r
553   _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);\r
554 \r
555   /* Arguments are optional */\r
556   size_t flagslen = 0;\r
557   size_t s = 0;\r
558   int i;\r
559   for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;\r
560   if (! flagslen) flagslen = 1;\r
561   if (flagslen > _countof(service->flags)) {\r
562     print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);\r
563     return 2;\r
564   }\r
565 \r
566   for (i = 2; i < argc; i++) {\r
567     size_t len = _tcslen(argv[i]);\r
568     memmove(service->flags + s, argv[i], len * sizeof(TCHAR));\r
569     s += len;\r
570     if (i < argc - 1) service->flags[s++] = _T(' ');\r
571   }\r
572 \r
573   /* Work out directory name */\r
574   _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
575   strip_basename(service->dir);\r
576 \r
577   int ret = install_service(service);\r
578   cleanup_nssm_service(service);\r
579   return ret;\r
580 }\r
581 \r
582 /* About to edit the service. */\r
583 int pre_edit_service(int argc, TCHAR **argv) {\r
584   /* Require service name. */\r
585   if (argc < 2) return usage(1);\r
586 \r
587   /* Are we editing on the command line? */\r
588   enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;\r
589   const TCHAR *verb = argv[0];\r
590   const TCHAR *service_name = argv[1];\r
591   bool getting = false;\r
592   bool unsetting = false;\r
593 \r
594   /* Minimum number of arguments. */\r
595   int mandatory = 2;\r
596   /* Index of first value. */\r
597   int remainder = 3;\r
598   int i;\r
599   if (str_equiv(verb, _T("get"))) {\r
600     mandatory = 3;\r
601     mode = MODE_GETTING;\r
602   }\r
603   else if (str_equiv(verb, _T("set"))) {\r
604     mandatory = 4;\r
605     mode = MODE_SETTING;\r
606   }\r
607   else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {\r
608     mandatory = 3;\r
609     mode = MODE_RESETTING;\r
610   }\r
611   if (argc < mandatory) return usage(1);\r
612 \r
613   const TCHAR *parameter = 0;\r
614   settings_t *setting = 0;\r
615   TCHAR *additional;\r
616 \r
617   /* Validate the parameter. */\r
618   if (mandatory > 2) {\r
619     bool additional_mandatory = false;\r
620 \r
621     parameter = argv[2];\r
622     for (i = 0; settings[i].name; i++) {\r
623       setting = &settings[i];\r
624       if (! str_equiv(setting->name, parameter)) continue;\r
625       if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {\r
626         additional_mandatory = true;\r
627         mandatory++;\r
628       }\r
629       break;\r
630     }\r
631     if (! settings[i].name) {\r
632       print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);\r
633       for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);\r
634       return 1;\r
635     }\r
636 \r
637     additional = 0;\r
638     if (additional_mandatory) {\r
639       if (argc < mandatory) {\r
640         print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);\r
641         return 1;\r
642       }\r
643       additional = argv[3];\r
644       remainder = 4;\r
645     }\r
646     else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {\r
647       additional = argv[3];\r
648       remainder = 4;\r
649     }\r
650     else {\r
651       additional = argv[remainder];\r
652       if (argc < mandatory) return usage(1);\r
653     }\r
654   }\r
655 \r
656   nssm_service_t *service = alloc_nssm_service();\r
657   _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);\r
658 \r
659   /* Open service manager */\r
660   SC_HANDLE services = open_service_manager();\r
661   if (! services) {\r
662     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
663     return 2;\r
664   }\r
665 \r
666   /* Try to open the service */\r
667   service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
668   if (! service->handle) {\r
669     CloseServiceHandle(services);\r
670     return 3;\r
671   }\r
672 \r
673   /* Get system details. */\r
674   QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);\r
675   if (! qsc) {\r
676     CloseHandle(service->handle);\r
677     CloseServiceHandle(services);\r
678     return 4;\r
679   }\r
680 \r
681   service->type = qsc->dwServiceType;\r
682   if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
683     if (mode != MODE_GETTING) {\r
684       HeapFree(GetProcessHeap(), 0, qsc);\r
685       CloseHandle(service->handle);\r
686       CloseServiceHandle(services);\r
687       print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);\r
688       return 3;\r
689     }\r
690   }\r
691 \r
692   if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
693     if (mode != MODE_GETTING) {\r
694       HeapFree(GetProcessHeap(), 0, qsc);\r
695       CloseHandle(service->handle);\r
696       CloseServiceHandle(services);\r
697       return 4;\r
698     }\r
699   }\r
700 \r
701   if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
702     if (mode != MODE_GETTING) {\r
703       HeapFree(GetProcessHeap(), 0, qsc);\r
704       CloseHandle(service->handle);\r
705       CloseServiceHandle(services);\r
706       return 5;\r
707     }\r
708   }\r
709 \r
710   _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);\r
711 \r
712   /* Get the canonical service name. We open it case insensitively. */\r
713   unsigned long bufsize = _countof(service->name);\r
714   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
715 \r
716   /* Remember the executable in case it isn't NSSM. */\r
717   _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);\r
718   HeapFree(GetProcessHeap(), 0, qsc);\r
719 \r
720   /* Get extended system details. */\r
721   if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
722     if (mode != MODE_GETTING) {\r
723       CloseHandle(service->handle);\r
724       CloseServiceHandle(services);\r
725       return 6;\r
726     }\r
727   }\r
728 \r
729   /* Get NSSM details. */\r
730   get_parameters(service, 0);\r
731 \r
732   CloseServiceHandle(services);\r
733 \r
734   if (! service->exe[0]) {\r
735     service->native = true;\r
736     if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);\r
737   }\r
738 \r
739   /* Editing with the GUI. */\r
740   if (mode == MODE_EDITING) {\r
741     nssm_gui(IDD_EDIT, service);\r
742     return 0;\r
743   }\r
744 \r
745   /* Trying to manage App* parameters for a non-NSSM service. */\r
746   if (! setting->native && service->native) {\r
747     CloseHandle(service->handle);\r
748     print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);\r
749     return 1;\r
750   }\r
751 \r
752   HKEY key;\r
753   value_t value;\r
754   int ret;\r
755 \r
756   if (mode == MODE_GETTING) {\r
757     if (! service->native) {\r
758       key = open_registry(service->name, KEY_READ);\r
759       if (! key) return 4;\r
760     }\r
761 \r
762     if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);\r
763     else ret = get_setting(service->name, key, setting, &value, additional);\r
764     if (ret < 0) {\r
765       CloseHandle(service->handle);\r
766       return 5;\r
767     }\r
768 \r
769     switch (setting->type) {\r
770       case REG_EXPAND_SZ:\r
771       case REG_MULTI_SZ:\r
772       case REG_SZ:\r
773         _tprintf(_T("%s\n"), value.string ? value.string : _T(""));\r
774         HeapFree(GetProcessHeap(), 0, value.string);\r
775         break;\r
776 \r
777       case REG_DWORD:\r
778         _tprintf(_T("%u\n"), value.numeric);\r
779         break;\r
780     }\r
781 \r
782     if (! service->native) RegCloseKey(key);\r
783     CloseHandle(service->handle);\r
784     return 0;\r
785   }\r
786 \r
787   /* Build the value. */\r
788   if (mode == MODE_RESETTING) {\r
789     /* Unset the parameter. */\r
790     value.string = 0;\r
791   }\r
792   else if (remainder == argc) {\r
793     value.string = 0;\r
794   }\r
795   else {\r
796     /* Set the parameter. */\r
797     size_t len = 0;\r
798     size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;\r
799     for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;\r
800     len++;\r
801 \r
802     value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
803     if (! value.string) {\r
804       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));\r
805       CloseHandle(service->handle);\r
806       return 2;\r
807     }\r
808 \r
809     size_t s = 0;\r
810     for (i = remainder; i < argc; i++) {\r
811       size_t len = _tcslen(argv[i]);\r
812       memmove(value.string + s, argv[i], len * sizeof(TCHAR));\r
813       s += len;\r
814       if (i < argc - 1) {\r
815         if (setting->additional & ADDITIONAL_CRLF) {\r
816           value.string[s++] = _T('\r');\r
817           value.string[s++] = _T('\n');\r
818         }\r
819         else value.string[s++] = _T(' ');\r
820       }\r
821     }\r
822     value.string[s] = _T('\0');\r
823   }\r
824 \r
825   if (! service->native) {\r
826     key = open_registry(service->name, KEY_WRITE);\r
827     if (! key) {\r
828       if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
829       return 4;\r
830     }\r
831   }\r
832 \r
833   if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);\r
834   else ret = set_setting(service->name, key, setting, &value, additional);\r
835   if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
836   if (ret < 0) {\r
837     if (! service->native) RegCloseKey(key);\r
838     CloseHandle(service->handle);\r
839     return 6;\r
840   }\r
841 \r
842   if (! service->native) RegCloseKey(key);\r
843   CloseHandle(service->handle);\r
844 \r
845   return 0;\r
846 }\r
847 \r
848 /* About to remove the service */\r
849 int pre_remove_service(int argc, TCHAR **argv) {\r
850   nssm_service_t *service = alloc_nssm_service();\r
851   set_nssm_service_defaults(service);\r
852   if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);\r
853 \r
854   /* Show dialogue box if we didn't pass service name and "confirm" */\r
855   if (argc < 2) return nssm_gui(IDD_REMOVE, service);\r
856   if (str_equiv(argv[1], _T("confirm"))) {\r
857     int ret = remove_service(service);\r
858     cleanup_nssm_service(service);\r
859     return ret;\r
860   }\r
861   print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);\r
862   return 100;\r
863 }\r
864 \r
865 /* Install the service */\r
866 int install_service(nssm_service_t *service) {\r
867   if (! service) return 1;\r
868 \r
869   /* Open service manager */\r
870   SC_HANDLE services = open_service_manager();\r
871   if (! services) {\r
872     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
873     cleanup_nssm_service(service);\r
874     return 2;\r
875   }\r
876 \r
877   /* Get path of this program */\r
878   GetModuleFileName(0, service->image, _countof(service->image));\r
879 \r
880   /* Create the service - settings will be changed in edit_service() */\r
881   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
882   if (! service->handle) {\r
883     print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));\r
884     CloseServiceHandle(services);\r
885     return 5;\r
886   }\r
887 \r
888   if (edit_service(service, false)) {\r
889     DeleteService(service->handle);\r
890     CloseServiceHandle(services);\r
891     return 6;\r
892   }\r
893 \r
894   print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
895 \r
896   /* Cleanup */\r
897   CloseServiceHandle(services);\r
898 \r
899   return 0;\r
900 }\r
901 \r
902 /* Edit the service. */\r
903 int edit_service(nssm_service_t *service, bool editing) {\r
904   if (! service) return 1;\r
905 \r
906   /*\r
907     The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS\r
908     and SERVICE_INTERACTIVE_PROCESS.\r
909   */\r
910   service->type &= SERVICE_INTERACTIVE_PROCESS;\r
911   service->type |= SERVICE_WIN32_OWN_PROCESS;\r
912 \r
913   /* Startup type. */\r
914   unsigned long startup;\r
915   switch (service->startup) {\r
916     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
917     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
918     default: startup = SERVICE_AUTO_START;\r
919   }\r
920 \r
921   /* Display name. */\r
922   if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);\r
923 \r
924   /*\r
925     Username must be NULL if we aren't changing or an account name.\r
926     We must explicitly use LOCALSYSTEM to change it when we are editing.\r
927     Password must be NULL if we aren't changing, a password or "".\r
928     Empty passwords are valid but we won't allow them in the GUI.\r
929   */\r
930   TCHAR *username = 0;\r
931   TCHAR *password = 0;\r
932   if (service->usernamelen) {\r
933     username = service->username;\r
934     if (service->passwordlen) password = service->password;\r
935     else password = _T("");\r
936   }\r
937   else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;\r
938 \r
939   if (well_known_username(username)) password = _T("");\r
940   else {\r
941     if (grant_logon_as_service(username)) {\r
942       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
943       return 5;\r
944     }\r
945   }\r
946 \r
947   if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {\r
948     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
949     return 5;\r
950   }\r
951 \r
952   if (service->description[0] || editing) {\r
953     set_service_description(service->name, service->handle, service->description);\r
954   }\r
955 \r
956   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
957   ZeroMemory(&delayed, sizeof(delayed));\r
958   if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
959   else delayed.fDelayedAutostart = 0;\r
960   /* Delayed startup isn't supported until Vista. */\r
961   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
962     unsigned long error = GetLastError();\r
963     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
964     if (error != ERROR_INVALID_LEVEL) {\r
965       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);\r
966     }\r
967   }\r
968 \r
969   /* Don't mess with parameters which aren't ours. */\r
970   if (! service->native) {\r
971     /* Now we need to put the parameters into the registry */\r
972     if (create_parameters(service, editing)) {\r
973       print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);\r
974       return 6;\r
975     }\r
976 \r
977     set_service_recovery(service);\r
978   }\r
979 \r
980   return 0;\r
981 }\r
982 \r
983 /* Control a service. */\r
984 int control_service(unsigned long control, int argc, TCHAR **argv) {\r
985   if (argc < 1) return usage(1);\r
986   TCHAR *service_name = argv[0];\r
987   TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
988 \r
989   SC_HANDLE services = open_service_manager();\r
990   if (! services) {\r
991     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
992     return 2;\r
993   }\r
994 \r
995   SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));\r
996   if (! service_handle) {\r
997     CloseServiceHandle(services);\r
998     return 3;\r
999   }\r
1000 \r
1001   int ret;\r
1002   unsigned long error;\r
1003   SERVICE_STATUS service_status;\r
1004   if (control == NSSM_SERVICE_CONTROL_START) {\r
1005     unsigned long initial_status = SERVICE_STOPPED;\r
1006     ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);\r
1007     error = GetLastError();\r
1008     CloseServiceHandle(services);\r
1009 \r
1010     if (error == ERROR_IO_PENDING) {\r
1011       /*\r
1012         Older versions of Windows return immediately with ERROR_IO_PENDING\r
1013         indicate that the operation is still in progress.  Newer versions\r
1014         will return it if there really is a delay.\r
1015       */\r
1016       ret = 1;\r
1017       error = ERROR_SUCCESS;\r
1018     }\r
1019 \r
1020     if (ret) {\r
1021       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1022       CloseHandle(service_handle);\r
1023 \r
1024       if (response) {\r
1025         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1026         return 1;\r
1027       }\r
1028       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1029       return 0;\r
1030     }\r
1031     else {\r
1032       CloseHandle(service_handle);\r
1033       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1034       return 1;\r
1035     }\r
1036   }\r
1037   else if (control == SERVICE_CONTROL_INTERROGATE) {\r
1038     /*\r
1039       We could actually send an INTERROGATE control but that won't return\r
1040       any information if the service is stopped and we don't care about\r
1041       the extra details it might give us in any case.  So we'll fake it.\r
1042     */\r
1043     ret = QueryServiceStatus(service_handle, &service_status);\r
1044     error = GetLastError();\r
1045 \r
1046     if (ret) {\r
1047       _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));\r
1048       return 0;\r
1049     }\r
1050     else {\r
1051       _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));\r
1052       return 1;\r
1053     }\r
1054   }\r
1055   else {\r
1056     ret = ControlService(service_handle, control, &service_status);\r
1057     unsigned long initial_status = service_status.dwCurrentState;\r
1058     error = GetLastError();\r
1059     CloseServiceHandle(services);\r
1060 \r
1061     if (error == ERROR_IO_PENDING) {\r
1062       ret = 1;\r
1063       error = ERROR_SUCCESS;\r
1064     }\r
1065 \r
1066     if (ret) {\r
1067       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1068       CloseHandle(service_handle);\r
1069 \r
1070       if (response) {\r
1071         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1072         return 1;\r
1073       }\r
1074       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1075       return 0;\r
1076     }\r
1077     else {\r
1078       CloseHandle(service_handle);\r
1079       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1080       if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
1081         if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
1082       }\r
1083       return 1;\r
1084     }\r
1085   }\r
1086 }\r
1087 \r
1088 /* Remove the service */\r
1089 int remove_service(nssm_service_t *service) {\r
1090   if (! service) return 1;\r
1091 \r
1092   /* Open service manager */\r
1093   SC_HANDLE services = open_service_manager();\r
1094   if (! services) {\r
1095     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1096     return 2;\r
1097   }\r
1098 \r
1099   /* Try to open the service */\r
1100   service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
1101   if (! service->handle) {\r
1102     CloseServiceHandle(services);\r
1103     return 3;\r
1104   }\r
1105 \r
1106   /* Get the canonical service name. We open it case insensitively. */\r
1107   unsigned long bufsize = _countof(service->displayname);\r
1108   GetServiceDisplayName(services, service->name, service->displayname, &bufsize);\r
1109   bufsize = _countof(service->name);\r
1110   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
1111 \r
1112   /* Try to delete the service */\r
1113   if (! DeleteService(service->handle)) {\r
1114     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);\r
1115     CloseServiceHandle(services);\r
1116     return 4;\r
1117   }\r
1118 \r
1119   /* Cleanup */\r
1120   CloseServiceHandle(services);\r
1121 \r
1122   print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
1123   return 0;\r
1124 }\r
1125 \r
1126 /* Service initialisation */\r
1127 void WINAPI service_main(unsigned long argc, TCHAR **argv) {\r
1128   nssm_service_t *service = alloc_nssm_service();\r
1129   if (! service) return;\r
1130 \r
1131   if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {\r
1132     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);\r
1133     return;\r
1134   }\r
1135 \r
1136   /* We can use a condition variable in a critical section on Vista or later. */\r
1137   if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;\r
1138   else use_critical_section = false;\r
1139 \r
1140   /* Initialise status */\r
1141   ZeroMemory(&service->status, sizeof(service->status));\r
1142   service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
1143   service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
1144   service->status.dwWin32ExitCode = NO_ERROR;\r
1145   service->status.dwServiceSpecificExitCode = 0;\r
1146   service->status.dwCheckPoint = 0;\r
1147   service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1148 \r
1149   /* Signal we AREN'T running the server */\r
1150   service->process_handle = 0;\r
1151   service->pid = 0;\r
1152 \r
1153   /* Register control handler */\r
1154   service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);\r
1155   if (! service->status_handle) {\r
1156     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);\r
1157     return;\r
1158   }\r
1159 \r
1160   log_service_control(service->name, 0, true);\r
1161 \r
1162   service->status.dwCurrentState = SERVICE_START_PENDING;\r
1163   service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;\r
1164   SetServiceStatus(service->status_handle, &service->status);\r
1165 \r
1166   if (is_admin) {\r
1167     /* Try to create the exit action parameters; we don't care if it fails */\r
1168     create_exit_action(service->name, exit_action_strings[0], false);\r
1169 \r
1170     SC_HANDLE services = open_service_manager();\r
1171     if (services) {\r
1172       service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);\r
1173       set_service_recovery(service);\r
1174       CloseServiceHandle(services);\r
1175     }\r
1176   }\r
1177 \r
1178   /* Used for signalling a resume if the service pauses when throttled. */\r
1179   if (use_critical_section) {\r
1180     InitializeCriticalSection(&service->throttle_section);\r
1181     service->throttle_section_initialised = true;\r
1182   }\r
1183   else {\r
1184     service->throttle_timer = CreateWaitableTimer(0, 1, 0);\r
1185     if (! service->throttle_timer) {\r
1186       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);\r
1187     }\r
1188   }\r
1189 \r
1190   /* Remember our initial environment. */\r
1191   service->initial_env = GetEnvironmentStrings();\r
1192 \r
1193   monitor_service(service);\r
1194 }\r
1195 \r
1196 /* Make sure service recovery actions are taken where necessary */\r
1197 void set_service_recovery(nssm_service_t *service) {\r
1198   SERVICE_FAILURE_ACTIONS_FLAG flag;\r
1199   ZeroMemory(&flag, sizeof(flag));\r
1200   flag.fFailureActionsOnNonCrashFailures = true;\r
1201 \r
1202   /* This functionality was added in Vista so the call may fail */\r
1203   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
1204     unsigned long error = GetLastError();\r
1205     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1206     if (error != ERROR_INVALID_LEVEL) {\r
1207       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);\r
1208     }\r
1209   }\r
1210 }\r
1211 \r
1212 int monitor_service(nssm_service_t *service) {\r
1213   /* Set service status to started */\r
1214   int ret = start_service(service);\r
1215   if (ret) {\r
1216     TCHAR code[16];\r
1217     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);\r
1218     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);\r
1219     return ret;\r
1220   }\r
1221   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);\r
1222 \r
1223   /* Monitor service */\r
1224   if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
1225     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);\r
1226   }\r
1227 \r
1228   return 0;\r
1229 }\r
1230 \r
1231 TCHAR *service_control_text(unsigned long control) {\r
1232   switch (control) {\r
1233     /* HACK: there is no SERVICE_CONTROL_START constant */\r
1234     case NSSM_SERVICE_CONTROL_START: return _T("START");\r
1235     case SERVICE_CONTROL_STOP: return _T("STOP");\r
1236     case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");\r
1237     case SERVICE_CONTROL_PAUSE: return _T("PAUSE");\r
1238     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
1239     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
1240     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
1241     default: return 0;\r
1242   }\r
1243 }\r
1244 \r
1245 TCHAR *service_status_text(unsigned long status) {\r
1246   switch (status) {\r
1247     case SERVICE_STOPPED: return _T("SERVICE_STOPPED");\r
1248     case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");\r
1249     case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");\r
1250     case SERVICE_RUNNING: return _T("SERVICE_RUNNING");\r
1251     case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");\r
1252     case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");\r
1253     case SERVICE_PAUSED: return _T("SERVICE_PAUSED");\r
1254     default: return 0;\r
1255   }\r
1256 }\r
1257 \r
1258 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {\r
1259   TCHAR *text = service_control_text(control);\r
1260   unsigned long event;\r
1261 \r
1262   if (! text) {\r
1263     /* "0x" + 8 x hex + NULL */\r
1264     text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));\r
1265     if (! text) {\r
1266       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1267       return;\r
1268     }\r
1269     if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {\r
1270       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1271       HeapFree(GetProcessHeap(), 0, text);\r
1272       return;\r
1273     }\r
1274 \r
1275     event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;\r
1276   }\r
1277   else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;\r
1278   else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;\r
1279 \r
1280   log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);\r
1281 \r
1282   if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {\r
1283     HeapFree(GetProcessHeap(), 0, text);\r
1284   }\r
1285 }\r
1286 \r
1287 /* Service control handler */\r
1288 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {\r
1289   nssm_service_t *service = (nssm_service_t *) context;\r
1290 \r
1291   switch (control) {\r
1292     case SERVICE_CONTROL_INTERROGATE:\r
1293       /* We always keep the service status up-to-date so this is a no-op. */\r
1294       return NO_ERROR;\r
1295 \r
1296     case SERVICE_CONTROL_SHUTDOWN:\r
1297     case SERVICE_CONTROL_STOP:\r
1298       log_service_control(service->name, control, true);\r
1299       /*\r
1300         We MUST acknowledge the stop request promptly but we're committed to\r
1301         waiting for the application to exit.  Spawn a new thread to wait\r
1302         while we acknowledge the request.\r
1303       */\r
1304       if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {\r
1305         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
1306 \r
1307         /*\r
1308           We couldn't create a thread to tidy up so we'll have to force the tidyup\r
1309           to complete in time in this thread.\r
1310         */\r
1311         service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
1312         service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
1313         service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
1314 \r
1315         stop_service(service, 0, true, true);\r
1316       }\r
1317       return NO_ERROR;\r
1318 \r
1319     case SERVICE_CONTROL_CONTINUE:\r
1320       log_service_control(service->name, control, true);\r
1321       service->throttle = 0;\r
1322       if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);\r
1323       else {\r
1324         if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
1325         ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1326         SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1327       }\r
1328       /* We can't continue if the application is running! */\r
1329       if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
1330       service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;\r
1331       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);\r
1332       SetServiceStatus(service->status_handle, &service->status);\r
1333       return NO_ERROR;\r
1334 \r
1335     case SERVICE_CONTROL_PAUSE:\r
1336       /*\r
1337         We don't accept pause messages but it isn't possible to register\r
1338         only for continue messages so we have to handle this case.\r
1339       */\r
1340       log_service_control(service->name, control, false);\r
1341       return ERROR_CALL_NOT_IMPLEMENTED;\r
1342 \r
1343     case NSSM_SERVICE_CONTROL_ROTATE:\r
1344       log_service_control(service->name, control, true);\r
1345       if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
1346       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
1347       return NO_ERROR;\r
1348   }\r
1349 \r
1350   /* Unknown control */\r
1351   log_service_control(service->name, control, false);\r
1352   return ERROR_CALL_NOT_IMPLEMENTED;\r
1353 }\r
1354 \r
1355 /* Start the service */\r
1356 int start_service(nssm_service_t *service) {\r
1357   service->stopping = false;\r
1358   service->allow_restart = true;\r
1359 \r
1360   if (service->process_handle) return 0;\r
1361 \r
1362   /* Allocate a STARTUPINFO structure for a new process */\r
1363   STARTUPINFO si;\r
1364   ZeroMemory(&si, sizeof(si));\r
1365   si.cb = sizeof(si);\r
1366 \r
1367   /* Allocate a PROCESSINFO structure for the process */\r
1368   PROCESS_INFORMATION pi;\r
1369   ZeroMemory(&pi, sizeof(pi));\r
1370 \r
1371   /* Get startup parameters */\r
1372   int ret = get_parameters(service, &si);\r
1373   if (ret) {\r
1374     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);\r
1375     return stop_service(service, 2, true, true);\r
1376   }\r
1377 \r
1378   /* Launch executable with arguments */\r
1379   TCHAR cmd[CMD_LENGTH];\r
1380   if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {\r
1381     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);\r
1382     return stop_service(service, 2, true, true);\r
1383   }\r
1384 \r
1385   throttle_restart(service);\r
1386 \r
1387   /* Set the environment. */\r
1388   if (service->env) duplicate_environment(service->env);\r
1389   if (service->env_extra) set_environment_block(service->env_extra);\r
1390 \r
1391   /* Set up I/O redirection. */\r
1392   if (get_output_handles(service, &si)) {\r
1393     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
1394     if (! service->no_console) FreeConsole();\r
1395     close_output_handles(&si);\r
1396     return stop_service(service, 4, true, true);\r
1397   }\r
1398 \r
1399   bool inherit_handles = false;\r
1400   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
1401   unsigned long flags = service->priority & priority_mask();\r
1402   if (service->affinity) flags |= CREATE_SUSPENDED;\r
1403   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
1404     unsigned long exitcode = 3;\r
1405     unsigned long error = GetLastError();\r
1406     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1407     close_output_handles(&si);\r
1408     duplicate_environment(service->initial_env);\r
1409     return stop_service(service, exitcode, true, true);\r
1410   }\r
1411   service->process_handle = pi.hProcess;\r
1412   service->pid = pi.dwProcessId;\r
1413 \r
1414   if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1415 \r
1416   close_output_handles(&si);\r
1417 \r
1418   if (! service->no_console) FreeConsole();\r
1419 \r
1420   /* Restore our environment. */\r
1421   duplicate_environment(service->initial_env);\r
1422 \r
1423   if (service->affinity) {\r
1424     /*\r
1425       We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1426       so that we can parse it regardless of whether we're running in 32-bit\r
1427       or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1428       defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1429       (or when running the 32-bit NSSM).\r
1430 \r
1431       The result is a lot of seemingly-unnecessary casting throughout the code\r
1432       and potentially confusion when we actually try to start the service.\r
1433       Having said that, however, it's unlikely that we're actually going to\r
1434       run in 32-bit mode on a system which has more than 32 CPUs so the\r
1435       likelihood of seeing a confusing situation is somewhat diminished.\r
1436     */\r
1437     DWORD_PTR affinity, system_affinity;\r
1438 \r
1439     if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1440     else {\r
1441       affinity = (DWORD_PTR) service->affinity;\r
1442       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1443     }\r
1444 \r
1445     if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1446       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1447     }\r
1448 \r
1449     ResumeThread(pi.hThread);\r
1450   }\r
1451 \r
1452   /*\r
1453     Wait for a clean startup before changing the service status to RUNNING\r
1454     but be mindful of the fact that we are blocking the service control manager\r
1455     so abandon the wait before too much time has elapsed.\r
1456   */\r
1457   unsigned long delay = service->throttle_delay;\r
1458   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
1459     TCHAR delay_milliseconds[16];\r
1460     _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
1461     TCHAR deadline_milliseconds[16];\r
1462     _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
1463     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
1464     delay = NSSM_SERVICE_STATUS_DEADLINE;\r
1465   }\r
1466   unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
1467 \r
1468   /* Signal successful start */\r
1469   service->status.dwCurrentState = SERVICE_RUNNING;\r
1470   SetServiceStatus(service->status_handle, &service->status);\r
1471 \r
1472   /* Continue waiting for a clean startup. */\r
1473   if (deadline == WAIT_TIMEOUT) {\r
1474     if (service->throttle_delay > delay) {\r
1475       if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
1476     }\r
1477     else service->throttle = 0;\r
1478   }\r
1479 \r
1480   /* Ensure the restart delay is always applied. */\r
1481   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1482 \r
1483   return 0;\r
1484 }\r
1485 \r
1486 /* Stop the service */\r
1487 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1488   service->allow_restart = false;\r
1489   if (service->wait_handle) {\r
1490     UnregisterWait(service->wait_handle);\r
1491     service->wait_handle = 0;\r
1492   }\r
1493 \r
1494   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1495 \r
1496   if (default_action && ! exitcode && ! graceful) {\r
1497     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
1498     graceful = true;\r
1499   }\r
1500 \r
1501   /* Signal we are stopping */\r
1502   if (graceful) {\r
1503     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1504     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1505     SetServiceStatus(service->status_handle, &service->status);\r
1506   }\r
1507 \r
1508   /* Nothing to do if service isn't running */\r
1509   if (service->pid) {\r
1510     /* Shut down service */\r
1511     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1512     kill_process(service, service->process_handle, service->pid, 0);\r
1513   }\r
1514   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1515 \r
1516   end_service((void *) service, true);\r
1517 \r
1518   /* Signal we stopped */\r
1519   if (graceful) {\r
1520     service->status.dwCurrentState = SERVICE_STOPPED;\r
1521     if (exitcode) {\r
1522       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1523       service->status.dwServiceSpecificExitCode = exitcode;\r
1524     }\r
1525     else {\r
1526       service->status.dwWin32ExitCode = NO_ERROR;\r
1527       service->status.dwServiceSpecificExitCode = 0;\r
1528     }\r
1529     SetServiceStatus(service->status_handle, &service->status);\r
1530   }\r
1531 \r
1532   return exitcode;\r
1533 }\r
1534 \r
1535 /* Callback function triggered when the server exits */\r
1536 void CALLBACK end_service(void *arg, unsigned char why) {\r
1537   nssm_service_t *service = (nssm_service_t *) arg;\r
1538 \r
1539   if (service->stopping) return;\r
1540 \r
1541   service->stopping = true;\r
1542 \r
1543   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1544 \r
1545   /* Use now as a dummy exit time. */\r
1546   GetSystemTimeAsFileTime(&service->exit_time);\r
1547 \r
1548   /* Check exit code */\r
1549   unsigned long exitcode = 0;\r
1550   TCHAR code[16];\r
1551   if (service->process_handle) {\r
1552     GetExitCodeProcess(service->process_handle, &exitcode);\r
1553     /* Check real exit time. */\r
1554     if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
1555     CloseHandle(service->process_handle);\r
1556   }\r
1557 \r
1558   service->process_handle = 0;\r
1559 \r
1560   /*\r
1561     Log that the service ended BEFORE logging about killing the process\r
1562     tree.  See below for the possible values of the why argument.\r
1563   */\r
1564   if (! why) {\r
1565     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1566     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1567   }\r
1568 \r
1569   /* Clean up. */\r
1570   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1571   if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
1572   service->pid = 0;\r
1573 \r
1574   /*\r
1575     The why argument is true if our wait timed out or false otherwise.\r
1576     Our wait is infinite so why will never be true when called by the system.\r
1577     If it is indeed true, assume we were called from stop_service() because\r
1578     this is a controlled shutdown, and don't take any restart action.\r
1579   */\r
1580   if (why) return;\r
1581   if (! service->allow_restart) return;\r
1582 \r
1583   /* What action should we take? */\r
1584   int action = NSSM_EXIT_RESTART;\r
1585   TCHAR action_string[ACTION_LEN];\r
1586   bool default_action;\r
1587   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1588     for (int i = 0; exit_action_strings[i]; i++) {\r
1589       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1590         action = i;\r
1591         break;\r
1592       }\r
1593     }\r
1594   }\r
1595 \r
1596   switch (action) {\r
1597     /* Try to restart the service or return failure code to service manager */\r
1598     case NSSM_EXIT_RESTART:\r
1599       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1600       while (monitor_service(service)) {\r
1601         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1602         Sleep(30000);\r
1603       }\r
1604     break;\r
1605 \r
1606     /* Do nothing, just like srvany would */\r
1607     case NSSM_EXIT_IGNORE:\r
1608       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1609       Sleep(INFINITE);\r
1610     break;\r
1611 \r
1612     /* Tell the service manager we are finished */\r
1613     case NSSM_EXIT_REALLY:\r
1614       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1615       stop_service(service, exitcode, true, default_action);\r
1616     break;\r
1617 \r
1618     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1619     case NSSM_EXIT_UNCLEAN:\r
1620       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1621       stop_service(service, exitcode, false, default_action);\r
1622       free_imports();\r
1623       exit(exitcode);\r
1624     break;\r
1625   }\r
1626 }\r
1627 \r
1628 void throttle_restart(nssm_service_t *service) {\r
1629   /* This can't be a restart if the service is already running. */\r
1630   if (! service->throttle++) return;\r
1631 \r
1632   unsigned long ms;\r
1633   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1634   TCHAR threshold[8], milliseconds[8];\r
1635 \r
1636   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1637   else ms = throttle_ms;\r
1638 \r
1639   if (service->throttle > 7) service->throttle = 8;\r
1640 \r
1641   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1642 \r
1643   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
1644   else {\r
1645     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
1646     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
1647   }\r
1648 \r
1649   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
1650   else if (service->throttle_timer) {\r
1651     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1652     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
1653     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1654   }\r
1655 \r
1656   service->status.dwCurrentState = SERVICE_PAUSED;\r
1657   SetServiceStatus(service->status_handle, &service->status);\r
1658 \r
1659   if (use_critical_section) {\r
1660     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
1661     LeaveCriticalSection(&service->throttle_section);\r
1662   }\r
1663   else {\r
1664     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
1665     else Sleep(ms);\r
1666   }\r
1667 }\r
1668 \r
1669 /*\r
1670   When responding to a stop (or any other) request we need to set dwWaitHint to\r
1671   the number of milliseconds we expect the operation to take, and optionally\r
1672   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
1673   operation completing or dwCheckPoint increasing, the system will consider the\r
1674   service to be hung.\r
1675 \r
1676   However the system will consider the service to be hung after 30000\r
1677   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
1678   changed.  Therefore if we want to wait longer than that we must periodically\r
1679   increase dwCheckPoint.\r
1680 \r
1681   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
1682   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
1683   time dwCheckPoint is also increased.\r
1684 \r
1685   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
1686   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
1687   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
1688   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
1689   dwWaitHint.\r
1690 \r
1691   Only doing both these things will prevent the system from killing the service.\r
1692 \r
1693   Returns: 1 if the wait timed out.\r
1694            0 if the wait completed.\r
1695           -1 on error.\r
1696 */\r
1697 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
1698   unsigned long interval;\r
1699   unsigned long waithint;\r
1700   unsigned long ret;\r
1701   unsigned long waited;\r
1702   TCHAR interval_milliseconds[16];\r
1703   TCHAR timeout_milliseconds[16];\r
1704   TCHAR waited_milliseconds[16];\r
1705   TCHAR *function = function_name;\r
1706 \r
1707   /* Add brackets to function name. */\r
1708   size_t funclen = _tcslen(function_name) + 3;\r
1709   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
1710   if (func) {\r
1711     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
1712   }\r
1713 \r
1714   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
1715 \r
1716   waithint = service->status.dwWaitHint;\r
1717   waited = 0;\r
1718   while (waited < timeout) {\r
1719     interval = timeout - waited;\r
1720     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
1721 \r
1722     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1723     service->status.dwWaitHint += interval;\r
1724     service->status.dwCheckPoint++;\r
1725     SetServiceStatus(service->status_handle, &service->status);\r
1726 \r
1727     if (waited) {\r
1728       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
1729       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
1730       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
1731     }\r
1732 \r
1733     switch (WaitForSingleObject(service->process_handle, interval)) {\r
1734       case WAIT_OBJECT_0:\r
1735         ret = 0;\r
1736         goto awaited;\r
1737 \r
1738       case WAIT_TIMEOUT:\r
1739         ret = 1;\r
1740       break;\r
1741 \r
1742       default:\r
1743         ret = -1;\r
1744         goto awaited;\r
1745     }\r
1746 \r
1747     waited += interval;\r
1748   }\r
1749 \r
1750 awaited:\r
1751   if (func) HeapFree(GetProcessHeap(), 0, func);\r
1752 \r
1753   return ret;\r
1754 }\r