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