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