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