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