9cdf9dedd301246632b5a32252e5d44b33be1d06
[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->affinity) flags |= CREATE_SUSPENDED;\r
1526 #ifdef UNICODE\r
1527   flags |= CREATE_UNICODE_ENVIRONMENT;\r
1528 #endif\r
1529   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {\r
1530     unsigned long exitcode = 3;\r
1531     unsigned long error = GetLastError();\r
1532     if (error == ERROR_INVALID_PARAMETER && service->env) {\r
1533       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);\r
1534       if (test_environment(service->env)) exitcode = 4;\r
1535     }\r
1536     else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1537     close_output_handles(&si);\r
1538     return stop_service(service, exitcode, true, true);\r
1539   }\r
1540   service->process_handle = pi.hProcess;\r
1541   service->pid = pi.dwProcessId;\r
1542 \r
1543   if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1544 \r
1545   close_output_handles(&si);\r
1546 \r
1547   if (service->affinity) {\r
1548     /*\r
1549       We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1550       so that we can parse it regardless of whether we're running in 32-bit\r
1551       or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1552       defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1553       (or when running the 32-bit NSSM).\r
1554 \r
1555       The result is a lot of seemingly-unnecessary casting throughout the code\r
1556       and potentially confusion when we actually try to start the service.\r
1557       Having said that, however, it's unlikely that we're actually going to\r
1558       run in 32-bit mode on a system which has more than 32 CPUs so the\r
1559       likelihood of seeing a confusing situation is somewhat diminished.\r
1560     */\r
1561     DWORD_PTR affinity, system_affinity;\r
1562 \r
1563     if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1564     else {\r
1565       affinity = (DWORD_PTR) service->affinity;\r
1566       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1567     }\r
1568 \r
1569     if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1570       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1571     }\r
1572 \r
1573     ResumeThread(pi.hThread);\r
1574   }\r
1575 \r
1576   /*\r
1577     Wait for a clean startup before changing the service status to RUNNING\r
1578     but be mindful of the fact that we are blocking the service control manager\r
1579     so abandon the wait before too much time has elapsed.\r
1580   */\r
1581   unsigned long delay = service->throttle_delay;\r
1582   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
1583     TCHAR delay_milliseconds[16];\r
1584     _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
1585     TCHAR deadline_milliseconds[16];\r
1586     _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
1587     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
1588     delay = NSSM_SERVICE_STATUS_DEADLINE;\r
1589   }\r
1590   unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
1591 \r
1592   /* Signal successful start */\r
1593   service->status.dwCurrentState = SERVICE_RUNNING;\r
1594   SetServiceStatus(service->status_handle, &service->status);\r
1595 \r
1596   /* Continue waiting for a clean startup. */\r
1597   if (deadline == WAIT_TIMEOUT) {\r
1598     if (service->throttle_delay > delay) {\r
1599       if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
1600     }\r
1601     else service->throttle = 0;\r
1602   }\r
1603 \r
1604   /* Ensure the restart delay is always applied. */\r
1605   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1606 \r
1607   return 0;\r
1608 }\r
1609 \r
1610 /* Stop the service */\r
1611 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1612   service->allow_restart = false;\r
1613   if (service->wait_handle) {\r
1614     UnregisterWait(service->wait_handle);\r
1615     service->wait_handle = 0;\r
1616   }\r
1617 \r
1618   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1619 \r
1620   if (default_action && ! exitcode && ! graceful) {\r
1621     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
1622     graceful = true;\r
1623   }\r
1624 \r
1625   /* Signal we are stopping */\r
1626   if (graceful) {\r
1627     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1628     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1629     SetServiceStatus(service->status_handle, &service->status);\r
1630   }\r
1631 \r
1632   /* Nothing to do if service isn't running */\r
1633   if (service->pid) {\r
1634     /* Shut down service */\r
1635     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1636     kill_process(service, service->process_handle, service->pid, 0);\r
1637   }\r
1638   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1639 \r
1640   end_service((void *) service, true);\r
1641 \r
1642   /* Signal we stopped */\r
1643   if (graceful) {\r
1644     service->status.dwCurrentState = SERVICE_STOPPED;\r
1645     if (exitcode) {\r
1646       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1647       service->status.dwServiceSpecificExitCode = exitcode;\r
1648     }\r
1649     else {\r
1650       service->status.dwWin32ExitCode = NO_ERROR;\r
1651       service->status.dwServiceSpecificExitCode = 0;\r
1652     }\r
1653     SetServiceStatus(service->status_handle, &service->status);\r
1654   }\r
1655 \r
1656   return exitcode;\r
1657 }\r
1658 \r
1659 /* Callback function triggered when the server exits */\r
1660 void CALLBACK end_service(void *arg, unsigned char why) {\r
1661   nssm_service_t *service = (nssm_service_t *) arg;\r
1662 \r
1663   if (service->stopping) return;\r
1664 \r
1665   service->stopping = true;\r
1666 \r
1667   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1668 \r
1669   /* Check exit code */\r
1670   unsigned long exitcode = 0;\r
1671   TCHAR code[16];\r
1672   if (service->process_handle) {\r
1673     GetExitCodeProcess(service->process_handle, &exitcode);\r
1674     if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);\r
1675     CloseHandle(service->process_handle);\r
1676   }\r
1677   else GetSystemTimeAsFileTime(&service->exit_time);\r
1678 \r
1679   service->process_handle = 0;\r
1680 \r
1681   /*\r
1682     Log that the service ended BEFORE logging about killing the process\r
1683     tree.  See below for the possible values of the why argument.\r
1684   */\r
1685   if (! why) {\r
1686     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1687     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1688   }\r
1689 \r
1690   /* Clean up. */\r
1691   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1692   if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
1693   service->pid = 0;\r
1694 \r
1695   /*\r
1696     The why argument is true if our wait timed out or false otherwise.\r
1697     Our wait is infinite so why will never be true when called by the system.\r
1698     If it is indeed true, assume we were called from stop_service() because\r
1699     this is a controlled shutdown, and don't take any restart action.\r
1700   */\r
1701   if (why) return;\r
1702   if (! service->allow_restart) return;\r
1703 \r
1704   /* What action should we take? */\r
1705   int action = NSSM_EXIT_RESTART;\r
1706   TCHAR action_string[ACTION_LEN];\r
1707   bool default_action;\r
1708   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1709     for (int i = 0; exit_action_strings[i]; i++) {\r
1710       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1711         action = i;\r
1712         break;\r
1713       }\r
1714     }\r
1715   }\r
1716 \r
1717   switch (action) {\r
1718     /* Try to restart the service or return failure code to service manager */\r
1719     case NSSM_EXIT_RESTART:\r
1720       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1721       while (monitor_service(service)) {\r
1722         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1723         Sleep(30000);\r
1724       }\r
1725     break;\r
1726 \r
1727     /* Do nothing, just like srvany would */\r
1728     case NSSM_EXIT_IGNORE:\r
1729       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1730       Sleep(INFINITE);\r
1731     break;\r
1732 \r
1733     /* Tell the service manager we are finished */\r
1734     case NSSM_EXIT_REALLY:\r
1735       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1736       stop_service(service, exitcode, true, default_action);\r
1737     break;\r
1738 \r
1739     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1740     case NSSM_EXIT_UNCLEAN:\r
1741       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1742       stop_service(service, exitcode, false, default_action);\r
1743       free_imports();\r
1744       exit(exitcode);\r
1745     break;\r
1746   }\r
1747 }\r
1748 \r
1749 void throttle_restart(nssm_service_t *service) {\r
1750   /* This can't be a restart if the service is already running. */\r
1751   if (! service->throttle++) return;\r
1752 \r
1753   unsigned long ms;\r
1754   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1755   TCHAR threshold[8], milliseconds[8];\r
1756 \r
1757   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1758   else ms = throttle_ms;\r
1759 \r
1760   if (service->throttle > 7) service->throttle = 8;\r
1761 \r
1762   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1763 \r
1764   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
1765   else {\r
1766     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
1767     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
1768   }\r
1769 \r
1770   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
1771   else if (service->throttle_timer) {\r
1772     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1773     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
1774     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1775   }\r
1776 \r
1777   service->status.dwCurrentState = SERVICE_PAUSED;\r
1778   SetServiceStatus(service->status_handle, &service->status);\r
1779 \r
1780   if (use_critical_section) {\r
1781     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
1782     LeaveCriticalSection(&service->throttle_section);\r
1783   }\r
1784   else {\r
1785     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
1786     else Sleep(ms);\r
1787   }\r
1788 }\r
1789 \r
1790 /*\r
1791   When responding to a stop (or any other) request we need to set dwWaitHint to\r
1792   the number of milliseconds we expect the operation to take, and optionally\r
1793   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
1794   operation completing or dwCheckPoint increasing, the system will consider the\r
1795   service to be hung.\r
1796 \r
1797   However the system will consider the service to be hung after 30000\r
1798   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
1799   changed.  Therefore if we want to wait longer than that we must periodically\r
1800   increase dwCheckPoint.\r
1801 \r
1802   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
1803   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
1804   time dwCheckPoint is also increased.\r
1805 \r
1806   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
1807   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
1808   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
1809   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
1810   dwWaitHint.\r
1811 \r
1812   Only doing both these things will prevent the system from killing the service.\r
1813 \r
1814   Returns: 1 if the wait timed out.\r
1815            0 if the wait completed.\r
1816           -1 on error.\r
1817 */\r
1818 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
1819   unsigned long interval;\r
1820   unsigned long waithint;\r
1821   unsigned long ret;\r
1822   unsigned long waited;\r
1823   TCHAR interval_milliseconds[16];\r
1824   TCHAR timeout_milliseconds[16];\r
1825   TCHAR waited_milliseconds[16];\r
1826   TCHAR *function = function_name;\r
1827 \r
1828   /* Add brackets to function name. */\r
1829   size_t funclen = _tcslen(function_name) + 3;\r
1830   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
1831   if (func) {\r
1832     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
1833   }\r
1834 \r
1835   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
1836 \r
1837   waithint = service->status.dwWaitHint;\r
1838   waited = 0;\r
1839   while (waited < timeout) {\r
1840     interval = timeout - waited;\r
1841     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
1842 \r
1843     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1844     service->status.dwWaitHint += interval;\r
1845     service->status.dwCheckPoint++;\r
1846     SetServiceStatus(service->status_handle, &service->status);\r
1847 \r
1848     if (waited) {\r
1849       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
1850       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
1851       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
1852     }\r
1853 \r
1854     switch (WaitForSingleObject(service->process_handle, interval)) {\r
1855       case WAIT_OBJECT_0:\r
1856         ret = 0;\r
1857         goto awaited;\r
1858 \r
1859       case WAIT_TIMEOUT:\r
1860         ret = 1;\r
1861       break;\r
1862 \r
1863       default:\r
1864         ret = -1;\r
1865         goto awaited;\r
1866     }\r
1867 \r
1868     waited += interval;\r
1869   }\r
1870 \r
1871 awaited:\r
1872   if (func) HeapFree(GetProcessHeap(), 0, func);\r
1873 \r
1874   return ret;\r
1875 }\r