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