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