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