Allow skipping kill_process_tree().
[nssm.git] / service.cpp
1 #include "nssm.h"\r
2 \r
3 bool is_admin;\r
4 bool use_critical_section;\r
5 \r
6 extern imports_t imports;\r
7 extern settings_t settings[];\r
8 \r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };\r
11 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
12 \r
13 typedef struct {\r
14   int first;\r
15   int last;\r
16 } list_t;\r
17 \r
18 /*\r
19   Check the status in response to a control.\r
20   Returns:  1 if the status is expected, eg STOP following CONTROL_STOP.\r
21             0 if the status is desired, eg STOPPED following CONTROL_STOP.\r
22            -1 if the status is undesired, eg STOPPED following CONTROL_START.\r
23 */\r
24 static inline int service_control_response(unsigned long control, unsigned long status) {\r
25   switch (control) {\r
26     case NSSM_SERVICE_CONTROL_START:\r
27       switch (status) {\r
28         case SERVICE_START_PENDING:\r
29           return 1;\r
30 \r
31         case SERVICE_RUNNING:\r
32           return 0;\r
33 \r
34         default:\r
35           return -1;\r
36       }\r
37 \r
38     case SERVICE_CONTROL_STOP:\r
39     case SERVICE_CONTROL_SHUTDOWN:\r
40       switch (status) {\r
41         case SERVICE_STOP_PENDING:\r
42           return 1;\r
43 \r
44         case SERVICE_STOPPED:\r
45           return 0;\r
46 \r
47         default:\r
48           return -1;\r
49       }\r
50 \r
51     case SERVICE_CONTROL_PAUSE:\r
52       switch (status) {\r
53         case SERVICE_PAUSE_PENDING:\r
54           return 1;\r
55 \r
56         case SERVICE_PAUSED:\r
57           return 0;\r
58 \r
59         default:\r
60           return -1;\r
61       }\r
62 \r
63     case SERVICE_CONTROL_CONTINUE:\r
64       switch (status) {\r
65         case SERVICE_CONTINUE_PENDING:\r
66           return 1;\r
67 \r
68         case SERVICE_RUNNING:\r
69           return 0;\r
70 \r
71         default:\r
72           return -1;\r
73       }\r
74 \r
75     case SERVICE_CONTROL_INTERROGATE:\r
76       return 0;\r
77   }\r
78 \r
79   return 0;\r
80 }\r
81 \r
82 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {\r
83   int tries = 0;\r
84   while (QueryServiceStatus(service_handle, service_status)) {\r
85     int response = service_control_response(control, service_status->dwCurrentState);\r
86     /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */\r
87     if (! response) return response;\r
88     if (response > 0 || service_status->dwCurrentState == initial_status) {\r
89       if (++tries > 10) return response;\r
90       Sleep(50 * tries);\r
91     }\r
92     else return response;\r
93   }\r
94   return -1;\r
95 }\r
96 \r
97 int affinity_mask_to_string(__int64 mask, TCHAR **string) {\r
98   if (! string) return 1;\r
99   if (! mask) {\r
100     *string = 0;\r
101     return 0;\r
102   }\r
103 \r
104   __int64 i, n;\r
105 \r
106   /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */\r
107   list_t set[64];\r
108   for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;\r
109 \r
110   for (i = 0, n = 0; i < _countof(set); i++) {\r
111     if (mask & (1LL << i)) {\r
112       if (set[n].first == -1) set[n].first = set[n].last = (int) i;\r
113       else if (set[n].last == (int) i - 1) set[n].last = (int) i;\r
114       else {\r
115         n++;\r
116         set[n].first = set[n].last = (int) i;\r
117       }\r
118     }\r
119   }\r
120 \r
121   /* Worst case is 2x2 characters for first and last CPU plus - and/or , */\r
122   size_t len = (size_t) (n + 1) * 6;\r
123   *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));\r
124   if (! string) return 2;\r
125 \r
126   size_t s = 0;\r
127   int ret;\r
128   for (i = 0; i <= n; i++) {\r
129     if (i) (*string)[s++] = _T(',');\r
130     ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);\r
131     if (ret < 0) {\r
132       HeapFree(GetProcessHeap(), 0, *string);\r
133       *string = 0;\r
134       return 3;\r
135     }\r
136     else s += ret;\r
137     if (set[i].last != set[i].first) {\r
138       ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);\r
139       if (ret < 0) {\r
140         HeapFree(GetProcessHeap(), 0, *string);\r
141         *string = 0;\r
142         return 4;\r
143       }\r
144       else s += ret;\r
145     }\r
146   }\r
147 \r
148   return 0;\r
149 }\r
150 \r
151 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {\r
152   if (! mask) return 1;\r
153 \r
154   *mask = 0LL;\r
155   if (! string) return 0;\r
156 \r
157   list_t set[64];\r
158 \r
159   TCHAR *s = string;\r
160   TCHAR *end;\r
161   int ret;\r
162   int i;\r
163   int n = 0;\r
164   unsigned long number;\r
165 \r
166   for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;\r
167   n = 0;\r
168 \r
169   while (*s) {\r
170     ret = str_number(s, &number, &end);\r
171     s = end;\r
172     if (ret == 0 || ret == 2) {\r
173       if (number >= _countof(set)) return 2;\r
174       set[n].first = set[n].last = (int) number;\r
175 \r
176       switch (*s) {\r
177         case 0:\r
178           break;\r
179 \r
180         case _T(','):\r
181           n++;\r
182           s++;\r
183           break;\r
184 \r
185         case _T('-'):\r
186           if (! *(++s)) return 3;\r
187           ret = str_number(s, &number, &end);\r
188           if (ret == 0 || ret == 2) {\r
189             s = end;\r
190             if (! *s || *s == _T(',')) {\r
191               set[n].last = (int) number;\r
192               if (! *s) break;\r
193               n++;\r
194               s++;\r
195             }\r
196             else return 3;\r
197           }\r
198           else return 3;\r
199           break;\r
200 \r
201         default:\r
202           return 3;\r
203       }\r
204     }\r
205     else return 4;\r
206   }\r
207 \r
208   for (i = 0; i <= n; i++) {\r
209     for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);\r
210   }\r
211 \r
212   return 0;\r
213 }\r
214 \r
215 inline unsigned long priority_mask() {\r
216  return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;\r
217 }\r
218 \r
219 int priority_constant_to_index(unsigned long constant) {\r
220   switch (constant & priority_mask()) {\r
221     case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;\r
222     case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;\r
223     case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;\r
224     case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;\r
225     case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;\r
226   }\r
227   return NSSM_NORMAL_PRIORITY;\r
228 }\r
229 \r
230 unsigned long priority_index_to_constant(int index) {\r
231   switch (index) {\r
232     case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;\r
233     case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;\r
234     case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;\r
235     case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;\r
236     case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;\r
237   }\r
238   return NORMAL_PRIORITY_CLASS;\r
239 }\r
240 \r
241 static inline unsigned long throttle_milliseconds(unsigned long throttle) {\r
242   /* pow() operates on doubles. */\r
243   unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;\r
244   return ret * 1000;\r
245 }\r
246 \r
247 /*\r
248   Wrapper to be called in a new thread so that we can acknowledge a STOP\r
249   control immediately.\r
250 */\r
251 static unsigned long WINAPI shutdown_service(void *arg) {\r
252   return stop_service((nssm_service_t *) arg, 0, true, true);\r
253 }\r
254 \r
255 /* Connect to the service manager */\r
256 SC_HANDLE open_service_manager(unsigned long access) {\r
257   SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);\r
258   if (! ret) {\r
259     if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);\r
260     return 0;\r
261   }\r
262 \r
263   return ret;\r
264 }\r
265 \r
266 /* Open a service by name or display name. */\r
267 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {\r
268   SC_HANDLE service_handle = OpenService(services, service_name, access);\r
269   if (service_handle) {\r
270     if (canonical_name && canonical_name != service_name) {\r
271       TCHAR displayname[SERVICE_NAME_LENGTH];\r
272       unsigned long displayname_len = (unsigned long) _countof(displayname);\r
273       GetServiceDisplayName(services, service_name, displayname, &displayname_len);\r
274       unsigned long keyname_len = canonical_namelen;\r
275       GetServiceKeyName(services, displayname, canonical_name, &keyname_len);\r
276     }\r
277     return service_handle;\r
278   }\r
279 \r
280   unsigned long error = GetLastError();\r
281   if (error != ERROR_SERVICE_DOES_NOT_EXIST) {\r
282     print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));\r
283     return 0;\r
284   }\r
285 \r
286   /* We can't look for a display name because there's no buffer to store it. */\r
287   if (! canonical_name) {\r
288     print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));\r
289     return 0;\r
290   }\r
291 \r
292   unsigned long bufsize, required, count, i;\r
293   unsigned long resume = 0;\r
294   EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);\r
295   error = GetLastError();\r
296   if (error != ERROR_MORE_DATA) {\r
297     print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));\r
298     return 0;\r
299   }\r
300 \r
301   ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);\r
302   if (! status) {\r
303     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));\r
304     return 0;\r
305   }\r
306 \r
307   bufsize = required;\r
308   while (true) {\r
309     /*\r
310       EnumServicesStatus() returns:\r
311       1 when it retrieved data and there's no more data to come.\r
312       0 and sets last error to ERROR_MORE_DATA when it retrieved data and\r
313         there's more data to come.\r
314       0 and sets last error to something else on error.\r
315     */\r
316     int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);\r
317     if (! ret) {\r
318       error = GetLastError();\r
319       if (error != ERROR_MORE_DATA) {\r
320         HeapFree(GetProcessHeap(), 0, status);\r
321         print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));\r
322         return 0;\r
323       }\r
324     }\r
325 \r
326     for (i = 0; i < count; i++) {\r
327       if (str_equiv(status[i].lpDisplayName, service_name)) {\r
328         if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {\r
329           HeapFree(GetProcessHeap(), 0, status);\r
330           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));\r
331           return 0;\r
332         }\r
333 \r
334         HeapFree(GetProcessHeap(), 0, status);\r
335         return open_service(services, canonical_name, access, 0, 0);\r
336       }\r
337     }\r
338 \r
339     if (ret) break;\r
340   }\r
341 \r
342   /* Recurse so we can get an error message. */\r
343   return open_service(services, service_name, access, 0, 0);\r
344 }\r
345 \r
346 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {\r
347   QUERY_SERVICE_CONFIG *qsc;\r
348   unsigned long bufsize;\r
349   unsigned long error;\r
350 \r
351   QueryServiceConfig(service_handle, 0, 0, &bufsize);\r
352   error = GetLastError();\r
353   if (error == ERROR_INSUFFICIENT_BUFFER) {\r
354     qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);\r
355     if (! qsc) {\r
356       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);\r
357       return 0;\r
358     }\r
359   }\r
360   else {\r
361     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);\r
362     return 0;\r
363   }\r
364 \r
365   if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {\r
366     HeapFree(GetProcessHeap(), 0, qsc);\r
367     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);\r
368     return 0;\r
369   }\r
370 \r
371   return qsc;\r
372 }\r
373 \r
374 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {\r
375   TCHAR *dependencies = _T("");\r
376   unsigned long num_dependencies = 0;\r
377 \r
378   if (buffer && buffer[0]) {\r
379     SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
380     if (! services) {\r
381       print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
382       return 1;\r
383     }\r
384 \r
385     /*\r
386       Count the dependencies then allocate a buffer big enough for their\r
387       canonical names, ie n * SERVICE_NAME_LENGTH.\r
388     */\r
389     TCHAR *s;\r
390     TCHAR *groups = 0;\r
391     for (s = buffer; *s; s++) {\r
392       num_dependencies++;\r
393       if (*s == SC_GROUP_IDENTIFIER) groups = s;\r
394       while (*s) s++;\r
395     }\r
396 \r
397     /* At least one dependency is a group so we need to verify them. */\r
398     if (groups) {\r
399       HKEY key;\r
400       if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {\r
401         _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));\r
402         return 2;\r
403       }\r
404 \r
405       unsigned long type;\r
406       unsigned long groupslen;\r
407       unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);\r
408       if (ret == ERROR_SUCCESS) {\r
409         groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);\r
410         if (! groups) {\r
411           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));\r
412           return 3;\r
413         }\r
414 \r
415         ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);\r
416         if (ret != ERROR_SUCCESS) {\r
417           _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));\r
418           HeapFree(GetProcessHeap(), 0, groups);\r
419           RegCloseKey(key);\r
420           return 4;\r
421         }\r
422       }\r
423       else if (ret != ERROR_FILE_NOT_FOUND) {\r
424         _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));\r
425         RegCloseKey(key);\r
426         return 4;\r
427       }\r
428 \r
429       RegCloseKey(key);\r
430 \r
431     }\r
432 \r
433     unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;\r
434     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));\r
435     size_t i = 0;\r
436 \r
437     TCHAR dependency[SERVICE_NAME_LENGTH];\r
438     for (s = buffer; *s; s++) {\r
439       /* Group? */\r
440       if (*s == SC_GROUP_IDENTIFIER) {\r
441         TCHAR *group = s + 1;\r
442 \r
443         bool ok = false;\r
444         if (*group) {\r
445           for (TCHAR *g = groups; *g; g++) {\r
446             if (str_equiv(g, group)) {\r
447               ok = true;\r
448               /* Set canonical name. */\r
449               memmove(group, g, _tcslen(g) * sizeof(TCHAR));\r
450               break;\r
451             }\r
452 \r
453             while (*g) g++;\r
454           }\r
455         }\r
456 \r
457         if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);\r
458         else {\r
459           HeapFree(GetProcessHeap(), 0, dependencies);\r
460           if (groups) HeapFree(GetProcessHeap(), 0, groups);\r
461           _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));\r
462           return 5;\r
463         }\r
464       }\r
465       else {\r
466         SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));\r
467         if (! dependency_handle) {\r
468           HeapFree(GetProcessHeap(), 0, dependencies);\r
469           if (groups) HeapFree(GetProcessHeap(), 0, groups);\r
470           CloseServiceHandle(services);\r
471           _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));\r
472           return 5;\r
473         }\r
474       }\r
475 \r
476       size_t len = _tcslen(dependency) + 1;\r
477       memmove(dependencies + i, dependency, len * sizeof(TCHAR));\r
478       i += len;\r
479 \r
480       while (*s) s++;\r
481     }\r
482 \r
483     if (groups) HeapFree(GetProcessHeap(), 0, groups);\r
484     CloseServiceHandle(services);\r
485   }\r
486 \r
487   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {\r
488     if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);\r
489     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
490     return -1;\r
491   }\r
492 \r
493   if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);\r
494   return 0;\r
495 }\r
496 \r
497 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {\r
498   if (! buffer) return 1;\r
499   if (! bufsize) return 2;\r
500 \r
501   *buffer = 0;\r
502   *bufsize = 0;\r
503 \r
504   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
505   if (! qsc) return 3;\r
506 \r
507   if (! qsc->lpDependencies) return 0;\r
508   if (! qsc->lpDependencies[0]) return 0;\r
509 \r
510   /* lpDependencies is doubly NULL terminated. */\r
511   while (qsc->lpDependencies[*bufsize]) {\r
512     while (qsc->lpDependencies[*bufsize]) ++*bufsize;\r
513     ++*bufsize;\r
514   }\r
515 \r
516   *bufsize += 2;\r
517 \r
518   *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));\r
519   if (! *buffer) {\r
520     *bufsize = 0;\r
521     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));\r
522     return 4;\r
523   }\r
524 \r
525   if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));\r
526   else {\r
527     TCHAR *s;\r
528     size_t i = 0;\r
529     *bufsize = 0;\r
530     for (s = qsc->lpDependencies; *s; s++) {\r
531       /* Only copy the appropriate type of dependency. */\r
532       if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {\r
533         size_t len = _tcslen(s) + 1;\r
534         *bufsize += (unsigned long) len;\r
535         memmove(*buffer + i, s, len * sizeof(TCHAR));\r
536         i += len;\r
537       }\r
538 \r
539       while (*s) s++;\r
540     }\r
541     ++*bufsize;\r
542   }\r
543 \r
544   HeapFree(GetProcessHeap(), 0, qsc);\r
545 \r
546   return 0;\r
547 }\r
548 \r
549 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {\r
550   return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);\r
551 }\r
552 \r
553 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {\r
554   SERVICE_DESCRIPTION description;\r
555   ZeroMemory(&description, sizeof(description));\r
556   /*\r
557     lpDescription must be NULL if we aren't changing, the new description\r
558     or "".\r
559   */\r
560   if (buffer && buffer[0]) description.lpDescription = buffer;\r
561   else description.lpDescription = _T("");\r
562 \r
563   if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;\r
564 \r
565   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);\r
566   return 1;\r
567 }\r
568 \r
569 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {\r
570   if (! buffer) return 1;\r
571 \r
572   unsigned long bufsize;\r
573   QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);\r
574   unsigned long error = GetLastError();\r
575   if (error == ERROR_INSUFFICIENT_BUFFER) {\r
576     SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);\r
577     if (! description) {\r
578       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));\r
579       return 2;\r
580     }\r
581 \r
582     if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {\r
583       if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);\r
584       else ZeroMemory(buffer, len * sizeof(TCHAR));\r
585       HeapFree(GetProcessHeap(), 0, description);\r
586       return 0;\r
587     }\r
588     else {\r
589       HeapFree(GetProcessHeap(), 0, description);\r
590       print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));\r
591       return 3;\r
592     }\r
593   }\r
594   else {\r
595     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));\r
596     return 4;\r
597   }\r
598 \r
599   return 0;\r
600 }\r
601 \r
602 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {\r
603   if (! qsc) return 1;\r
604 \r
605   switch (qsc->dwStartType) {\r
606     case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;\r
607     case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;\r
608     default: *startup = NSSM_STARTUP_AUTOMATIC;\r
609   }\r
610 \r
611   if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;\r
612 \r
613   /* Check for delayed start. */\r
614   unsigned long bufsize;\r
615   unsigned long error;\r
616   QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);\r
617   error = GetLastError();\r
618   if (error == ERROR_INSUFFICIENT_BUFFER) {\r
619     SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);\r
620     if (! info) {\r
621       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));\r
622       return 2;\r
623     }\r
624 \r
625     if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {\r
626       if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;\r
627       HeapFree(GetProcessHeap(), 0, info);\r
628       return 0;\r
629     }\r
630     else {\r
631       error = GetLastError();\r
632       if (error != ERROR_INVALID_LEVEL) {\r
633         print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));\r
634         return 3;\r
635       }\r
636     }\r
637   }\r
638   else if (error != ERROR_INVALID_LEVEL) {\r
639     print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));\r
640     return 3;\r
641   }\r
642 \r
643   return 0;\r
644 }\r
645 \r
646 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {\r
647   if (! username) return 1;\r
648   if (! usernamelen) return 1;\r
649 \r
650   *username = 0;\r
651   *usernamelen = 0;\r
652 \r
653   if (! qsc) return 1;\r
654 \r
655   if (qsc->lpServiceStartName[0]) {\r
656     if (is_localsystem(qsc->lpServiceStartName)) return 0;\r
657 \r
658     size_t len = _tcslen(qsc->lpServiceStartName);\r
659     *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));\r
660     if (! *username) {\r
661       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));\r
662       return 2;\r
663     }\r
664 \r
665     memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));\r
666     *usernamelen = len;\r
667   }\r
668 \r
669   return 0;\r
670 }\r
671 \r
672 /* Set default values which aren't zero. */\r
673 void set_nssm_service_defaults(nssm_service_t *service) {\r
674   if (! service) return;\r
675 \r
676   service->type = SERVICE_WIN32_OWN_PROCESS;\r
677   service->priority = NORMAL_PRIORITY_CLASS;\r
678   service->stdin_sharing = NSSM_STDIN_SHARING;\r
679   service->stdin_disposition = NSSM_STDIN_DISPOSITION;\r
680   service->stdin_flags = NSSM_STDIN_FLAGS;\r
681   service->stdout_sharing = NSSM_STDOUT_SHARING;\r
682   service->stdout_disposition = NSSM_STDOUT_DISPOSITION;\r
683   service->stdout_flags = NSSM_STDOUT_FLAGS;\r
684   service->stderr_sharing = NSSM_STDERR_SHARING;\r
685   service->stderr_disposition = NSSM_STDERR_DISPOSITION;\r
686   service->stderr_flags = NSSM_STDERR_FLAGS;\r
687   service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;\r
688   service->stop_method = ~0;\r
689   service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
690   service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
691   service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
692   service->kill_process_tree = 1;\r
693 }\r
694 \r
695 /* Allocate and zero memory for a service. */\r
696 nssm_service_t *alloc_nssm_service() {\r
697   nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));\r
698   if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);\r
699   return service;\r
700 }\r
701 \r
702 /* Free memory for a service. */\r
703 void cleanup_nssm_service(nssm_service_t *service) {\r
704   if (! service) return;\r
705   if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
706   if (service->password) {\r
707     SecureZeroMemory(service->password, service->passwordlen);\r
708     HeapFree(GetProcessHeap(), 0, service->password);\r
709   }\r
710   if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);\r
711   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
712   if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);\r
713   if (service->handle) CloseServiceHandle(service->handle);\r
714   if (service->process_handle) CloseHandle(service->process_handle);\r
715   if (service->wait_handle) UnregisterWait(service->wait_handle);\r
716   if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);\r
717   if (service->throttle_timer) CloseHandle(service->throttle_timer);\r
718   if (service->initial_env) FreeEnvironmentStrings(service->initial_env);\r
719   HeapFree(GetProcessHeap(), 0, service);\r
720 }\r
721 \r
722 /* About to install the service */\r
723 int pre_install_service(int argc, TCHAR **argv) {\r
724   nssm_service_t *service = alloc_nssm_service();\r
725   set_nssm_service_defaults(service);\r
726   if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);\r
727 \r
728   /* Show the dialogue box if we didn't give the service name and path */\r
729   if (argc < 2) return nssm_gui(IDD_INSTALL, service);\r
730 \r
731   if (! service) {\r
732     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));\r
733     return 1;\r
734   }\r
735   _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);\r
736 \r
737   /* Arguments are optional */\r
738   size_t flagslen = 0;\r
739   size_t s = 0;\r
740   int i;\r
741   for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;\r
742   if (! flagslen) flagslen = 1;\r
743   if (flagslen > _countof(service->flags)) {\r
744     print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);\r
745     return 2;\r
746   }\r
747 \r
748   for (i = 2; i < argc; i++) {\r
749     size_t len = _tcslen(argv[i]);\r
750     memmove(service->flags + s, argv[i], len * sizeof(TCHAR));\r
751     s += len;\r
752     if (i < argc - 1) service->flags[s++] = _T(' ');\r
753   }\r
754 \r
755   /* Work out directory name */\r
756   _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
757   strip_basename(service->dir);\r
758 \r
759   int ret = install_service(service);\r
760   cleanup_nssm_service(service);\r
761   return ret;\r
762 }\r
763 \r
764 /* About to edit the service. */\r
765 int pre_edit_service(int argc, TCHAR **argv) {\r
766   /* Require service name. */\r
767   if (argc < 2) return usage(1);\r
768 \r
769   /* Are we editing on the command line? */\r
770   enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;\r
771   const TCHAR *verb = argv[0];\r
772   const TCHAR *service_name = argv[1];\r
773   bool getting = false;\r
774   bool unsetting = false;\r
775 \r
776   /* Minimum number of arguments. */\r
777   int mandatory = 2;\r
778   /* Index of first value. */\r
779   int remainder = 3;\r
780   int i;\r
781   if (str_equiv(verb, _T("get"))) {\r
782     mandatory = 3;\r
783     mode = MODE_GETTING;\r
784   }\r
785   else if (str_equiv(verb, _T("set"))) {\r
786     mandatory = 4;\r
787     mode = MODE_SETTING;\r
788   }\r
789   else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {\r
790     mandatory = 3;\r
791     mode = MODE_RESETTING;\r
792   }\r
793   if (argc < mandatory) return usage(1);\r
794 \r
795   const TCHAR *parameter = 0;\r
796   settings_t *setting = 0;\r
797   TCHAR *additional;\r
798 \r
799   /* Validate the parameter. */\r
800   if (mandatory > 2) {\r
801     bool additional_mandatory = false;\r
802 \r
803     parameter = argv[2];\r
804     for (i = 0; settings[i].name; i++) {\r
805       setting = &settings[i];\r
806       if (! str_equiv(setting->name, parameter)) continue;\r
807       if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {\r
808         additional_mandatory = true;\r
809         mandatory++;\r
810       }\r
811       break;\r
812     }\r
813     if (! settings[i].name) {\r
814       print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);\r
815       for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);\r
816       return 1;\r
817     }\r
818 \r
819     additional = 0;\r
820     if (additional_mandatory) {\r
821       if (argc < mandatory) {\r
822         print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);\r
823         return 1;\r
824       }\r
825       additional = argv[3];\r
826       remainder = 4;\r
827     }\r
828     else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {\r
829       additional = argv[3];\r
830       remainder = 4;\r
831     }\r
832     else {\r
833       additional = argv[remainder];\r
834       if (argc < mandatory) return usage(1);\r
835     }\r
836   }\r
837 \r
838   nssm_service_t *service = alloc_nssm_service();\r
839   _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);\r
840 \r
841   /* Open service manager */\r
842   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
843   if (! services) {\r
844     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
845     return 2;\r
846   }\r
847 \r
848   /* Try to open the service */\r
849   unsigned long access = SERVICE_QUERY_CONFIG;\r
850   if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;\r
851   service->handle = open_service(services, service->name, access, service->name, _countof(service->name));\r
852   if (! service->handle) {\r
853     CloseServiceHandle(services);\r
854     return 3;\r
855   }\r
856 \r
857   /* Get system details. */\r
858   QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);\r
859   if (! qsc) {\r
860     CloseServiceHandle(service->handle);\r
861     CloseServiceHandle(services);\r
862     return 4;\r
863   }\r
864 \r
865   service->type = qsc->dwServiceType;\r
866   if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
867     if (mode != MODE_GETTING) {\r
868       HeapFree(GetProcessHeap(), 0, qsc);\r
869       CloseServiceHandle(service->handle);\r
870       CloseServiceHandle(services);\r
871       print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);\r
872       return 3;\r
873     }\r
874   }\r
875 \r
876   if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
877     if (mode != MODE_GETTING) {\r
878       HeapFree(GetProcessHeap(), 0, qsc);\r
879       CloseServiceHandle(service->handle);\r
880       CloseServiceHandle(services);\r
881       return 4;\r
882     }\r
883   }\r
884 \r
885   if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
886     if (mode != MODE_GETTING) {\r
887       HeapFree(GetProcessHeap(), 0, qsc);\r
888       CloseServiceHandle(service->handle);\r
889       CloseServiceHandle(services);\r
890       return 5;\r
891     }\r
892   }\r
893 \r
894   _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);\r
895 \r
896   /* Get the canonical service name. We open it case insensitively. */\r
897   unsigned long bufsize = _countof(service->name);\r
898   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
899 \r
900   /* Remember the executable in case it isn't NSSM. */\r
901   _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);\r
902   HeapFree(GetProcessHeap(), 0, qsc);\r
903 \r
904   /* Get extended system details. */\r
905   if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
906     if (mode != MODE_GETTING) {\r
907       CloseServiceHandle(service->handle);\r
908       CloseServiceHandle(services);\r
909       return 6;\r
910     }\r
911   }\r
912 \r
913   if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {\r
914     if (mode != MODE_GETTING) {\r
915       CloseServiceHandle(service->handle);\r
916       CloseServiceHandle(services);\r
917       return 7;\r
918     }\r
919   }\r
920 \r
921   /* Get NSSM details. */\r
922   get_parameters(service, 0);\r
923 \r
924   CloseServiceHandle(services);\r
925 \r
926   if (! service->exe[0]) {\r
927     service->native = true;\r
928     if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);\r
929   }\r
930 \r
931   /* Editing with the GUI. */\r
932   if (mode == MODE_EDITING) {\r
933     nssm_gui(IDD_EDIT, service);\r
934     return 0;\r
935   }\r
936 \r
937   /* Trying to manage App* parameters for a non-NSSM service. */\r
938   if (! setting->native && service->native) {\r
939     CloseServiceHandle(service->handle);\r
940     print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);\r
941     return 1;\r
942   }\r
943 \r
944   HKEY key;\r
945   value_t value;\r
946   int ret;\r
947 \r
948   if (mode == MODE_GETTING) {\r
949     if (! service->native) {\r
950       key = open_registry(service->name, KEY_READ);\r
951       if (! key) return 4;\r
952     }\r
953 \r
954     if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);\r
955     else ret = get_setting(service->name, key, setting, &value, additional);\r
956     if (ret < 0) {\r
957       CloseServiceHandle(service->handle);\r
958       return 5;\r
959     }\r
960 \r
961     switch (setting->type) {\r
962       case REG_EXPAND_SZ:\r
963       case REG_MULTI_SZ:\r
964       case REG_SZ:\r
965         _tprintf(_T("%s\n"), value.string ? value.string : _T(""));\r
966         HeapFree(GetProcessHeap(), 0, value.string);\r
967         break;\r
968 \r
969       case REG_DWORD:\r
970         _tprintf(_T("%u\n"), value.numeric);\r
971         break;\r
972     }\r
973 \r
974     if (! service->native) RegCloseKey(key);\r
975     CloseServiceHandle(service->handle);\r
976     return 0;\r
977   }\r
978 \r
979   /* Build the value. */\r
980   if (mode == MODE_RESETTING) {\r
981     /* Unset the parameter. */\r
982     value.string = 0;\r
983   }\r
984   else if (remainder == argc) {\r
985     value.string = 0;\r
986   }\r
987   else {\r
988     /* Set the parameter. */\r
989     size_t len = 0;\r
990     size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;\r
991     for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;\r
992     len++;\r
993 \r
994     value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
995     if (! value.string) {\r
996       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));\r
997       CloseServiceHandle(service->handle);\r
998       return 2;\r
999     }\r
1000 \r
1001     size_t s = 0;\r
1002     for (i = remainder; i < argc; i++) {\r
1003       size_t len = _tcslen(argv[i]);\r
1004       memmove(value.string + s, argv[i], len * sizeof(TCHAR));\r
1005       s += len;\r
1006       if (i < argc - 1) {\r
1007         if (setting->additional & ADDITIONAL_CRLF) {\r
1008           value.string[s++] = _T('\r');\r
1009           value.string[s++] = _T('\n');\r
1010         }\r
1011         else value.string[s++] = _T(' ');\r
1012       }\r
1013     }\r
1014     value.string[s] = _T('\0');\r
1015   }\r
1016 \r
1017   if (! service->native) {\r
1018     key = open_registry(service->name, KEY_WRITE);\r
1019     if (! key) {\r
1020       if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
1021       return 4;\r
1022     }\r
1023   }\r
1024 \r
1025   if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);\r
1026   else ret = set_setting(service->name, key, setting, &value, additional);\r
1027   if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
1028   if (ret < 0) {\r
1029     if (! service->native) RegCloseKey(key);\r
1030     CloseServiceHandle(service->handle);\r
1031     return 6;\r
1032   }\r
1033 \r
1034   if (! service->native) RegCloseKey(key);\r
1035   CloseServiceHandle(service->handle);\r
1036 \r
1037   return 0;\r
1038 }\r
1039 \r
1040 /* About to remove the service */\r
1041 int pre_remove_service(int argc, TCHAR **argv) {\r
1042   nssm_service_t *service = alloc_nssm_service();\r
1043   set_nssm_service_defaults(service);\r
1044   if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);\r
1045 \r
1046   /* Show dialogue box if we didn't pass service name and "confirm" */\r
1047   if (argc < 2) return nssm_gui(IDD_REMOVE, service);\r
1048   if (str_equiv(argv[1], _T("confirm"))) {\r
1049     int ret = remove_service(service);\r
1050     cleanup_nssm_service(service);\r
1051     return ret;\r
1052   }\r
1053   print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);\r
1054   return 100;\r
1055 }\r
1056 \r
1057 /* Install the service */\r
1058 int install_service(nssm_service_t *service) {\r
1059   if (! service) return 1;\r
1060 \r
1061   /* Open service manager */\r
1062   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);\r
1063   if (! services) {\r
1064     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1065     cleanup_nssm_service(service);\r
1066     return 2;\r
1067   }\r
1068 \r
1069   /* Get path of this program */\r
1070   GetModuleFileName(0, service->image, _countof(service->image));\r
1071 \r
1072   /* Create the service - settings will be changed in edit_service() */\r
1073   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
1074   if (! service->handle) {\r
1075     print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));\r
1076     CloseServiceHandle(services);\r
1077     return 5;\r
1078   }\r
1079 \r
1080   if (edit_service(service, false)) {\r
1081     DeleteService(service->handle);\r
1082     CloseServiceHandle(services);\r
1083     return 6;\r
1084   }\r
1085 \r
1086   print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
1087 \r
1088   /* Cleanup */\r
1089   CloseServiceHandle(services);\r
1090 \r
1091   return 0;\r
1092 }\r
1093 \r
1094 /* Edit the service. */\r
1095 int edit_service(nssm_service_t *service, bool editing) {\r
1096   if (! service) return 1;\r
1097 \r
1098   /*\r
1099     The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS\r
1100     and SERVICE_INTERACTIVE_PROCESS.\r
1101   */\r
1102   service->type &= SERVICE_INTERACTIVE_PROCESS;\r
1103   service->type |= SERVICE_WIN32_OWN_PROCESS;\r
1104 \r
1105   /* Startup type. */\r
1106   unsigned long startup;\r
1107   switch (service->startup) {\r
1108     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
1109     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
1110     default: startup = SERVICE_AUTO_START;\r
1111   }\r
1112 \r
1113   /* Display name. */\r
1114   if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);\r
1115 \r
1116   /*\r
1117     Username must be NULL if we aren't changing or an account name.\r
1118     We must explicitly use LOCALSYSTEM to change it when we are editing.\r
1119     Password must be NULL if we aren't changing, a password or "".\r
1120     Empty passwords are valid but we won't allow them in the GUI.\r
1121   */\r
1122   TCHAR *username = 0;\r
1123   TCHAR *canon = 0;\r
1124   TCHAR *password = 0;\r
1125   if (service->usernamelen) {\r
1126     username = service->username;\r
1127     if (canonicalise_username(username, &canon)) return 5;\r
1128     if (service->passwordlen) password = service->password;\r
1129   }\r
1130   else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;\r
1131 \r
1132   if (well_known_username(canon)) password = _T("");\r
1133   else {\r
1134     if (grant_logon_as_service(canon)) {\r
1135       if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
1136       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
1137       return 5;\r
1138     }\r
1139   }\r
1140 \r
1141   TCHAR *dependencies = _T("");\r
1142   if (service->dependencieslen) dependencies = 0; /* Change later. */\r
1143 \r
1144   if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {\r
1145     if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
1146     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1147     return 5;\r
1148   }\r
1149   if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
1150 \r
1151   if (service->dependencieslen) {\r
1152     if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;\r
1153   }\r
1154 \r
1155   if (service->description[0] || editing) {\r
1156     set_service_description(service->name, service->handle, service->description);\r
1157   }\r
1158 \r
1159   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
1160   ZeroMemory(&delayed, sizeof(delayed));\r
1161   if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
1162   else delayed.fDelayedAutostart = 0;\r
1163   /* Delayed startup isn't supported until Vista. */\r
1164   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
1165     unsigned long error = GetLastError();\r
1166     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1167     if (error != ERROR_INVALID_LEVEL) {\r
1168       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);\r
1169     }\r
1170   }\r
1171 \r
1172   /* Don't mess with parameters which aren't ours. */\r
1173   if (! service->native) {\r
1174     /* Now we need to put the parameters into the registry */\r
1175     if (create_parameters(service, editing)) {\r
1176       print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);\r
1177       return 6;\r
1178     }\r
1179 \r
1180     set_service_recovery(service);\r
1181   }\r
1182 \r
1183   return 0;\r
1184 }\r
1185 \r
1186 /* Control a service. */\r
1187 int control_service(unsigned long control, int argc, TCHAR **argv) {\r
1188   if (argc < 1) return usage(1);\r
1189   TCHAR *service_name = argv[0];\r
1190   TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
1191 \r
1192   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
1193   if (! services) {\r
1194     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1195     return 2;\r
1196   }\r
1197 \r
1198   unsigned long access = SERVICE_QUERY_STATUS;\r
1199   switch (control) {\r
1200     case NSSM_SERVICE_CONTROL_START:\r
1201       access |= SERVICE_START;\r
1202     break;\r
1203 \r
1204     case SERVICE_CONTROL_CONTINUE:\r
1205     case SERVICE_CONTROL_PAUSE:\r
1206       access |= SERVICE_PAUSE_CONTINUE;\r
1207       break;\r
1208 \r
1209     case SERVICE_CONTROL_STOP:\r
1210       access |= SERVICE_STOP;\r
1211       break;\r
1212 \r
1213     case NSSM_SERVICE_CONTROL_ROTATE:\r
1214       access |= SERVICE_USER_DEFINED_CONTROL;\r
1215       break;\r
1216   }\r
1217 \r
1218   SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));\r
1219   if (! service_handle) {\r
1220     CloseServiceHandle(services);\r
1221     return 3;\r
1222   }\r
1223 \r
1224   int ret;\r
1225   unsigned long error;\r
1226   SERVICE_STATUS service_status;\r
1227   if (control == NSSM_SERVICE_CONTROL_START) {\r
1228     unsigned long initial_status = SERVICE_STOPPED;\r
1229     ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);\r
1230     error = GetLastError();\r
1231     CloseServiceHandle(services);\r
1232 \r
1233     if (error == ERROR_IO_PENDING) {\r
1234       /*\r
1235         Older versions of Windows return immediately with ERROR_IO_PENDING\r
1236         indicate that the operation is still in progress.  Newer versions\r
1237         will return it if there really is a delay.\r
1238       */\r
1239       ret = 1;\r
1240       error = ERROR_SUCCESS;\r
1241     }\r
1242 \r
1243     if (ret) {\r
1244       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1245       CloseServiceHandle(service_handle);\r
1246 \r
1247       if (response) {\r
1248         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1249         return 1;\r
1250       }\r
1251       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1252       return 0;\r
1253     }\r
1254     else {\r
1255       CloseServiceHandle(service_handle);\r
1256       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1257       return 1;\r
1258     }\r
1259   }\r
1260   else if (control == SERVICE_CONTROL_INTERROGATE) {\r
1261     /*\r
1262       We could actually send an INTERROGATE control but that won't return\r
1263       any information if the service is stopped and we don't care about\r
1264       the extra details it might give us in any case.  So we'll fake it.\r
1265     */\r
1266     ret = QueryServiceStatus(service_handle, &service_status);\r
1267     error = GetLastError();\r
1268 \r
1269     if (ret) {\r
1270       _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));\r
1271       return 0;\r
1272     }\r
1273     else {\r
1274       _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));\r
1275       return 1;\r
1276     }\r
1277   }\r
1278   else {\r
1279     ret = ControlService(service_handle, control, &service_status);\r
1280     unsigned long initial_status = service_status.dwCurrentState;\r
1281     error = GetLastError();\r
1282     CloseServiceHandle(services);\r
1283 \r
1284     if (error == ERROR_IO_PENDING) {\r
1285       ret = 1;\r
1286       error = ERROR_SUCCESS;\r
1287     }\r
1288 \r
1289     if (ret) {\r
1290       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1291       CloseServiceHandle(service_handle);\r
1292 \r
1293       if (response) {\r
1294         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1295         return 1;\r
1296       }\r
1297       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1298       return 0;\r
1299     }\r
1300     else {\r
1301       CloseServiceHandle(service_handle);\r
1302       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1303       if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
1304         if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
1305       }\r
1306       return 1;\r
1307     }\r
1308   }\r
1309 }\r
1310 \r
1311 /* Remove the service */\r
1312 int remove_service(nssm_service_t *service) {\r
1313   if (! service) return 1;\r
1314 \r
1315   /* Open service manager */\r
1316   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
1317   if (! services) {\r
1318     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1319     return 2;\r
1320   }\r
1321 \r
1322   /* Try to open the service */\r
1323   service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));\r
1324   if (! service->handle) {\r
1325     CloseServiceHandle(services);\r
1326     return 3;\r
1327   }\r
1328 \r
1329   /* Get the canonical service name. We open it case insensitively. */\r
1330   unsigned long bufsize = _countof(service->displayname);\r
1331   GetServiceDisplayName(services, service->name, service->displayname, &bufsize);\r
1332   bufsize = _countof(service->name);\r
1333   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
1334 \r
1335   /* Try to delete the service */\r
1336   if (! DeleteService(service->handle)) {\r
1337     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);\r
1338     CloseServiceHandle(services);\r
1339     return 4;\r
1340   }\r
1341 \r
1342   /* Cleanup */\r
1343   CloseServiceHandle(services);\r
1344 \r
1345   print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
1346   return 0;\r
1347 }\r
1348 \r
1349 /* Service initialisation */\r
1350 void WINAPI service_main(unsigned long argc, TCHAR **argv) {\r
1351   nssm_service_t *service = alloc_nssm_service();\r
1352   if (! service) return;\r
1353 \r
1354   if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {\r
1355     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);\r
1356     return;\r
1357   }\r
1358 \r
1359   /* We can use a condition variable in a critical section on Vista or later. */\r
1360   if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;\r
1361   else use_critical_section = false;\r
1362 \r
1363   /* Initialise status */\r
1364   ZeroMemory(&service->status, sizeof(service->status));\r
1365   service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
1366   service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
1367   service->status.dwWin32ExitCode = NO_ERROR;\r
1368   service->status.dwServiceSpecificExitCode = 0;\r
1369   service->status.dwCheckPoint = 0;\r
1370   service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1371 \r
1372   /* Signal we AREN'T running the server */\r
1373   service->process_handle = 0;\r
1374   service->pid = 0;\r
1375 \r
1376   /* Register control handler */\r
1377   service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);\r
1378   if (! service->status_handle) {\r
1379     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);\r
1380     return;\r
1381   }\r
1382 \r
1383   log_service_control(service->name, 0, true);\r
1384 \r
1385   service->status.dwCurrentState = SERVICE_START_PENDING;\r
1386   service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;\r
1387   SetServiceStatus(service->status_handle, &service->status);\r
1388 \r
1389   if (is_admin) {\r
1390     /* Try to create the exit action parameters; we don't care if it fails */\r
1391     create_exit_action(service->name, exit_action_strings[0], false);\r
1392 \r
1393     SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);\r
1394     if (services) {\r
1395       service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);\r
1396       set_service_recovery(service);\r
1397       CloseServiceHandle(services);\r
1398     }\r
1399   }\r
1400 \r
1401   /* Used for signalling a resume if the service pauses when throttled. */\r
1402   if (use_critical_section) {\r
1403     InitializeCriticalSection(&service->throttle_section);\r
1404     service->throttle_section_initialised = true;\r
1405   }\r
1406   else {\r
1407     service->throttle_timer = CreateWaitableTimer(0, 1, 0);\r
1408     if (! service->throttle_timer) {\r
1409       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);\r
1410     }\r
1411   }\r
1412 \r
1413   /* Remember our initial environment. */\r
1414   service->initial_env = GetEnvironmentStrings();\r
1415 \r
1416   monitor_service(service);\r
1417 }\r
1418 \r
1419 /* Make sure service recovery actions are taken where necessary */\r
1420 void set_service_recovery(nssm_service_t *service) {\r
1421   SERVICE_FAILURE_ACTIONS_FLAG flag;\r
1422   ZeroMemory(&flag, sizeof(flag));\r
1423   flag.fFailureActionsOnNonCrashFailures = true;\r
1424 \r
1425   /* This functionality was added in Vista so the call may fail */\r
1426   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
1427     unsigned long error = GetLastError();\r
1428     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1429     if (error != ERROR_INVALID_LEVEL) {\r
1430       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);\r
1431     }\r
1432   }\r
1433 }\r
1434 \r
1435 int monitor_service(nssm_service_t *service) {\r
1436   /* Set service status to started */\r
1437   int ret = start_service(service);\r
1438   if (ret) {\r
1439     TCHAR code[16];\r
1440     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);\r
1441     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);\r
1442     return ret;\r
1443   }\r
1444   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);\r
1445 \r
1446   /* Monitor service */\r
1447   if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
1448     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);\r
1449   }\r
1450 \r
1451   return 0;\r
1452 }\r
1453 \r
1454 TCHAR *service_control_text(unsigned long control) {\r
1455   switch (control) {\r
1456     /* HACK: there is no SERVICE_CONTROL_START constant */\r
1457     case NSSM_SERVICE_CONTROL_START: return _T("START");\r
1458     case SERVICE_CONTROL_STOP: return _T("STOP");\r
1459     case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");\r
1460     case SERVICE_CONTROL_PAUSE: return _T("PAUSE");\r
1461     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
1462     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
1463     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
1464     case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");\r
1465     default: return 0;\r
1466   }\r
1467 }\r
1468 \r
1469 TCHAR *service_status_text(unsigned long status) {\r
1470   switch (status) {\r
1471     case SERVICE_STOPPED: return _T("SERVICE_STOPPED");\r
1472     case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");\r
1473     case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");\r
1474     case SERVICE_RUNNING: return _T("SERVICE_RUNNING");\r
1475     case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");\r
1476     case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");\r
1477     case SERVICE_PAUSED: return _T("SERVICE_PAUSED");\r
1478     default: return 0;\r
1479   }\r
1480 }\r
1481 \r
1482 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {\r
1483   TCHAR *text = service_control_text(control);\r
1484   unsigned long event;\r
1485 \r
1486   if (! text) {\r
1487     /* "0x" + 8 x hex + NULL */\r
1488     text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));\r
1489     if (! text) {\r
1490       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1491       return;\r
1492     }\r
1493     if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {\r
1494       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1495       HeapFree(GetProcessHeap(), 0, text);\r
1496       return;\r
1497     }\r
1498 \r
1499     event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;\r
1500   }\r
1501   else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;\r
1502   else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;\r
1503 \r
1504   log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);\r
1505 \r
1506   if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {\r
1507     HeapFree(GetProcessHeap(), 0, text);\r
1508   }\r
1509 }\r
1510 \r
1511 /* Service control handler */\r
1512 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {\r
1513   nssm_service_t *service = (nssm_service_t *) context;\r
1514 \r
1515   switch (control) {\r
1516     case SERVICE_CONTROL_INTERROGATE:\r
1517       /* We always keep the service status up-to-date so this is a no-op. */\r
1518       return NO_ERROR;\r
1519 \r
1520     case SERVICE_CONTROL_SHUTDOWN:\r
1521     case SERVICE_CONTROL_STOP:\r
1522       log_service_control(service->name, control, true);\r
1523       /*\r
1524         We MUST acknowledge the stop request promptly but we're committed to\r
1525         waiting for the application to exit.  Spawn a new thread to wait\r
1526         while we acknowledge the request.\r
1527       */\r
1528       if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {\r
1529         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
1530 \r
1531         /*\r
1532           We couldn't create a thread to tidy up so we'll have to force the tidyup\r
1533           to complete in time in this thread.\r
1534         */\r
1535         service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
1536         service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
1537         service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
1538 \r
1539         stop_service(service, 0, true, true);\r
1540       }\r
1541       return NO_ERROR;\r
1542 \r
1543     case SERVICE_CONTROL_CONTINUE:\r
1544       log_service_control(service->name, control, true);\r
1545       service->throttle = 0;\r
1546       if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);\r
1547       else {\r
1548         if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
1549         ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1550         SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1551       }\r
1552       /* We can't continue if the application is running! */\r
1553       if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
1554       service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;\r
1555       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);\r
1556       SetServiceStatus(service->status_handle, &service->status);\r
1557       return NO_ERROR;\r
1558 \r
1559     case SERVICE_CONTROL_PAUSE:\r
1560       /*\r
1561         We don't accept pause messages but it isn't possible to register\r
1562         only for continue messages so we have to handle this case.\r
1563       */\r
1564       log_service_control(service->name, control, false);\r
1565       return ERROR_CALL_NOT_IMPLEMENTED;\r
1566 \r
1567     case NSSM_SERVICE_CONTROL_ROTATE:\r
1568       log_service_control(service->name, control, true);\r
1569       if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
1570       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
1571       return NO_ERROR;\r
1572 \r
1573     case SERVICE_CONTROL_POWEREVENT:\r
1574       if (event != PBT_APMRESUMEAUTOMATIC) {\r
1575         log_service_control(service->name, control, false);\r
1576         return NO_ERROR;\r
1577       }\r
1578       log_service_control(service->name, control, true);\r
1579       end_service((void *) service, false);\r
1580       return NO_ERROR;\r
1581   }\r
1582 \r
1583   /* Unknown control */\r
1584   log_service_control(service->name, control, false);\r
1585   return ERROR_CALL_NOT_IMPLEMENTED;\r
1586 }\r
1587 \r
1588 /* Start the service */\r
1589 int start_service(nssm_service_t *service) {\r
1590   service->stopping = false;\r
1591   service->allow_restart = true;\r
1592 \r
1593   if (service->process_handle) return 0;\r
1594 \r
1595   /* Allocate a STARTUPINFO structure for a new process */\r
1596   STARTUPINFO si;\r
1597   ZeroMemory(&si, sizeof(si));\r
1598   si.cb = sizeof(si);\r
1599 \r
1600   /* Allocate a PROCESSINFO structure for the process */\r
1601   PROCESS_INFORMATION pi;\r
1602   ZeroMemory(&pi, sizeof(pi));\r
1603 \r
1604   /* Get startup parameters */\r
1605   int ret = get_parameters(service, &si);\r
1606   if (ret) {\r
1607     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);\r
1608     return stop_service(service, 2, true, true);\r
1609   }\r
1610 \r
1611   /* Launch executable with arguments */\r
1612   TCHAR cmd[CMD_LENGTH];\r
1613   if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {\r
1614     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);\r
1615     return stop_service(service, 2, true, true);\r
1616   }\r
1617 \r
1618   throttle_restart(service);\r
1619 \r
1620   /* Set the environment. */\r
1621   if (service->env) duplicate_environment(service->env);\r
1622   if (service->env_extra) set_environment_block(service->env_extra);\r
1623 \r
1624   /* Set up I/O redirection. */\r
1625   if (get_output_handles(service, &si)) {\r
1626     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
1627     if (! service->no_console) FreeConsole();\r
1628     close_output_handles(&si);\r
1629     return stop_service(service, 4, true, true);\r
1630   }\r
1631 \r
1632   bool inherit_handles = false;\r
1633   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
1634   unsigned long flags = service->priority & priority_mask();\r
1635   if (service->affinity) flags |= CREATE_SUSPENDED;\r
1636   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
1637     unsigned long exitcode = 3;\r
1638     unsigned long error = GetLastError();\r
1639     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1640     close_output_handles(&si);\r
1641     duplicate_environment_strings(service->initial_env);\r
1642     return stop_service(service, exitcode, true, true);\r
1643   }\r
1644   service->process_handle = pi.hProcess;\r
1645   service->pid = pi.dwProcessId;\r
1646 \r
1647   if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1648 \r
1649   close_output_handles(&si);\r
1650 \r
1651   if (! service->no_console) FreeConsole();\r
1652 \r
1653   /* Restore our environment. */\r
1654   duplicate_environment_strings(service->initial_env);\r
1655 \r
1656   if (service->affinity) {\r
1657     /*\r
1658       We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1659       so that we can parse it regardless of whether we're running in 32-bit\r
1660       or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1661       defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1662       (or when running the 32-bit NSSM).\r
1663 \r
1664       The result is a lot of seemingly-unnecessary casting throughout the code\r
1665       and potentially confusion when we actually try to start the service.\r
1666       Having said that, however, it's unlikely that we're actually going to\r
1667       run in 32-bit mode on a system which has more than 32 CPUs so the\r
1668       likelihood of seeing a confusing situation is somewhat diminished.\r
1669     */\r
1670     DWORD_PTR affinity, system_affinity;\r
1671 \r
1672     if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1673     else {\r
1674       affinity = (DWORD_PTR) service->affinity;\r
1675       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1676     }\r
1677 \r
1678     if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1679       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1680     }\r
1681 \r
1682     ResumeThread(pi.hThread);\r
1683   }\r
1684 \r
1685   /*\r
1686     Wait for a clean startup before changing the service status to RUNNING\r
1687     but be mindful of the fact that we are blocking the service control manager\r
1688     so abandon the wait before too much time has elapsed.\r
1689   */\r
1690   unsigned long delay = service->throttle_delay;\r
1691   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
1692     TCHAR delay_milliseconds[16];\r
1693     _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
1694     TCHAR deadline_milliseconds[16];\r
1695     _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
1696     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
1697     delay = NSSM_SERVICE_STATUS_DEADLINE;\r
1698   }\r
1699   unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
1700 \r
1701   /* Signal successful start */\r
1702   service->status.dwCurrentState = SERVICE_RUNNING;\r
1703   SetServiceStatus(service->status_handle, &service->status);\r
1704 \r
1705   /* Continue waiting for a clean startup. */\r
1706   if (deadline == WAIT_TIMEOUT) {\r
1707     if (service->throttle_delay > delay) {\r
1708       if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
1709     }\r
1710     else service->throttle = 0;\r
1711   }\r
1712 \r
1713   /* Ensure the restart delay is always applied. */\r
1714   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1715 \r
1716   return 0;\r
1717 }\r
1718 \r
1719 /* Stop the service */\r
1720 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1721   service->allow_restart = false;\r
1722   if (service->wait_handle) {\r
1723     UnregisterWait(service->wait_handle);\r
1724     service->wait_handle = 0;\r
1725   }\r
1726 \r
1727   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1728 \r
1729   if (default_action && ! exitcode && ! graceful) {\r
1730     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
1731     graceful = true;\r
1732   }\r
1733 \r
1734   /* Signal we are stopping */\r
1735   if (graceful) {\r
1736     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1737     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1738     SetServiceStatus(service->status_handle, &service->status);\r
1739   }\r
1740 \r
1741   /* Nothing to do if service isn't running */\r
1742   if (service->pid) {\r
1743     /* Shut down service */\r
1744     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1745     kill_process(service, service->process_handle, service->pid, 0);\r
1746   }\r
1747   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1748 \r
1749   end_service((void *) service, true);\r
1750 \r
1751   /* Signal we stopped */\r
1752   if (graceful) {\r
1753     service->status.dwCurrentState = SERVICE_STOPPED;\r
1754     if (exitcode) {\r
1755       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1756       service->status.dwServiceSpecificExitCode = exitcode;\r
1757     }\r
1758     else {\r
1759       service->status.dwWin32ExitCode = NO_ERROR;\r
1760       service->status.dwServiceSpecificExitCode = 0;\r
1761     }\r
1762     SetServiceStatus(service->status_handle, &service->status);\r
1763   }\r
1764 \r
1765   return exitcode;\r
1766 }\r
1767 \r
1768 /* Callback function triggered when the server exits */\r
1769 void CALLBACK end_service(void *arg, unsigned char why) {\r
1770   nssm_service_t *service = (nssm_service_t *) arg;\r
1771 \r
1772   if (service->stopping) return;\r
1773 \r
1774   service->stopping = true;\r
1775 \r
1776   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1777 \r
1778   /* Use now as a dummy exit time. */\r
1779   GetSystemTimeAsFileTime(&service->exit_time);\r
1780 \r
1781   /* Check exit code */\r
1782   unsigned long exitcode = 0;\r
1783   TCHAR code[16];\r
1784   if (service->process_handle) {\r
1785     GetExitCodeProcess(service->process_handle, &exitcode);\r
1786     /* Check real exit time. */\r
1787     if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
1788     CloseHandle(service->process_handle);\r
1789   }\r
1790 \r
1791   service->process_handle = 0;\r
1792 \r
1793   /*\r
1794     Log that the service ended BEFORE logging about killing the process\r
1795     tree.  See below for the possible values of the why argument.\r
1796   */\r
1797   if (! why) {\r
1798     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1799     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1800   }\r
1801 \r
1802   /* Clean up. */\r
1803   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1804   if (service->pid && service->kill_process_tree) kill_process_tree(service, service->pid, exitcode, service->pid);\r
1805   service->pid = 0;\r
1806 \r
1807   /*\r
1808     The why argument is true if our wait timed out or false otherwise.\r
1809     Our wait is infinite so why will never be true when called by the system.\r
1810     If it is indeed true, assume we were called from stop_service() because\r
1811     this is a controlled shutdown, and don't take any restart action.\r
1812   */\r
1813   if (why) return;\r
1814   if (! service->allow_restart) return;\r
1815 \r
1816   /* What action should we take? */\r
1817   int action = NSSM_EXIT_RESTART;\r
1818   TCHAR action_string[ACTION_LEN];\r
1819   bool default_action;\r
1820   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1821     for (int i = 0; exit_action_strings[i]; i++) {\r
1822       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1823         action = i;\r
1824         break;\r
1825       }\r
1826     }\r
1827   }\r
1828 \r
1829   switch (action) {\r
1830     /* Try to restart the service or return failure code to service manager */\r
1831     case NSSM_EXIT_RESTART:\r
1832       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1833       while (monitor_service(service)) {\r
1834         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1835         Sleep(30000);\r
1836       }\r
1837     break;\r
1838 \r
1839     /* Do nothing, just like srvany would */\r
1840     case NSSM_EXIT_IGNORE:\r
1841       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1842       Sleep(INFINITE);\r
1843     break;\r
1844 \r
1845     /* Tell the service manager we are finished */\r
1846     case NSSM_EXIT_REALLY:\r
1847       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1848       stop_service(service, exitcode, true, default_action);\r
1849     break;\r
1850 \r
1851     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1852     case NSSM_EXIT_UNCLEAN:\r
1853       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1854       stop_service(service, exitcode, false, default_action);\r
1855       free_imports();\r
1856       exit(exitcode);\r
1857     break;\r
1858   }\r
1859 }\r
1860 \r
1861 void throttle_restart(nssm_service_t *service) {\r
1862   /* This can't be a restart if the service is already running. */\r
1863   if (! service->throttle++) return;\r
1864 \r
1865   unsigned long ms;\r
1866   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1867   TCHAR threshold[8], milliseconds[8];\r
1868 \r
1869   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1870   else ms = throttle_ms;\r
1871 \r
1872   if (service->throttle > 7) service->throttle = 8;\r
1873 \r
1874   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1875 \r
1876   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
1877   else {\r
1878     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
1879     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
1880   }\r
1881 \r
1882   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
1883   else if (service->throttle_timer) {\r
1884     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1885     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
1886     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1887   }\r
1888 \r
1889   service->status.dwCurrentState = SERVICE_PAUSED;\r
1890   SetServiceStatus(service->status_handle, &service->status);\r
1891 \r
1892   if (use_critical_section) {\r
1893     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
1894     LeaveCriticalSection(&service->throttle_section);\r
1895   }\r
1896   else {\r
1897     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
1898     else Sleep(ms);\r
1899   }\r
1900 }\r
1901 \r
1902 /*\r
1903   When responding to a stop (or any other) request we need to set dwWaitHint to\r
1904   the number of milliseconds we expect the operation to take, and optionally\r
1905   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
1906   operation completing or dwCheckPoint increasing, the system will consider the\r
1907   service to be hung.\r
1908 \r
1909   However the system will consider the service to be hung after 30000\r
1910   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
1911   changed.  Therefore if we want to wait longer than that we must periodically\r
1912   increase dwCheckPoint.\r
1913 \r
1914   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
1915   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
1916   time dwCheckPoint is also increased.\r
1917 \r
1918   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
1919   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
1920   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
1921   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
1922   dwWaitHint.\r
1923 \r
1924   Only doing both these things will prevent the system from killing the service.\r
1925 \r
1926   Returns: 1 if the wait timed out.\r
1927            0 if the wait completed.\r
1928           -1 on error.\r
1929 */\r
1930 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
1931   unsigned long interval;\r
1932   unsigned long waithint;\r
1933   unsigned long ret;\r
1934   unsigned long waited;\r
1935   TCHAR interval_milliseconds[16];\r
1936   TCHAR timeout_milliseconds[16];\r
1937   TCHAR waited_milliseconds[16];\r
1938   TCHAR *function = function_name;\r
1939 \r
1940   /* Add brackets to function name. */\r
1941   size_t funclen = _tcslen(function_name) + 3;\r
1942   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
1943   if (func) {\r
1944     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
1945   }\r
1946 \r
1947   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
1948 \r
1949   waithint = service->status.dwWaitHint;\r
1950   waited = 0;\r
1951   while (waited < timeout) {\r
1952     interval = timeout - waited;\r
1953     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
1954 \r
1955     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1956     service->status.dwWaitHint += interval;\r
1957     service->status.dwCheckPoint++;\r
1958     SetServiceStatus(service->status_handle, &service->status);\r
1959 \r
1960     if (waited) {\r
1961       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
1962       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
1963       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
1964     }\r
1965 \r
1966     switch (WaitForSingleObject(service->process_handle, interval)) {\r
1967       case WAIT_OBJECT_0:\r
1968         ret = 0;\r
1969         goto awaited;\r
1970 \r
1971       case WAIT_TIMEOUT:\r
1972         ret = 1;\r
1973       break;\r
1974 \r
1975       default:\r
1976         ret = -1;\r
1977         goto awaited;\r
1978     }\r
1979 \r
1980     waited += interval;\r
1981   }\r
1982 \r
1983 awaited:\r
1984   if (func) HeapFree(GetProcessHeap(), 0, func);\r
1985 \r
1986   return ret;\r
1987 }\r