Handle well-known account names.
[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 (requires_password(username)) {\r
938     if (grant_logon_as_service(username)) {\r
939       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
940       return 5;\r
941     }\r
942   }\r
943 \r
944   if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {\r
945     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
946     return 5;\r
947   }\r
948 \r
949   if (service->description[0] || editing) {\r
950     set_service_description(service->name, service->handle, service->description);\r
951   }\r
952 \r
953   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
954   ZeroMemory(&delayed, sizeof(delayed));\r
955   if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
956   else delayed.fDelayedAutostart = 0;\r
957   /* Delayed startup isn't supported until Vista. */\r
958   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
959     unsigned long error = GetLastError();\r
960     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
961     if (error != ERROR_INVALID_LEVEL) {\r
962       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);\r
963     }\r
964   }\r
965 \r
966   /* Don't mess with parameters which aren't ours. */\r
967   if (! service->native) {\r
968     /* Now we need to put the parameters into the registry */\r
969     if (create_parameters(service, editing)) {\r
970       print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);\r
971       return 6;\r
972     }\r
973 \r
974     set_service_recovery(service);\r
975   }\r
976 \r
977   return 0;\r
978 }\r
979 \r
980 /* Control a service. */\r
981 int control_service(unsigned long control, int argc, TCHAR **argv) {\r
982   if (argc < 1) return usage(1);\r
983   TCHAR *service_name = argv[0];\r
984   TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
985 \r
986   SC_HANDLE services = open_service_manager();\r
987   if (! services) {\r
988     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
989     return 2;\r
990   }\r
991 \r
992   SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));\r
993   if (! service_handle) {\r
994     CloseServiceHandle(services);\r
995     return 3;\r
996   }\r
997 \r
998   int ret;\r
999   unsigned long error;\r
1000   SERVICE_STATUS service_status;\r
1001   if (control == NSSM_SERVICE_CONTROL_START) {\r
1002     unsigned long initial_status = SERVICE_STOPPED;\r
1003     ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);\r
1004     error = GetLastError();\r
1005     CloseServiceHandle(services);\r
1006 \r
1007     if (error == ERROR_IO_PENDING) {\r
1008       /*\r
1009         Older versions of Windows return immediately with ERROR_IO_PENDING\r
1010         indicate that the operation is still in progress.  Newer versions\r
1011         will return it if there really is a delay.\r
1012       */\r
1013       ret = 1;\r
1014       error = ERROR_SUCCESS;\r
1015     }\r
1016 \r
1017     if (ret) {\r
1018       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1019       CloseHandle(service_handle);\r
1020 \r
1021       if (response) {\r
1022         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1023         return 1;\r
1024       }\r
1025       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1026       return 0;\r
1027     }\r
1028     else {\r
1029       CloseHandle(service_handle);\r
1030       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1031       return 1;\r
1032     }\r
1033   }\r
1034   else if (control == SERVICE_CONTROL_INTERROGATE) {\r
1035     /*\r
1036       We could actually send an INTERROGATE control but that won't return\r
1037       any information if the service is stopped and we don't care about\r
1038       the extra details it might give us in any case.  So we'll fake it.\r
1039     */\r
1040     ret = QueryServiceStatus(service_handle, &service_status);\r
1041     error = GetLastError();\r
1042 \r
1043     if (ret) {\r
1044       _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));\r
1045       return 0;\r
1046     }\r
1047     else {\r
1048       _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));\r
1049       return 1;\r
1050     }\r
1051   }\r
1052   else {\r
1053     ret = ControlService(service_handle, control, &service_status);\r
1054     unsigned long initial_status = service_status.dwCurrentState;\r
1055     error = GetLastError();\r
1056     CloseServiceHandle(services);\r
1057 \r
1058     if (error == ERROR_IO_PENDING) {\r
1059       ret = 1;\r
1060       error = ERROR_SUCCESS;\r
1061     }\r
1062 \r
1063     if (ret) {\r
1064       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1065       CloseHandle(service_handle);\r
1066 \r
1067       if (response) {\r
1068         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1069         return 1;\r
1070       }\r
1071       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1072       return 0;\r
1073     }\r
1074     else {\r
1075       CloseHandle(service_handle);\r
1076       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1077       if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
1078         if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
1079       }\r
1080       return 1;\r
1081     }\r
1082   }\r
1083 }\r
1084 \r
1085 /* Remove the service */\r
1086 int remove_service(nssm_service_t *service) {\r
1087   if (! service) return 1;\r
1088 \r
1089   /* Open service manager */\r
1090   SC_HANDLE services = open_service_manager();\r
1091   if (! services) {\r
1092     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1093     return 2;\r
1094   }\r
1095 \r
1096   /* Try to open the service */\r
1097   service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
1098   if (! service->handle) {\r
1099     CloseServiceHandle(services);\r
1100     return 3;\r
1101   }\r
1102 \r
1103   /* Get the canonical service name. We open it case insensitively. */\r
1104   unsigned long bufsize = _countof(service->displayname);\r
1105   GetServiceDisplayName(services, service->name, service->displayname, &bufsize);\r
1106   bufsize = _countof(service->name);\r
1107   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
1108 \r
1109   /* Try to delete the service */\r
1110   if (! DeleteService(service->handle)) {\r
1111     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);\r
1112     CloseServiceHandle(services);\r
1113     return 4;\r
1114   }\r
1115 \r
1116   /* Cleanup */\r
1117   CloseServiceHandle(services);\r
1118 \r
1119   print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
1120   return 0;\r
1121 }\r
1122 \r
1123 /* Service initialisation */\r
1124 void WINAPI service_main(unsigned long argc, TCHAR **argv) {\r
1125   nssm_service_t *service = alloc_nssm_service();\r
1126   if (! service) return;\r
1127 \r
1128   if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {\r
1129     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);\r
1130     return;\r
1131   }\r
1132 \r
1133   /* We can use a condition variable in a critical section on Vista or later. */\r
1134   if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;\r
1135   else use_critical_section = false;\r
1136 \r
1137   /* Initialise status */\r
1138   ZeroMemory(&service->status, sizeof(service->status));\r
1139   service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
1140   service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
1141   service->status.dwWin32ExitCode = NO_ERROR;\r
1142   service->status.dwServiceSpecificExitCode = 0;\r
1143   service->status.dwCheckPoint = 0;\r
1144   service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1145 \r
1146   /* Signal we AREN'T running the server */\r
1147   service->process_handle = 0;\r
1148   service->pid = 0;\r
1149 \r
1150   /* Register control handler */\r
1151   service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);\r
1152   if (! service->status_handle) {\r
1153     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);\r
1154     return;\r
1155   }\r
1156 \r
1157   log_service_control(service->name, 0, true);\r
1158 \r
1159   service->status.dwCurrentState = SERVICE_START_PENDING;\r
1160   service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;\r
1161   SetServiceStatus(service->status_handle, &service->status);\r
1162 \r
1163   if (is_admin) {\r
1164     /* Try to create the exit action parameters; we don't care if it fails */\r
1165     create_exit_action(service->name, exit_action_strings[0], false);\r
1166 \r
1167     SC_HANDLE services = open_service_manager();\r
1168     if (services) {\r
1169       service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);\r
1170       set_service_recovery(service);\r
1171       CloseServiceHandle(services);\r
1172     }\r
1173   }\r
1174 \r
1175   /* Used for signalling a resume if the service pauses when throttled. */\r
1176   if (use_critical_section) {\r
1177     InitializeCriticalSection(&service->throttle_section);\r
1178     service->throttle_section_initialised = true;\r
1179   }\r
1180   else {\r
1181     service->throttle_timer = CreateWaitableTimer(0, 1, 0);\r
1182     if (! service->throttle_timer) {\r
1183       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);\r
1184     }\r
1185   }\r
1186 \r
1187   /* Remember our initial environment. */\r
1188   service->initial_env = GetEnvironmentStrings();\r
1189 \r
1190   monitor_service(service);\r
1191 }\r
1192 \r
1193 /* Make sure service recovery actions are taken where necessary */\r
1194 void set_service_recovery(nssm_service_t *service) {\r
1195   SERVICE_FAILURE_ACTIONS_FLAG flag;\r
1196   ZeroMemory(&flag, sizeof(flag));\r
1197   flag.fFailureActionsOnNonCrashFailures = true;\r
1198 \r
1199   /* This functionality was added in Vista so the call may fail */\r
1200   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
1201     unsigned long error = GetLastError();\r
1202     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1203     if (error != ERROR_INVALID_LEVEL) {\r
1204       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);\r
1205     }\r
1206   }\r
1207 }\r
1208 \r
1209 int monitor_service(nssm_service_t *service) {\r
1210   /* Set service status to started */\r
1211   int ret = start_service(service);\r
1212   if (ret) {\r
1213     TCHAR code[16];\r
1214     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);\r
1215     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);\r
1216     return ret;\r
1217   }\r
1218   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);\r
1219 \r
1220   /* Monitor service */\r
1221   if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
1222     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);\r
1223   }\r
1224 \r
1225   return 0;\r
1226 }\r
1227 \r
1228 TCHAR *service_control_text(unsigned long control) {\r
1229   switch (control) {\r
1230     /* HACK: there is no SERVICE_CONTROL_START constant */\r
1231     case NSSM_SERVICE_CONTROL_START: return _T("START");\r
1232     case SERVICE_CONTROL_STOP: return _T("STOP");\r
1233     case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");\r
1234     case SERVICE_CONTROL_PAUSE: return _T("PAUSE");\r
1235     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
1236     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
1237     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
1238     default: return 0;\r
1239   }\r
1240 }\r
1241 \r
1242 TCHAR *service_status_text(unsigned long status) {\r
1243   switch (status) {\r
1244     case SERVICE_STOPPED: return _T("SERVICE_STOPPED");\r
1245     case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");\r
1246     case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");\r
1247     case SERVICE_RUNNING: return _T("SERVICE_RUNNING");\r
1248     case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");\r
1249     case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");\r
1250     case SERVICE_PAUSED: return _T("SERVICE_PAUSED");\r
1251     default: return 0;\r
1252   }\r
1253 }\r
1254 \r
1255 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {\r
1256   TCHAR *text = service_control_text(control);\r
1257   unsigned long event;\r
1258 \r
1259   if (! text) {\r
1260     /* "0x" + 8 x hex + NULL */\r
1261     text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));\r
1262     if (! text) {\r
1263       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1264       return;\r
1265     }\r
1266     if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {\r
1267       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1268       HeapFree(GetProcessHeap(), 0, text);\r
1269       return;\r
1270     }\r
1271 \r
1272     event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;\r
1273   }\r
1274   else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;\r
1275   else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;\r
1276 \r
1277   log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);\r
1278 \r
1279   if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {\r
1280     HeapFree(GetProcessHeap(), 0, text);\r
1281   }\r
1282 }\r
1283 \r
1284 /* Service control handler */\r
1285 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {\r
1286   nssm_service_t *service = (nssm_service_t *) context;\r
1287 \r
1288   switch (control) {\r
1289     case SERVICE_CONTROL_INTERROGATE:\r
1290       /* We always keep the service status up-to-date so this is a no-op. */\r
1291       return NO_ERROR;\r
1292 \r
1293     case SERVICE_CONTROL_SHUTDOWN:\r
1294     case SERVICE_CONTROL_STOP:\r
1295       log_service_control(service->name, control, true);\r
1296       /*\r
1297         We MUST acknowledge the stop request promptly but we're committed to\r
1298         waiting for the application to exit.  Spawn a new thread to wait\r
1299         while we acknowledge the request.\r
1300       */\r
1301       if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {\r
1302         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
1303 \r
1304         /*\r
1305           We couldn't create a thread to tidy up so we'll have to force the tidyup\r
1306           to complete in time in this thread.\r
1307         */\r
1308         service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
1309         service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
1310         service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
1311 \r
1312         stop_service(service, 0, true, true);\r
1313       }\r
1314       return NO_ERROR;\r
1315 \r
1316     case SERVICE_CONTROL_CONTINUE:\r
1317       log_service_control(service->name, control, true);\r
1318       service->throttle = 0;\r
1319       if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);\r
1320       else {\r
1321         if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
1322         ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1323         SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1324       }\r
1325       /* We can't continue if the application is running! */\r
1326       if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
1327       service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;\r
1328       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);\r
1329       SetServiceStatus(service->status_handle, &service->status);\r
1330       return NO_ERROR;\r
1331 \r
1332     case SERVICE_CONTROL_PAUSE:\r
1333       /*\r
1334         We don't accept pause messages but it isn't possible to register\r
1335         only for continue messages so we have to handle this case.\r
1336       */\r
1337       log_service_control(service->name, control, false);\r
1338       return ERROR_CALL_NOT_IMPLEMENTED;\r
1339 \r
1340     case NSSM_SERVICE_CONTROL_ROTATE:\r
1341       log_service_control(service->name, control, true);\r
1342       if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
1343       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
1344       return NO_ERROR;\r
1345   }\r
1346 \r
1347   /* Unknown control */\r
1348   log_service_control(service->name, control, false);\r
1349   return ERROR_CALL_NOT_IMPLEMENTED;\r
1350 }\r
1351 \r
1352 /* Start the service */\r
1353 int start_service(nssm_service_t *service) {\r
1354   service->stopping = false;\r
1355   service->allow_restart = true;\r
1356 \r
1357   if (service->process_handle) return 0;\r
1358 \r
1359   /* Allocate a STARTUPINFO structure for a new process */\r
1360   STARTUPINFO si;\r
1361   ZeroMemory(&si, sizeof(si));\r
1362   si.cb = sizeof(si);\r
1363 \r
1364   /* Allocate a PROCESSINFO structure for the process */\r
1365   PROCESS_INFORMATION pi;\r
1366   ZeroMemory(&pi, sizeof(pi));\r
1367 \r
1368   /* Get startup parameters */\r
1369   int ret = get_parameters(service, &si);\r
1370   if (ret) {\r
1371     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);\r
1372     return stop_service(service, 2, true, true);\r
1373   }\r
1374 \r
1375   /* Launch executable with arguments */\r
1376   TCHAR cmd[CMD_LENGTH];\r
1377   if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {\r
1378     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);\r
1379     return stop_service(service, 2, true, true);\r
1380   }\r
1381 \r
1382   throttle_restart(service);\r
1383 \r
1384   /* Set the environment. */\r
1385   if (service->env) duplicate_environment(service->env);\r
1386   if (service->env_extra) set_environment_block(service->env_extra);\r
1387 \r
1388   /* Set up I/O redirection. */\r
1389   if (get_output_handles(service, &si)) {\r
1390     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
1391     if (! service->no_console) FreeConsole();\r
1392     close_output_handles(&si);\r
1393     return stop_service(service, 4, true, true);\r
1394   }\r
1395 \r
1396   bool inherit_handles = false;\r
1397   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
1398   unsigned long flags = service->priority & priority_mask();\r
1399   if (service->affinity) flags |= CREATE_SUSPENDED;\r
1400   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
1401     unsigned long exitcode = 3;\r
1402     unsigned long error = GetLastError();\r
1403     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1404     close_output_handles(&si);\r
1405     duplicate_environment(service->initial_env);\r
1406     return stop_service(service, exitcode, true, true);\r
1407   }\r
1408   service->process_handle = pi.hProcess;\r
1409   service->pid = pi.dwProcessId;\r
1410 \r
1411   if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1412 \r
1413   close_output_handles(&si);\r
1414 \r
1415   if (! service->no_console) FreeConsole();\r
1416 \r
1417   /* Restore our environment. */\r
1418   duplicate_environment(service->initial_env);\r
1419 \r
1420   if (service->affinity) {\r
1421     /*\r
1422       We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1423       so that we can parse it regardless of whether we're running in 32-bit\r
1424       or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1425       defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1426       (or when running the 32-bit NSSM).\r
1427 \r
1428       The result is a lot of seemingly-unnecessary casting throughout the code\r
1429       and potentially confusion when we actually try to start the service.\r
1430       Having said that, however, it's unlikely that we're actually going to\r
1431       run in 32-bit mode on a system which has more than 32 CPUs so the\r
1432       likelihood of seeing a confusing situation is somewhat diminished.\r
1433     */\r
1434     DWORD_PTR affinity, system_affinity;\r
1435 \r
1436     if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1437     else {\r
1438       affinity = (DWORD_PTR) service->affinity;\r
1439       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1440     }\r
1441 \r
1442     if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1443       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1444     }\r
1445 \r
1446     ResumeThread(pi.hThread);\r
1447   }\r
1448 \r
1449   /*\r
1450     Wait for a clean startup before changing the service status to RUNNING\r
1451     but be mindful of the fact that we are blocking the service control manager\r
1452     so abandon the wait before too much time has elapsed.\r
1453   */\r
1454   unsigned long delay = service->throttle_delay;\r
1455   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
1456     TCHAR delay_milliseconds[16];\r
1457     _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
1458     TCHAR deadline_milliseconds[16];\r
1459     _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
1460     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
1461     delay = NSSM_SERVICE_STATUS_DEADLINE;\r
1462   }\r
1463   unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
1464 \r
1465   /* Signal successful start */\r
1466   service->status.dwCurrentState = SERVICE_RUNNING;\r
1467   SetServiceStatus(service->status_handle, &service->status);\r
1468 \r
1469   /* Continue waiting for a clean startup. */\r
1470   if (deadline == WAIT_TIMEOUT) {\r
1471     if (service->throttle_delay > delay) {\r
1472       if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
1473     }\r
1474     else service->throttle = 0;\r
1475   }\r
1476 \r
1477   /* Ensure the restart delay is always applied. */\r
1478   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1479 \r
1480   return 0;\r
1481 }\r
1482 \r
1483 /* Stop the service */\r
1484 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1485   service->allow_restart = false;\r
1486   if (service->wait_handle) {\r
1487     UnregisterWait(service->wait_handle);\r
1488     service->wait_handle = 0;\r
1489   }\r
1490 \r
1491   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1492 \r
1493   if (default_action && ! exitcode && ! graceful) {\r
1494     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
1495     graceful = true;\r
1496   }\r
1497 \r
1498   /* Signal we are stopping */\r
1499   if (graceful) {\r
1500     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1501     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1502     SetServiceStatus(service->status_handle, &service->status);\r
1503   }\r
1504 \r
1505   /* Nothing to do if service isn't running */\r
1506   if (service->pid) {\r
1507     /* Shut down service */\r
1508     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1509     kill_process(service, service->process_handle, service->pid, 0);\r
1510   }\r
1511   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1512 \r
1513   end_service((void *) service, true);\r
1514 \r
1515   /* Signal we stopped */\r
1516   if (graceful) {\r
1517     service->status.dwCurrentState = SERVICE_STOPPED;\r
1518     if (exitcode) {\r
1519       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1520       service->status.dwServiceSpecificExitCode = exitcode;\r
1521     }\r
1522     else {\r
1523       service->status.dwWin32ExitCode = NO_ERROR;\r
1524       service->status.dwServiceSpecificExitCode = 0;\r
1525     }\r
1526     SetServiceStatus(service->status_handle, &service->status);\r
1527   }\r
1528 \r
1529   return exitcode;\r
1530 }\r
1531 \r
1532 /* Callback function triggered when the server exits */\r
1533 void CALLBACK end_service(void *arg, unsigned char why) {\r
1534   nssm_service_t *service = (nssm_service_t *) arg;\r
1535 \r
1536   if (service->stopping) return;\r
1537 \r
1538   service->stopping = true;\r
1539 \r
1540   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1541 \r
1542   /* Use now as a dummy exit time. */\r
1543   GetSystemTimeAsFileTime(&service->exit_time);\r
1544 \r
1545   /* Check exit code */\r
1546   unsigned long exitcode = 0;\r
1547   TCHAR code[16];\r
1548   if (service->process_handle) {\r
1549     GetExitCodeProcess(service->process_handle, &exitcode);\r
1550     /* Check real exit time. */\r
1551     if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
1552     CloseHandle(service->process_handle);\r
1553   }\r
1554 \r
1555   service->process_handle = 0;\r
1556 \r
1557   /*\r
1558     Log that the service ended BEFORE logging about killing the process\r
1559     tree.  See below for the possible values of the why argument.\r
1560   */\r
1561   if (! why) {\r
1562     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1563     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1564   }\r
1565 \r
1566   /* Clean up. */\r
1567   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1568   if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
1569   service->pid = 0;\r
1570 \r
1571   /*\r
1572     The why argument is true if our wait timed out or false otherwise.\r
1573     Our wait is infinite so why will never be true when called by the system.\r
1574     If it is indeed true, assume we were called from stop_service() because\r
1575     this is a controlled shutdown, and don't take any restart action.\r
1576   */\r
1577   if (why) return;\r
1578   if (! service->allow_restart) return;\r
1579 \r
1580   /* What action should we take? */\r
1581   int action = NSSM_EXIT_RESTART;\r
1582   TCHAR action_string[ACTION_LEN];\r
1583   bool default_action;\r
1584   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1585     for (int i = 0; exit_action_strings[i]; i++) {\r
1586       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1587         action = i;\r
1588         break;\r
1589       }\r
1590     }\r
1591   }\r
1592 \r
1593   switch (action) {\r
1594     /* Try to restart the service or return failure code to service manager */\r
1595     case NSSM_EXIT_RESTART:\r
1596       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1597       while (monitor_service(service)) {\r
1598         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1599         Sleep(30000);\r
1600       }\r
1601     break;\r
1602 \r
1603     /* Do nothing, just like srvany would */\r
1604     case NSSM_EXIT_IGNORE:\r
1605       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1606       Sleep(INFINITE);\r
1607     break;\r
1608 \r
1609     /* Tell the service manager we are finished */\r
1610     case NSSM_EXIT_REALLY:\r
1611       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1612       stop_service(service, exitcode, true, default_action);\r
1613     break;\r
1614 \r
1615     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1616     case NSSM_EXIT_UNCLEAN:\r
1617       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1618       stop_service(service, exitcode, false, default_action);\r
1619       free_imports();\r
1620       exit(exitcode);\r
1621     break;\r
1622   }\r
1623 }\r
1624 \r
1625 void throttle_restart(nssm_service_t *service) {\r
1626   /* This can't be a restart if the service is already running. */\r
1627   if (! service->throttle++) return;\r
1628 \r
1629   unsigned long ms;\r
1630   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1631   TCHAR threshold[8], milliseconds[8];\r
1632 \r
1633   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1634   else ms = throttle_ms;\r
1635 \r
1636   if (service->throttle > 7) service->throttle = 8;\r
1637 \r
1638   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1639 \r
1640   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
1641   else {\r
1642     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
1643     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
1644   }\r
1645 \r
1646   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
1647   else if (service->throttle_timer) {\r
1648     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1649     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
1650     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1651   }\r
1652 \r
1653   service->status.dwCurrentState = SERVICE_PAUSED;\r
1654   SetServiceStatus(service->status_handle, &service->status);\r
1655 \r
1656   if (use_critical_section) {\r
1657     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
1658     LeaveCriticalSection(&service->throttle_section);\r
1659   }\r
1660   else {\r
1661     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
1662     else Sleep(ms);\r
1663   }\r
1664 }\r
1665 \r
1666 /*\r
1667   When responding to a stop (or any other) request we need to set dwWaitHint to\r
1668   the number of milliseconds we expect the operation to take, and optionally\r
1669   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
1670   operation completing or dwCheckPoint increasing, the system will consider the\r
1671   service to be hung.\r
1672 \r
1673   However the system will consider the service to be hung after 30000\r
1674   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
1675   changed.  Therefore if we want to wait longer than that we must periodically\r
1676   increase dwCheckPoint.\r
1677 \r
1678   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
1679   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
1680   time dwCheckPoint is also increased.\r
1681 \r
1682   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
1683   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
1684   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
1685   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
1686   dwWaitHint.\r
1687 \r
1688   Only doing both these things will prevent the system from killing the service.\r
1689 \r
1690   Returns: 1 if the wait timed out.\r
1691            0 if the wait completed.\r
1692           -1 on error.\r
1693 */\r
1694 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
1695   unsigned long interval;\r
1696   unsigned long waithint;\r
1697   unsigned long ret;\r
1698   unsigned long waited;\r
1699   TCHAR interval_milliseconds[16];\r
1700   TCHAR timeout_milliseconds[16];\r
1701   TCHAR waited_milliseconds[16];\r
1702   TCHAR *function = function_name;\r
1703 \r
1704   /* Add brackets to function name. */\r
1705   size_t funclen = _tcslen(function_name) + 3;\r
1706   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
1707   if (func) {\r
1708     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
1709   }\r
1710 \r
1711   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
1712 \r
1713   waithint = service->status.dwWaitHint;\r
1714   waited = 0;\r
1715   while (waited < timeout) {\r
1716     interval = timeout - waited;\r
1717     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
1718 \r
1719     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1720     service->status.dwWaitHint += interval;\r
1721     service->status.dwCheckPoint++;\r
1722     SetServiceStatus(service->status_handle, &service->status);\r
1723 \r
1724     if (waited) {\r
1725       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
1726       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
1727       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
1728     }\r
1729 \r
1730     switch (WaitForSingleObject(service->process_handle, interval)) {\r
1731       case WAIT_OBJECT_0:\r
1732         ret = 0;\r
1733         goto awaited;\r
1734 \r
1735       case WAIT_TIMEOUT:\r
1736         ret = 1;\r
1737       break;\r
1738 \r
1739       default:\r
1740         ret = -1;\r
1741         goto awaited;\r
1742     }\r
1743 \r
1744     waited += interval;\r
1745   }\r
1746 \r
1747 awaited:\r
1748   if (func) HeapFree(GetProcessHeap(), 0, func);\r
1749 \r
1750   return ret;\r
1751 }\r