Allow editing service dependencies.
[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) CloseHandle(service->handle);\r
713   if (service->process_handle) CloseHandle(service->process_handle);\r
714   if (service->wait_handle) UnregisterWait(service->process_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     CloseHandle(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       CloseHandle(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       CloseHandle(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       CloseHandle(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       CloseHandle(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       CloseHandle(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     CloseHandle(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       CloseHandle(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     CloseHandle(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       CloseHandle(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     CloseHandle(service->handle);\r
1030     return 6;\r
1031   }\r
1032 \r
1033   if (! service->native) RegCloseKey(key);\r
1034   CloseHandle(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 *password = 0;\r
1123   if (service->usernamelen) {\r
1124     username = service->username;\r
1125     if (service->passwordlen) password = service->password;\r
1126     else password = _T("");\r
1127   }\r
1128   else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;\r
1129 \r
1130   if (well_known_username(username)) password = _T("");\r
1131   else {\r
1132     if (grant_logon_as_service(username)) {\r
1133       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
1134       return 5;\r
1135     }\r
1136   }\r
1137 \r
1138   TCHAR *dependencies = _T("");\r
1139   if (service->dependencieslen) dependencies = 0; /* Change later. */\r
1140 \r
1141   if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, username, password, service->displayname)) {\r
1142     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1143     return 5;\r
1144   }\r
1145 \r
1146   if (service->dependencieslen) {\r
1147     if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;\r
1148   }\r
1149 \r
1150   if (service->description[0] || editing) {\r
1151     set_service_description(service->name, service->handle, service->description);\r
1152   }\r
1153 \r
1154   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
1155   ZeroMemory(&delayed, sizeof(delayed));\r
1156   if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
1157   else delayed.fDelayedAutostart = 0;\r
1158   /* Delayed startup isn't supported until Vista. */\r
1159   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
1160     unsigned long error = GetLastError();\r
1161     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1162     if (error != ERROR_INVALID_LEVEL) {\r
1163       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);\r
1164     }\r
1165   }\r
1166 \r
1167   /* Don't mess with parameters which aren't ours. */\r
1168   if (! service->native) {\r
1169     /* Now we need to put the parameters into the registry */\r
1170     if (create_parameters(service, editing)) {\r
1171       print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);\r
1172       return 6;\r
1173     }\r
1174 \r
1175     set_service_recovery(service);\r
1176   }\r
1177 \r
1178   return 0;\r
1179 }\r
1180 \r
1181 /* Control a service. */\r
1182 int control_service(unsigned long control, int argc, TCHAR **argv) {\r
1183   if (argc < 1) return usage(1);\r
1184   TCHAR *service_name = argv[0];\r
1185   TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
1186 \r
1187   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
1188   if (! services) {\r
1189     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1190     return 2;\r
1191   }\r
1192 \r
1193   unsigned long access = SERVICE_QUERY_STATUS;\r
1194   switch (control) {\r
1195     case NSSM_SERVICE_CONTROL_START:\r
1196       access |= SERVICE_START;\r
1197     break;\r
1198 \r
1199     case SERVICE_CONTROL_CONTINUE:\r
1200     case SERVICE_CONTROL_PAUSE:\r
1201       access |= SERVICE_PAUSE_CONTINUE;\r
1202       break;\r
1203 \r
1204     case SERVICE_CONTROL_STOP:\r
1205       access |= SERVICE_STOP;\r
1206       break;\r
1207 \r
1208     case NSSM_SERVICE_CONTROL_ROTATE:\r
1209       access |= SERVICE_USER_DEFINED_CONTROL;\r
1210       break;\r
1211   }\r
1212 \r
1213   SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));\r
1214   if (! service_handle) {\r
1215     CloseServiceHandle(services);\r
1216     return 3;\r
1217   }\r
1218 \r
1219   int ret;\r
1220   unsigned long error;\r
1221   SERVICE_STATUS service_status;\r
1222   if (control == NSSM_SERVICE_CONTROL_START) {\r
1223     unsigned long initial_status = SERVICE_STOPPED;\r
1224     ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);\r
1225     error = GetLastError();\r
1226     CloseServiceHandle(services);\r
1227 \r
1228     if (error == ERROR_IO_PENDING) {\r
1229       /*\r
1230         Older versions of Windows return immediately with ERROR_IO_PENDING\r
1231         indicate that the operation is still in progress.  Newer versions\r
1232         will return it if there really is a delay.\r
1233       */\r
1234       ret = 1;\r
1235       error = ERROR_SUCCESS;\r
1236     }\r
1237 \r
1238     if (ret) {\r
1239       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1240       CloseHandle(service_handle);\r
1241 \r
1242       if (response) {\r
1243         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1244         return 1;\r
1245       }\r
1246       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1247       return 0;\r
1248     }\r
1249     else {\r
1250       CloseHandle(service_handle);\r
1251       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1252       return 1;\r
1253     }\r
1254   }\r
1255   else if (control == SERVICE_CONTROL_INTERROGATE) {\r
1256     /*\r
1257       We could actually send an INTERROGATE control but that won't return\r
1258       any information if the service is stopped and we don't care about\r
1259       the extra details it might give us in any case.  So we'll fake it.\r
1260     */\r
1261     ret = QueryServiceStatus(service_handle, &service_status);\r
1262     error = GetLastError();\r
1263 \r
1264     if (ret) {\r
1265       _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));\r
1266       return 0;\r
1267     }\r
1268     else {\r
1269       _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));\r
1270       return 1;\r
1271     }\r
1272   }\r
1273   else {\r
1274     ret = ControlService(service_handle, control, &service_status);\r
1275     unsigned long initial_status = service_status.dwCurrentState;\r
1276     error = GetLastError();\r
1277     CloseServiceHandle(services);\r
1278 \r
1279     if (error == ERROR_IO_PENDING) {\r
1280       ret = 1;\r
1281       error = ERROR_SUCCESS;\r
1282     }\r
1283 \r
1284     if (ret) {\r
1285       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
1286       CloseHandle(service_handle);\r
1287 \r
1288       if (response) {\r
1289         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
1290         return 1;\r
1291       }\r
1292       else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1293       return 0;\r
1294     }\r
1295     else {\r
1296       CloseHandle(service_handle);\r
1297       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
1298       if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
1299         if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
1300       }\r
1301       return 1;\r
1302     }\r
1303   }\r
1304 }\r
1305 \r
1306 /* Remove the service */\r
1307 int remove_service(nssm_service_t *service) {\r
1308   if (! service) return 1;\r
1309 \r
1310   /* Open service manager */\r
1311   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
1312   if (! services) {\r
1313     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
1314     return 2;\r
1315   }\r
1316 \r
1317   /* Try to open the service */\r
1318   service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));\r
1319   if (! service->handle) {\r
1320     CloseServiceHandle(services);\r
1321     return 3;\r
1322   }\r
1323 \r
1324   /* Get the canonical service name. We open it case insensitively. */\r
1325   unsigned long bufsize = _countof(service->displayname);\r
1326   GetServiceDisplayName(services, service->name, service->displayname, &bufsize);\r
1327   bufsize = _countof(service->name);\r
1328   GetServiceKeyName(services, service->displayname, service->name, &bufsize);\r
1329 \r
1330   /* Try to delete the service */\r
1331   if (! DeleteService(service->handle)) {\r
1332     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);\r
1333     CloseServiceHandle(services);\r
1334     return 4;\r
1335   }\r
1336 \r
1337   /* Cleanup */\r
1338   CloseServiceHandle(services);\r
1339 \r
1340   print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
1341   return 0;\r
1342 }\r
1343 \r
1344 /* Service initialisation */\r
1345 void WINAPI service_main(unsigned long argc, TCHAR **argv) {\r
1346   nssm_service_t *service = alloc_nssm_service();\r
1347   if (! service) return;\r
1348 \r
1349   if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {\r
1350     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);\r
1351     return;\r
1352   }\r
1353 \r
1354   /* We can use a condition variable in a critical section on Vista or later. */\r
1355   if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;\r
1356   else use_critical_section = false;\r
1357 \r
1358   /* Initialise status */\r
1359   ZeroMemory(&service->status, sizeof(service->status));\r
1360   service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
1361   service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
1362   service->status.dwWin32ExitCode = NO_ERROR;\r
1363   service->status.dwServiceSpecificExitCode = 0;\r
1364   service->status.dwCheckPoint = 0;\r
1365   service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1366 \r
1367   /* Signal we AREN'T running the server */\r
1368   service->process_handle = 0;\r
1369   service->pid = 0;\r
1370 \r
1371   /* Register control handler */\r
1372   service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);\r
1373   if (! service->status_handle) {\r
1374     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);\r
1375     return;\r
1376   }\r
1377 \r
1378   log_service_control(service->name, 0, true);\r
1379 \r
1380   service->status.dwCurrentState = SERVICE_START_PENDING;\r
1381   service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;\r
1382   SetServiceStatus(service->status_handle, &service->status);\r
1383 \r
1384   if (is_admin) {\r
1385     /* Try to create the exit action parameters; we don't care if it fails */\r
1386     create_exit_action(service->name, exit_action_strings[0], false);\r
1387 \r
1388     SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);\r
1389     if (services) {\r
1390       service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);\r
1391       set_service_recovery(service);\r
1392       CloseServiceHandle(services);\r
1393     }\r
1394   }\r
1395 \r
1396   /* Used for signalling a resume if the service pauses when throttled. */\r
1397   if (use_critical_section) {\r
1398     InitializeCriticalSection(&service->throttle_section);\r
1399     service->throttle_section_initialised = true;\r
1400   }\r
1401   else {\r
1402     service->throttle_timer = CreateWaitableTimer(0, 1, 0);\r
1403     if (! service->throttle_timer) {\r
1404       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);\r
1405     }\r
1406   }\r
1407 \r
1408   /* Remember our initial environment. */\r
1409   service->initial_env = GetEnvironmentStrings();\r
1410 \r
1411   monitor_service(service);\r
1412 }\r
1413 \r
1414 /* Make sure service recovery actions are taken where necessary */\r
1415 void set_service_recovery(nssm_service_t *service) {\r
1416   SERVICE_FAILURE_ACTIONS_FLAG flag;\r
1417   ZeroMemory(&flag, sizeof(flag));\r
1418   flag.fFailureActionsOnNonCrashFailures = true;\r
1419 \r
1420   /* This functionality was added in Vista so the call may fail */\r
1421   if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
1422     unsigned long error = GetLastError();\r
1423     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1424     if (error != ERROR_INVALID_LEVEL) {\r
1425       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);\r
1426     }\r
1427   }\r
1428 }\r
1429 \r
1430 int monitor_service(nssm_service_t *service) {\r
1431   /* Set service status to started */\r
1432   int ret = start_service(service);\r
1433   if (ret) {\r
1434     TCHAR code[16];\r
1435     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);\r
1436     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);\r
1437     return ret;\r
1438   }\r
1439   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);\r
1440 \r
1441   /* Monitor service */\r
1442   if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
1443     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);\r
1444   }\r
1445 \r
1446   return 0;\r
1447 }\r
1448 \r
1449 TCHAR *service_control_text(unsigned long control) {\r
1450   switch (control) {\r
1451     /* HACK: there is no SERVICE_CONTROL_START constant */\r
1452     case NSSM_SERVICE_CONTROL_START: return _T("START");\r
1453     case SERVICE_CONTROL_STOP: return _T("STOP");\r
1454     case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");\r
1455     case SERVICE_CONTROL_PAUSE: return _T("PAUSE");\r
1456     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
1457     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
1458     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
1459     default: return 0;\r
1460   }\r
1461 }\r
1462 \r
1463 TCHAR *service_status_text(unsigned long status) {\r
1464   switch (status) {\r
1465     case SERVICE_STOPPED: return _T("SERVICE_STOPPED");\r
1466     case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");\r
1467     case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");\r
1468     case SERVICE_RUNNING: return _T("SERVICE_RUNNING");\r
1469     case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");\r
1470     case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");\r
1471     case SERVICE_PAUSED: return _T("SERVICE_PAUSED");\r
1472     default: return 0;\r
1473   }\r
1474 }\r
1475 \r
1476 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {\r
1477   TCHAR *text = service_control_text(control);\r
1478   unsigned long event;\r
1479 \r
1480   if (! text) {\r
1481     /* "0x" + 8 x hex + NULL */\r
1482     text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));\r
1483     if (! text) {\r
1484       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1485       return;\r
1486     }\r
1487     if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {\r
1488       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);\r
1489       HeapFree(GetProcessHeap(), 0, text);\r
1490       return;\r
1491     }\r
1492 \r
1493     event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;\r
1494   }\r
1495   else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;\r
1496   else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;\r
1497 \r
1498   log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);\r
1499 \r
1500   if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {\r
1501     HeapFree(GetProcessHeap(), 0, text);\r
1502   }\r
1503 }\r
1504 \r
1505 /* Service control handler */\r
1506 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {\r
1507   nssm_service_t *service = (nssm_service_t *) context;\r
1508 \r
1509   switch (control) {\r
1510     case SERVICE_CONTROL_INTERROGATE:\r
1511       /* We always keep the service status up-to-date so this is a no-op. */\r
1512       return NO_ERROR;\r
1513 \r
1514     case SERVICE_CONTROL_SHUTDOWN:\r
1515     case SERVICE_CONTROL_STOP:\r
1516       log_service_control(service->name, control, true);\r
1517       /*\r
1518         We MUST acknowledge the stop request promptly but we're committed to\r
1519         waiting for the application to exit.  Spawn a new thread to wait\r
1520         while we acknowledge the request.\r
1521       */\r
1522       if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {\r
1523         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
1524 \r
1525         /*\r
1526           We couldn't create a thread to tidy up so we'll have to force the tidyup\r
1527           to complete in time in this thread.\r
1528         */\r
1529         service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
1530         service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
1531         service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
1532 \r
1533         stop_service(service, 0, true, true);\r
1534       }\r
1535       return NO_ERROR;\r
1536 \r
1537     case SERVICE_CONTROL_CONTINUE:\r
1538       log_service_control(service->name, control, true);\r
1539       service->throttle = 0;\r
1540       if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);\r
1541       else {\r
1542         if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
1543         ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1544         SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1545       }\r
1546       /* We can't continue if the application is running! */\r
1547       if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
1548       service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;\r
1549       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);\r
1550       SetServiceStatus(service->status_handle, &service->status);\r
1551       return NO_ERROR;\r
1552 \r
1553     case SERVICE_CONTROL_PAUSE:\r
1554       /*\r
1555         We don't accept pause messages but it isn't possible to register\r
1556         only for continue messages so we have to handle this case.\r
1557       */\r
1558       log_service_control(service->name, control, false);\r
1559       return ERROR_CALL_NOT_IMPLEMENTED;\r
1560 \r
1561     case NSSM_SERVICE_CONTROL_ROTATE:\r
1562       log_service_control(service->name, control, true);\r
1563       if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
1564       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
1565       return NO_ERROR;\r
1566   }\r
1567 \r
1568   /* Unknown control */\r
1569   log_service_control(service->name, control, false);\r
1570   return ERROR_CALL_NOT_IMPLEMENTED;\r
1571 }\r
1572 \r
1573 /* Start the service */\r
1574 int start_service(nssm_service_t *service) {\r
1575   service->stopping = false;\r
1576   service->allow_restart = true;\r
1577 \r
1578   if (service->process_handle) return 0;\r
1579 \r
1580   /* Allocate a STARTUPINFO structure for a new process */\r
1581   STARTUPINFO si;\r
1582   ZeroMemory(&si, sizeof(si));\r
1583   si.cb = sizeof(si);\r
1584 \r
1585   /* Allocate a PROCESSINFO structure for the process */\r
1586   PROCESS_INFORMATION pi;\r
1587   ZeroMemory(&pi, sizeof(pi));\r
1588 \r
1589   /* Get startup parameters */\r
1590   int ret = get_parameters(service, &si);\r
1591   if (ret) {\r
1592     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);\r
1593     return stop_service(service, 2, true, true);\r
1594   }\r
1595 \r
1596   /* Launch executable with arguments */\r
1597   TCHAR cmd[CMD_LENGTH];\r
1598   if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {\r
1599     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);\r
1600     return stop_service(service, 2, true, true);\r
1601   }\r
1602 \r
1603   throttle_restart(service);\r
1604 \r
1605   /* Set the environment. */\r
1606   if (service->env) duplicate_environment(service->env);\r
1607   if (service->env_extra) set_environment_block(service->env_extra);\r
1608 \r
1609   /* Set up I/O redirection. */\r
1610   if (get_output_handles(service, &si)) {\r
1611     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
1612     if (! service->no_console) FreeConsole();\r
1613     close_output_handles(&si);\r
1614     return stop_service(service, 4, true, true);\r
1615   }\r
1616 \r
1617   bool inherit_handles = false;\r
1618   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
1619   unsigned long flags = service->priority & priority_mask();\r
1620   if (service->affinity) flags |= CREATE_SUSPENDED;\r
1621   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
1622     unsigned long exitcode = 3;\r
1623     unsigned long error = GetLastError();\r
1624     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1625     close_output_handles(&si);\r
1626     duplicate_environment(service->initial_env);\r
1627     return stop_service(service, exitcode, true, true);\r
1628   }\r
1629   service->process_handle = pi.hProcess;\r
1630   service->pid = pi.dwProcessId;\r
1631 \r
1632   if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1633 \r
1634   close_output_handles(&si);\r
1635 \r
1636   if (! service->no_console) FreeConsole();\r
1637 \r
1638   /* Restore our environment. */\r
1639   duplicate_environment(service->initial_env);\r
1640 \r
1641   if (service->affinity) {\r
1642     /*\r
1643       We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1644       so that we can parse it regardless of whether we're running in 32-bit\r
1645       or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1646       defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1647       (or when running the 32-bit NSSM).\r
1648 \r
1649       The result is a lot of seemingly-unnecessary casting throughout the code\r
1650       and potentially confusion when we actually try to start the service.\r
1651       Having said that, however, it's unlikely that we're actually going to\r
1652       run in 32-bit mode on a system which has more than 32 CPUs so the\r
1653       likelihood of seeing a confusing situation is somewhat diminished.\r
1654     */\r
1655     DWORD_PTR affinity, system_affinity;\r
1656 \r
1657     if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1658     else {\r
1659       affinity = (DWORD_PTR) service->affinity;\r
1660       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1661     }\r
1662 \r
1663     if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1664       log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1665     }\r
1666 \r
1667     ResumeThread(pi.hThread);\r
1668   }\r
1669 \r
1670   /*\r
1671     Wait for a clean startup before changing the service status to RUNNING\r
1672     but be mindful of the fact that we are blocking the service control manager\r
1673     so abandon the wait before too much time has elapsed.\r
1674   */\r
1675   unsigned long delay = service->throttle_delay;\r
1676   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
1677     TCHAR delay_milliseconds[16];\r
1678     _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
1679     TCHAR deadline_milliseconds[16];\r
1680     _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
1681     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
1682     delay = NSSM_SERVICE_STATUS_DEADLINE;\r
1683   }\r
1684   unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
1685 \r
1686   /* Signal successful start */\r
1687   service->status.dwCurrentState = SERVICE_RUNNING;\r
1688   SetServiceStatus(service->status_handle, &service->status);\r
1689 \r
1690   /* Continue waiting for a clean startup. */\r
1691   if (deadline == WAIT_TIMEOUT) {\r
1692     if (service->throttle_delay > delay) {\r
1693       if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
1694     }\r
1695     else service->throttle = 0;\r
1696   }\r
1697 \r
1698   /* Ensure the restart delay is always applied. */\r
1699   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1700 \r
1701   return 0;\r
1702 }\r
1703 \r
1704 /* Stop the service */\r
1705 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1706   service->allow_restart = false;\r
1707   if (service->wait_handle) {\r
1708     UnregisterWait(service->wait_handle);\r
1709     service->wait_handle = 0;\r
1710   }\r
1711 \r
1712   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1713 \r
1714   if (default_action && ! exitcode && ! graceful) {\r
1715     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
1716     graceful = true;\r
1717   }\r
1718 \r
1719   /* Signal we are stopping */\r
1720   if (graceful) {\r
1721     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1722     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1723     SetServiceStatus(service->status_handle, &service->status);\r
1724   }\r
1725 \r
1726   /* Nothing to do if service isn't running */\r
1727   if (service->pid) {\r
1728     /* Shut down service */\r
1729     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1730     kill_process(service, service->process_handle, service->pid, 0);\r
1731   }\r
1732   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1733 \r
1734   end_service((void *) service, true);\r
1735 \r
1736   /* Signal we stopped */\r
1737   if (graceful) {\r
1738     service->status.dwCurrentState = SERVICE_STOPPED;\r
1739     if (exitcode) {\r
1740       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1741       service->status.dwServiceSpecificExitCode = exitcode;\r
1742     }\r
1743     else {\r
1744       service->status.dwWin32ExitCode = NO_ERROR;\r
1745       service->status.dwServiceSpecificExitCode = 0;\r
1746     }\r
1747     SetServiceStatus(service->status_handle, &service->status);\r
1748   }\r
1749 \r
1750   return exitcode;\r
1751 }\r
1752 \r
1753 /* Callback function triggered when the server exits */\r
1754 void CALLBACK end_service(void *arg, unsigned char why) {\r
1755   nssm_service_t *service = (nssm_service_t *) arg;\r
1756 \r
1757   if (service->stopping) return;\r
1758 \r
1759   service->stopping = true;\r
1760 \r
1761   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1762 \r
1763   /* Use now as a dummy exit time. */\r
1764   GetSystemTimeAsFileTime(&service->exit_time);\r
1765 \r
1766   /* Check exit code */\r
1767   unsigned long exitcode = 0;\r
1768   TCHAR code[16];\r
1769   if (service->process_handle) {\r
1770     GetExitCodeProcess(service->process_handle, &exitcode);\r
1771     /* Check real exit time. */\r
1772     if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
1773     CloseHandle(service->process_handle);\r
1774   }\r
1775 \r
1776   service->process_handle = 0;\r
1777 \r
1778   /*\r
1779     Log that the service ended BEFORE logging about killing the process\r
1780     tree.  See below for the possible values of the why argument.\r
1781   */\r
1782   if (! why) {\r
1783     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1784     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1785   }\r
1786 \r
1787   /* Clean up. */\r
1788   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1789   if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
1790   service->pid = 0;\r
1791 \r
1792   /*\r
1793     The why argument is true if our wait timed out or false otherwise.\r
1794     Our wait is infinite so why will never be true when called by the system.\r
1795     If it is indeed true, assume we were called from stop_service() because\r
1796     this is a controlled shutdown, and don't take any restart action.\r
1797   */\r
1798   if (why) return;\r
1799   if (! service->allow_restart) return;\r
1800 \r
1801   /* What action should we take? */\r
1802   int action = NSSM_EXIT_RESTART;\r
1803   TCHAR action_string[ACTION_LEN];\r
1804   bool default_action;\r
1805   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1806     for (int i = 0; exit_action_strings[i]; i++) {\r
1807       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1808         action = i;\r
1809         break;\r
1810       }\r
1811     }\r
1812   }\r
1813 \r
1814   switch (action) {\r
1815     /* Try to restart the service or return failure code to service manager */\r
1816     case NSSM_EXIT_RESTART:\r
1817       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1818       while (monitor_service(service)) {\r
1819         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1820         Sleep(30000);\r
1821       }\r
1822     break;\r
1823 \r
1824     /* Do nothing, just like srvany would */\r
1825     case NSSM_EXIT_IGNORE:\r
1826       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1827       Sleep(INFINITE);\r
1828     break;\r
1829 \r
1830     /* Tell the service manager we are finished */\r
1831     case NSSM_EXIT_REALLY:\r
1832       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1833       stop_service(service, exitcode, true, default_action);\r
1834     break;\r
1835 \r
1836     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1837     case NSSM_EXIT_UNCLEAN:\r
1838       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1839       stop_service(service, exitcode, false, default_action);\r
1840       free_imports();\r
1841       exit(exitcode);\r
1842     break;\r
1843   }\r
1844 }\r
1845 \r
1846 void throttle_restart(nssm_service_t *service) {\r
1847   /* This can't be a restart if the service is already running. */\r
1848   if (! service->throttle++) return;\r
1849 \r
1850   unsigned long ms;\r
1851   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1852   TCHAR threshold[8], milliseconds[8];\r
1853 \r
1854   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1855   else ms = throttle_ms;\r
1856 \r
1857   if (service->throttle > 7) service->throttle = 8;\r
1858 \r
1859   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1860 \r
1861   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
1862   else {\r
1863     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
1864     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
1865   }\r
1866 \r
1867   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
1868   else if (service->throttle_timer) {\r
1869     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
1870     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
1871     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
1872   }\r
1873 \r
1874   service->status.dwCurrentState = SERVICE_PAUSED;\r
1875   SetServiceStatus(service->status_handle, &service->status);\r
1876 \r
1877   if (use_critical_section) {\r
1878     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
1879     LeaveCriticalSection(&service->throttle_section);\r
1880   }\r
1881   else {\r
1882     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
1883     else Sleep(ms);\r
1884   }\r
1885 }\r
1886 \r
1887 /*\r
1888   When responding to a stop (or any other) request we need to set dwWaitHint to\r
1889   the number of milliseconds we expect the operation to take, and optionally\r
1890   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
1891   operation completing or dwCheckPoint increasing, the system will consider the\r
1892   service to be hung.\r
1893 \r
1894   However the system will consider the service to be hung after 30000\r
1895   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
1896   changed.  Therefore if we want to wait longer than that we must periodically\r
1897   increase dwCheckPoint.\r
1898 \r
1899   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
1900   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
1901   time dwCheckPoint is also increased.\r
1902 \r
1903   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
1904   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
1905   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
1906   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
1907   dwWaitHint.\r
1908 \r
1909   Only doing both these things will prevent the system from killing the service.\r
1910 \r
1911   Returns: 1 if the wait timed out.\r
1912            0 if the wait completed.\r
1913           -1 on error.\r
1914 */\r
1915 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
1916   unsigned long interval;\r
1917   unsigned long waithint;\r
1918   unsigned long ret;\r
1919   unsigned long waited;\r
1920   TCHAR interval_milliseconds[16];\r
1921   TCHAR timeout_milliseconds[16];\r
1922   TCHAR waited_milliseconds[16];\r
1923   TCHAR *function = function_name;\r
1924 \r
1925   /* Add brackets to function name. */\r
1926   size_t funclen = _tcslen(function_name) + 3;\r
1927   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
1928   if (func) {\r
1929     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
1930   }\r
1931 \r
1932   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
1933 \r
1934   waithint = service->status.dwWaitHint;\r
1935   waited = 0;\r
1936   while (waited < timeout) {\r
1937     interval = timeout - waited;\r
1938     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
1939 \r
1940     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1941     service->status.dwWaitHint += interval;\r
1942     service->status.dwCheckPoint++;\r
1943     SetServiceStatus(service->status_handle, &service->status);\r
1944 \r
1945     if (waited) {\r
1946       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
1947       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
1948       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
1949     }\r
1950 \r
1951     switch (WaitForSingleObject(service->process_handle, interval)) {\r
1952       case WAIT_OBJECT_0:\r
1953         ret = 0;\r
1954         goto awaited;\r
1955 \r
1956       case WAIT_TIMEOUT:\r
1957         ret = 1;\r
1958       break;\r
1959 \r
1960       default:\r
1961         ret = -1;\r
1962         goto awaited;\r
1963     }\r
1964 \r
1965     waited += interval;\r
1966   }\r
1967 \r
1968 awaited:\r
1969   if (func) HeapFree(GetProcessHeap(), 0, func);\r
1970 \r
1971   return ret;\r
1972 }\r