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