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