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