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