remove dead breaks and returns
[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);\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   /* Pre-start hook. */\r
1725   unsigned long control = NSSM_SERVICE_CONTROL_START;\r
1726   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
1727     TCHAR code[16];\r
1728     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);\r
1729     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);\r
1730     unset_service_environment(service);\r
1731     return stop_service(service, 5, true, true);\r
1732   }\r
1733 \r
1734   /* Did another thread receive a stop control? */\r
1735   if (service->allow_restart) {\r
1736     /* Set up I/O redirection. */\r
1737     if (get_output_handles(service, &si)) {\r
1738       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
1739       if (! service->no_console) FreeConsole();\r
1740       close_output_handles(&si);\r
1741       unset_service_environment(service);\r
1742       return stop_service(service, 4, true, true);\r
1743     }\r
1744 \r
1745     /* The pre-start hook will have cleaned the environment. */\r
1746     set_service_environment(service);\r
1747 \r
1748     bool inherit_handles = false;\r
1749     if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
1750     unsigned long flags = service->priority & priority_mask();\r
1751     if (service->affinity) flags |= CREATE_SUSPENDED;\r
1752     if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
1753       unsigned long exitcode = 3;\r
1754       unsigned long error = GetLastError();\r
1755       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
1756       close_output_handles(&si);\r
1757       unset_service_environment(service);\r
1758       return stop_service(service, exitcode, true, true);\r
1759     }\r
1760     service->start_count++;\r
1761     service->process_handle = pi.hProcess;\r
1762     service->pid = pi.dwProcessId;\r
1763 \r
1764     if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
1765 \r
1766     close_output_handles(&si);\r
1767 \r
1768     if (! service->no_console) FreeConsole();\r
1769 \r
1770     if (service->affinity) {\r
1771       /*\r
1772         We are explicitly storing service->affinity as a 64-bit unsigned integer\r
1773         so that we can parse it regardless of whether we're running in 32-bit\r
1774         or 64-bit mode.  The arguments to SetProcessAffinityMask(), however, are\r
1775         defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
1776         (or when running the 32-bit NSSM).\r
1777 \r
1778         The result is a lot of seemingly-unnecessary casting throughout the code\r
1779         and potentially confusion when we actually try to start the service.\r
1780         Having said that, however, it's unlikely that we're actually going to\r
1781         run in 32-bit mode on a system which has more than 32 CPUs so the\r
1782         likelihood of seeing a confusing situation is somewhat diminished.\r
1783       */\r
1784       DWORD_PTR affinity, system_affinity;\r
1785 \r
1786       if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
1787       else {\r
1788         affinity = (DWORD_PTR) service->affinity;\r
1789         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1790       }\r
1791 \r
1792       if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
1793         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
1794       }\r
1795 \r
1796       ResumeThread(pi.hThread);\r
1797     }\r
1798   }\r
1799 \r
1800   /* Restore our environment. */\r
1801   unset_service_environment(service);\r
1802 \r
1803   /*\r
1804     Wait for a clean startup before changing the service status to RUNNING\r
1805     but be mindful of the fact that we are blocking the service control manager\r
1806     so abandon the wait before too much time has elapsed.\r
1807   */\r
1808   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
1809 \r
1810   /* Did another thread receive a stop control? */\r
1811   if (! service->allow_restart) return 0;\r
1812 \r
1813   /* Signal successful start */\r
1814   service->status.dwCurrentState = SERVICE_RUNNING;\r
1815   service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;\r
1816   SetServiceStatus(service->status_handle, &service->status);\r
1817 \r
1818   /* Post-start hook. */\r
1819   if (! service->throttle) {\r
1820     (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);\r
1821   }\r
1822 \r
1823   /* Ensure the restart delay is always applied. */\r
1824   if (service->restart_delay && ! service->throttle) service->throttle++;\r
1825 \r
1826   return 0;\r
1827 }\r
1828 \r
1829 /* Stop the service */\r
1830 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
1831   service->allow_restart = false;\r
1832   if (service->wait_handle) {\r
1833     UnregisterWait(service->wait_handle);\r
1834     service->wait_handle = 0;\r
1835   }\r
1836 \r
1837   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1838 \r
1839   if (default_action && ! exitcode && ! graceful) {\r
1840     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
1841     graceful = true;\r
1842   }\r
1843 \r
1844   /* Signal we are stopping */\r
1845   if (graceful) {\r
1846     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1847     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
1848     SetServiceStatus(service->status_handle, &service->status);\r
1849   }\r
1850 \r
1851   /* Nothing to do if service isn't running */\r
1852   if (service->pid) {\r
1853     /* Shut down service */\r
1854     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
1855     kill_t k;\r
1856     service_kill_t(service, &k);\r
1857     k.exitcode = 0;\r
1858     kill_process(&k);\r
1859   }\r
1860   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
1861 \r
1862   end_service((void *) service, true);\r
1863 \r
1864   /* Signal we stopped */\r
1865   if (graceful) {\r
1866     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
1867     wait_for_hooks(service, true);\r
1868     service->status.dwCurrentState = SERVICE_STOPPED;\r
1869     if (exitcode) {\r
1870       service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
1871       service->status.dwServiceSpecificExitCode = exitcode;\r
1872     }\r
1873     else {\r
1874       service->status.dwWin32ExitCode = NO_ERROR;\r
1875       service->status.dwServiceSpecificExitCode = 0;\r
1876     }\r
1877     SetServiceStatus(service->status_handle, &service->status);\r
1878   }\r
1879 \r
1880   return exitcode;\r
1881 }\r
1882 \r
1883 /* Callback function triggered when the server exits */\r
1884 void CALLBACK end_service(void *arg, unsigned char why) {\r
1885   nssm_service_t *service = (nssm_service_t *) arg;\r
1886 \r
1887   if (service->stopping) return;\r
1888 \r
1889   service->stopping = true;\r
1890 \r
1891   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
1892 \r
1893   /* Use now as a dummy exit time. */\r
1894   GetSystemTimeAsFileTime(&service->exit_time);\r
1895 \r
1896   /* Check exit code */\r
1897   unsigned long exitcode = 0;\r
1898   TCHAR code[16];\r
1899   if (service->process_handle) {\r
1900     GetExitCodeProcess(service->process_handle, &exitcode);\r
1901     service->exitcode = exitcode;\r
1902     /* Check real exit time. */\r
1903     if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
1904     CloseHandle(service->process_handle);\r
1905   }\r
1906 \r
1907   service->process_handle = 0;\r
1908 \r
1909   /*\r
1910     Log that the service ended BEFORE logging about killing the process\r
1911     tree.  See below for the possible values of the why argument.\r
1912   */\r
1913   if (! why) {\r
1914     _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);\r
1915     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
1916   }\r
1917 \r
1918   /* Clean up. */\r
1919   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
1920   if (service->pid && service->kill_process_tree) {\r
1921     kill_t k;\r
1922     service_kill_t(service, &k);\r
1923     kill_process_tree(&k, service->pid);\r
1924   }\r
1925   service->pid = 0;\r
1926 \r
1927   /* Exit hook. */\r
1928   service->exit_count++;\r
1929   (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);\r
1930 \r
1931   /*\r
1932     The why argument is true if our wait timed out or false otherwise.\r
1933     Our wait is infinite so why will never be true when called by the system.\r
1934     If it is indeed true, assume we were called from stop_service() because\r
1935     this is a controlled shutdown, and don't take any restart action.\r
1936   */\r
1937   if (why) return;\r
1938   if (! service->allow_restart) return;\r
1939 \r
1940   /* What action should we take? */\r
1941   int action = NSSM_EXIT_RESTART;\r
1942   TCHAR action_string[ACTION_LEN];\r
1943   bool default_action;\r
1944   if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
1945     for (int i = 0; exit_action_strings[i]; i++) {\r
1946       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
1947         action = i;\r
1948         break;\r
1949       }\r
1950     }\r
1951   }\r
1952 \r
1953   switch (action) {\r
1954     /* Try to restart the service or return failure code to service manager */\r
1955     case NSSM_EXIT_RESTART:\r
1956       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
1957       while (monitor_service(service)) {\r
1958         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
1959         Sleep(30000);\r
1960       }\r
1961     break;\r
1962 \r
1963     /* Do nothing, just like srvany would */\r
1964     case NSSM_EXIT_IGNORE:\r
1965       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
1966       wait_for_hooks(service, false);\r
1967       Sleep(INFINITE);\r
1968     break;\r
1969 \r
1970     /* Tell the service manager we are finished */\r
1971     case NSSM_EXIT_REALLY:\r
1972       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
1973       stop_service(service, exitcode, true, default_action);\r
1974     break;\r
1975 \r
1976     /* Fake a crash so pre-Vista service managers will run recovery actions. */\r
1977     case NSSM_EXIT_UNCLEAN:\r
1978       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
1979       stop_service(service, exitcode, false, default_action);\r
1980       wait_for_hooks(service, false);\r
1981       free_imports();\r
1982       exit(exitcode);\r
1983   }\r
1984 }\r
1985 \r
1986 void throttle_restart(nssm_service_t *service) {\r
1987   /* This can't be a restart if the service is already running. */\r
1988   if (! service->throttle++) return;\r
1989 \r
1990   unsigned long ms;\r
1991   unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
1992   TCHAR threshold[8], milliseconds[8];\r
1993 \r
1994   if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
1995   else ms = throttle_ms;\r
1996 \r
1997   _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
1998 \r
1999   if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
2000   else {\r
2001     _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
2002     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
2003   }\r
2004 \r
2005   if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
2006   else if (service->throttle_timer) {\r
2007     ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
2008     service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
2009     SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
2010   }\r
2011 \r
2012   service->status.dwCurrentState = SERVICE_PAUSED;\r
2013   service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;\r
2014   SetServiceStatus(service->status_handle, &service->status);\r
2015 \r
2016   if (use_critical_section) {\r
2017     imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
2018     LeaveCriticalSection(&service->throttle_section);\r
2019   }\r
2020   else {\r
2021     if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
2022     else Sleep(ms);\r
2023   }\r
2024 }\r
2025 \r
2026 /*\r
2027   When responding to a stop (or any other) request we need to set dwWaitHint to\r
2028   the number of milliseconds we expect the operation to take, and optionally\r
2029   increase dwCheckPoint.  If dwWaitHint milliseconds elapses without the\r
2030   operation completing or dwCheckPoint increasing, the system will consider the\r
2031   service to be hung.\r
2032 \r
2033   However the system will consider the service to be hung after 30000\r
2034   milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
2035   changed.  Therefore if we want to wait longer than that we must periodically\r
2036   increase dwCheckPoint.\r
2037 \r
2038   Furthermore, it will consider the service to be hung after 60000 milliseconds\r
2039   regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
2040   time dwCheckPoint is also increased.\r
2041 \r
2042   Our strategy then is to retrieve the initial dwWaitHint and wait for\r
2043   NSSM_SERVICE_STATUS_DEADLINE milliseconds.  If the process is still running\r
2044   and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
2045   smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
2046   dwWaitHint.\r
2047 \r
2048   Only doing both these things will prevent the system from killing the service.\r
2049 \r
2050   If the status_handle and service_status arguments are omitted, this function\r
2051   will not try to update the service manager but it will still log to the\r
2052   event log that it is waiting for a handle.\r
2053 \r
2054   Returns: 1 if the wait timed out.\r
2055            0 if the wait completed.\r
2056           -1 on error.\r
2057 */\r
2058 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {\r
2059   unsigned long interval;\r
2060   unsigned long ret;\r
2061   unsigned long waited;\r
2062   TCHAR interval_milliseconds[16];\r
2063   TCHAR timeout_milliseconds[16];\r
2064   TCHAR waited_milliseconds[16];\r
2065   TCHAR *function = function_name;\r
2066 \r
2067   /* Add brackets to function name. */\r
2068   size_t funclen = _tcslen(function_name) + 3;\r
2069   TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
2070   if (func) {\r
2071     if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
2072   }\r
2073 \r
2074   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
2075 \r
2076   waited = 0;\r
2077   while (waited < timeout) {\r
2078     interval = timeout - waited;\r
2079     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
2080 \r
2081     if (status) {\r
2082       status->dwWaitHint += interval;\r
2083       status->dwCheckPoint++;\r
2084       SetServiceStatus(status_handle, status);\r
2085     }\r
2086 \r
2087     if (waited) {\r
2088       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
2089       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
2090       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
2091     }\r
2092 \r
2093     switch (WaitForSingleObject(handle, interval)) {\r
2094       case WAIT_OBJECT_0:\r
2095         ret = 0;\r
2096         goto awaited;\r
2097 \r
2098       case WAIT_TIMEOUT:\r
2099         ret = 1;\r
2100         break;\r
2101 \r
2102       default:\r
2103         ret = -1;\r
2104         goto awaited;\r
2105     }\r
2106 \r
2107     waited += interval;\r
2108   }\r
2109 \r
2110 awaited:\r
2111   if (func) HeapFree(GetProcessHeap(), 0, func);\r
2112 \r
2113   return ret;\r
2114 }\r
2115 \r
2116 int list_nssm_services() {\r
2117   /* Open service manager. */\r
2118   SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
2119   if (! services) {\r
2120     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
2121     return 1;\r
2122   }\r
2123 \r
2124   unsigned long bufsize, required, count, i;\r
2125   unsigned long resume = 0;\r
2126   EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);\r
2127   unsigned long error = GetLastError();\r
2128   if (error != ERROR_MORE_DATA) {\r
2129     print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));\r
2130     return 2;\r
2131   }\r
2132 \r
2133   ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);\r
2134   if (! status) {\r
2135     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));\r
2136     return 3;\r
2137   }\r
2138 \r
2139   bufsize = required;\r
2140   while (true) {\r
2141     int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);\r
2142     if (! ret) {\r
2143       error = GetLastError();\r
2144       if (error != ERROR_MORE_DATA) {\r
2145         HeapFree(GetProcessHeap(), 0, status);\r
2146         print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));\r
2147         return 4;\r
2148       }\r
2149     }\r
2150 \r
2151     for (i = 0; i < count; i++) {\r
2152       /* Try to get the service parameters. */\r
2153       nssm_service_t *service = alloc_nssm_service();\r
2154       if (! service) {\r
2155         HeapFree(GetProcessHeap(), 0, status);\r
2156         print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));\r
2157         return 5;\r
2158       }\r
2159       _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);\r
2160 \r
2161       get_parameters(service, 0);\r
2162       /* We manage the service if we have an Application. */\r
2163       if (service->exe[0]) _tprintf(_T("%s\n"), service->name);\r
2164 \r
2165       cleanup_nssm_service(service);\r
2166     }\r
2167 \r
2168     if (ret) break;\r
2169   }\r
2170 \r
2171   HeapFree(GetProcessHeap(), 0, status);\r
2172   return 0;\r
2173 }\r