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