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