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