4 bool use_critical_section;
\r
6 extern imports_t imports;
\r
7 extern settings_t settings[];
\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
13 static hook_thread_t hook_threads = { NULL, 0 };
\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
26 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
28 case NSSM_SERVICE_CONTROL_START:
\r
30 case SERVICE_START_PENDING:
\r
33 case SERVICE_RUNNING:
\r
40 case SERVICE_CONTROL_STOP:
\r
41 case SERVICE_CONTROL_SHUTDOWN:
\r
43 case SERVICE_RUNNING:
\r
44 case SERVICE_STOP_PENDING:
\r
47 case SERVICE_STOPPED:
\r
54 case SERVICE_CONTROL_PAUSE:
\r
56 case SERVICE_PAUSE_PENDING:
\r
59 case SERVICE_PAUSED:
\r
66 case SERVICE_CONTROL_CONTINUE:
\r
68 case SERVICE_CONTINUE_PENDING:
\r
71 case SERVICE_RUNNING:
\r
78 case SERVICE_CONTROL_INTERROGATE:
\r
79 case NSSM_SERVICE_CONTROL_ROTATE:
\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
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
101 else return response;
\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
110 /* On a clean shutdown we need to keep the service's status up-to-date. */
\r
112 status_handle = service->status_handle;
\r
113 status = &service->status;
\r
116 status_handle = NULL;
\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
125 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
126 if (! string) return 1;
\r
134 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
136 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\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
144 set[n].first = set[n].last = (int) i;
\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
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
160 HeapFree(GetProcessHeap(), 0, *string);
\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
168 HeapFree(GetProcessHeap(), 0, *string);
\r
179 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
180 if (! mask) return 1;
\r
183 if (! string) return 0;
\r
192 unsigned long number;
\r
194 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
198 ret = str_number(s, &number, &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
214 if (! *(++s)) return 3;
\r
215 ret = str_number(s, &number, &end);
\r
216 if (ret == 0 || ret == 2) {
\r
218 if (! *s || *s == _T(',')) {
\r
219 set[n].last = (int) number;
\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
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
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
255 return NSSM_NORMAL_PRIORITY;
\r
258 unsigned long priority_index_to_constant(int 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
266 return NORMAL_PRIORITY_CLASS;
\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
276 void set_service_environment(nssm_service_t *service) {
\r
277 if (! service) return;
\r
280 We have to duplicate the block because this function will be called
\r
281 multiple times between registry reads.
\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
288 set_environment_block(env_extra);
\r
289 HeapFree(GetProcessHeap(), 0, env_extra);
\r
292 void unset_service_environment(nssm_service_t *service) {
\r
293 if (! service) return;
\r
294 duplicate_environment_strings(service->initial_env);
\r
298 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
299 control immediately.
\r
301 static unsigned long WINAPI shutdown_service(void *arg) {
\r
302 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
306 Wrapper to be called in a new thread so that we can acknowledge start
\r
309 static unsigned long WINAPI launch_service(void *arg) {
\r
310 return monitor_service((nssm_service_t *) arg);
\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
317 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\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
335 return service_handle;
\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
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
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
359 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
361 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
365 bufsize = required;
\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
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
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
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
392 HeapFree(GetProcessHeap(), 0, status);
\r
393 return open_service(services, canonical_name, access, 0, 0);
\r
400 /* Recurse so we can get an error message. */
\r
401 return open_service(services, service_name, access, 0, 0);
\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
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
414 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
419 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\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
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
439 size_t len = _tcslen(group) + 1;
\r
440 *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
442 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("prepend_service_group_identifier()"));
\r
447 *s++ = SC_GROUP_IDENTIFIER;
\r
448 memmove(s, group, len * sizeof(TCHAR));
\r
449 (*canon)[len] = _T('\0');
\r
454 int append_to_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) {
\r
458 if (type == DEPENDENCY_GROUPS) {
\r
459 if (prepend_service_group_identifier(string, &canon)) return 1;
\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
468 int remove_from_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) {
\r
472 if (type == DEPENDENCY_GROUPS) {
\r
473 if (prepend_service_group_identifier(string, &canon)) return 1;
\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
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
486 if (buffer && buffer[0]) {
\r
487 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
489 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
494 Count the dependencies then allocate a buffer big enough for their
\r
495 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
499 for (s = buffer; *s; s++) {
\r
500 num_dependencies++;
\r
501 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
505 /* At least one dependency is a group so we need to verify them. */
\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
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
519 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\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
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
541 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
542 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
545 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
546 for (s = buffer; *s; s++) {
\r
548 if (*s == SC_GROUP_IDENTIFIER) {
\r
549 TCHAR *group = s + 1;
\r
553 for (TCHAR *g = groups; *g; g++) {
\r
554 if (str_equiv(g, group)) {
\r
556 /* Set canonical name. */
\r
557 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
565 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\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
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
584 size_t len = _tcslen(dependency) + 1;
\r
585 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
591 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
592 CloseServiceHandle(services);
\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
601 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\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
612 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
613 if (! qsc) return 3;
\r
615 if (! qsc->lpDependencies || ! qsc->lpDependencies[0]) {
\r
616 HeapFree(GetProcessHeap(), 0, qsc);
\r
620 /* lpDependencies is doubly NULL terminated. */
\r
621 while (qsc->lpDependencies[*bufsize]) {
\r
622 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
628 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
631 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
632 HeapFree(GetProcessHeap(), 0, qsc);
\r
636 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\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
655 HeapFree(GetProcessHeap(), 0, qsc);
\r
657 if (! *buffer[0]) {
\r
658 HeapFree(GetProcessHeap(), 0, *buffer);
\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
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
674 lpDescription must be NULL if we aren't changing, the new description
\r
677 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
678 else description.lpDescription = _T("");
\r
680 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
682 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\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
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
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
706 HeapFree(GetProcessHeap(), 0, description);
\r
707 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
712 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\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
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
726 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\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
736 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\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
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
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
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
768 if (! qsc) return 1;
\r
770 if (qsc->lpServiceStartName[0]) {
\r
771 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
773 size_t len = _tcslen(qsc->lpServiceStartName);
\r
774 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
776 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
780 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
781 *usernamelen = len;
\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
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
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
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
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
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
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
848 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
851 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
853 /* Arguments are optional */
\r
854 size_t flagslen = 0;
\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
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
868 if (i < argc - 1) service->flags[s++] = _T(' ');
\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
875 int ret = install_service(service);
\r
876 cleanup_nssm_service(service);
\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
885 /* Are we editing on the command line? */
\r
886 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING, MODE_DUMPING } 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
892 /* Minimum number of arguments. */
\r
894 /* Index of first value. */
\r
897 if (str_equiv(verb, _T("get"))) {
\r
899 mode = MODE_GETTING;
\r
901 else if (str_equiv(verb, _T("set"))) {
\r
903 mode = MODE_SETTING;
\r
905 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
907 mode = MODE_RESETTING;
\r
909 else if (str_equiv(verb, _T("dump"))) {
\r
912 mode = MODE_DUMPING;
\r
914 if (argc < mandatory) return usage(1);
\r
916 const TCHAR *parameter = 0;
\r
917 settings_t *setting = 0;
\r
920 /* Validate the parameter. */
\r
921 if (mandatory > 2) {
\r
922 bool additional_mandatory = false;
\r
924 parameter = argv[2];
\r
925 for (i = 0; settings[i].name; i++) {
\r
926 setting = &settings[i];
\r
927 if (! str_equiv(setting->name, parameter)) continue;
\r
928 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
929 additional_mandatory = true;
\r
934 if (! settings[i].name) {
\r
935 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
936 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
941 if (additional_mandatory) {
\r
942 if (argc < mandatory) {
\r
943 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
946 additional = argv[3];
\r
949 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
950 additional = argv[3];
\r
954 additional = argv[remainder];
\r
955 if (argc < mandatory) return usage(1);
\r
959 nssm_service_t *service = alloc_nssm_service();
\r
960 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
962 /* Open service manager */
\r
963 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
965 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
969 /* Try to open the service */
\r
970 unsigned long access = SERVICE_QUERY_CONFIG;
\r
971 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
972 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
973 if (! service->handle) {
\r
974 CloseServiceHandle(services);
\r
978 /* Get system details. */
\r
979 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
981 CloseServiceHandle(service->handle);
\r
982 CloseServiceHandle(services);
\r
986 service->type = qsc->dwServiceType;
\r
987 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
988 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
989 HeapFree(GetProcessHeap(), 0, qsc);
\r
990 CloseServiceHandle(service->handle);
\r
991 CloseServiceHandle(services);
\r
992 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
997 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
998 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
999 HeapFree(GetProcessHeap(), 0, qsc);
\r
1000 CloseServiceHandle(service->handle);
\r
1001 CloseServiceHandle(services);
\r
1006 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
1007 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1008 HeapFree(GetProcessHeap(), 0, qsc);
\r
1009 CloseServiceHandle(service->handle);
\r
1010 CloseServiceHandle(services);
\r
1015 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
1017 /* Get the canonical service name. We open it case insensitively. */
\r
1018 unsigned long bufsize = _countof(service->name);
\r
1019 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1021 /* Remember the executable in case it isn't NSSM. */
\r
1022 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
1023 HeapFree(GetProcessHeap(), 0, qsc);
\r
1025 /* Get extended system details. */
\r
1026 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
1027 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1028 CloseServiceHandle(service->handle);
\r
1029 CloseServiceHandle(services);
\r
1034 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
1035 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1036 CloseServiceHandle(service->handle);
\r
1037 CloseServiceHandle(services);
\r
1042 /* Get NSSM details. */
\r
1043 get_parameters(service, 0);
\r
1045 CloseServiceHandle(services);
\r
1047 if (! service->exe[0]) {
\r
1048 service->native = true;
\r
1049 if (mode != MODE_GETTING && mode != MODE_DUMPING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
1052 /* Editing with the GUI. */
\r
1053 if (mode == MODE_EDITING) {
\r
1054 nssm_gui(IDD_EDIT, service);
\r
1062 if (mode == MODE_DUMPING) {
\r
1063 TCHAR *service_name = service->name;
\r
1064 if (argc > remainder) service_name = argv[remainder];
\r
1065 if (service->native) key = 0;
\r
1067 key = open_registry(service->name, KEY_READ);
\r
1068 if (! key) return 4;
\r
1071 TCHAR quoted_service_name[SERVICE_NAME_LENGTH * 2];
\r
1072 TCHAR quoted_exe[EXE_LENGTH * 2];
\r
1073 TCHAR quoted_nssm[EXE_LENGTH * 2];
\r
1074 if (quote(service_name, quoted_service_name, _countof(quoted_service_name))) return 5;
\r
1075 if (quote(service->exe, quoted_exe, _countof(quoted_exe))) return 6;
\r
1076 if (quote(nssm_exe(), quoted_nssm, _countof(quoted_nssm))) return 6;
\r
1077 _tprintf(_T("%s install %s %s\n"), quoted_nssm, quoted_service_name, quoted_exe);
\r
1080 for (i = 0; settings[i].name; i++) {
\r
1081 setting = &settings[i];
\r
1082 if (! setting->native && service->native) continue;
\r
1083 if (dump_setting(service_name, key, service->handle, setting)) ret++;
\r
1086 if (! service->native) RegCloseKey(key);
\r
1087 CloseServiceHandle(service->handle);
\r
1089 if (ret) return 1;
\r
1093 /* Trying to manage App* parameters for a non-NSSM service. */
\r
1094 if (! setting->native && service->native) {
\r
1095 CloseServiceHandle(service->handle);
\r
1096 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1100 if (mode == MODE_GETTING) {
\r
1101 if (! service->native) {
\r
1102 key = open_registry(service->name, KEY_READ);
\r
1103 if (! key) return 4;
\r
1106 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1107 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1109 CloseServiceHandle(service->handle);
\r
1113 switch (setting->type) {
\r
1114 case REG_EXPAND_SZ:
\r
1115 case REG_MULTI_SZ:
\r
1117 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1118 HeapFree(GetProcessHeap(), 0, value.string);
\r
1122 _tprintf(_T("%lu\n"), value.numeric);
\r
1126 if (! service->native) RegCloseKey(key);
\r
1127 CloseServiceHandle(service->handle);
\r
1131 /* Build the value. */
\r
1132 if (mode == MODE_RESETTING) {
\r
1133 /* Unset the parameter. */
\r
1136 else if (remainder == argc) {
\r
1140 /* Set the parameter. */
\r
1142 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1143 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1146 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1147 if (! value.string) {
\r
1148 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1149 CloseServiceHandle(service->handle);
\r
1154 for (i = remainder; i < argc; i++) {
\r
1155 size_t len = _tcslen(argv[i]);
\r
1156 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1158 if (i < argc - 1) {
\r
1159 if (setting->additional & ADDITIONAL_CRLF) {
\r
1160 value.string[s++] = _T('\r');
\r
1161 value.string[s++] = _T('\n');
\r
1163 else value.string[s++] = _T(' ');
\r
1166 value.string[s] = _T('\0');
\r
1169 if (! service->native) {
\r
1170 key = open_registry(service->name, KEY_READ | KEY_WRITE);
\r
1172 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1177 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1178 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1179 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1181 if (! service->native) RegCloseKey(key);
\r
1182 CloseServiceHandle(service->handle);
\r
1186 if (! service->native) RegCloseKey(key);
\r
1187 CloseServiceHandle(service->handle);
\r
1192 /* About to remove the service */
\r
1193 int pre_remove_service(int argc, TCHAR **argv) {
\r
1194 nssm_service_t *service = alloc_nssm_service();
\r
1195 set_nssm_service_defaults(service);
\r
1196 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1198 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1199 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1200 if (str_equiv(argv[1], _T("confirm"))) {
\r
1201 int ret = remove_service(service);
\r
1202 cleanup_nssm_service(service);
\r
1205 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1209 /* Install the service */
\r
1210 int install_service(nssm_service_t *service) {
\r
1211 if (! service) return 1;
\r
1213 /* Open service manager */
\r
1214 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1216 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1217 cleanup_nssm_service(service);
\r
1221 /* Get path of this program */
\r
1222 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1224 /* Create the service - settings will be changed in edit_service() */
\r
1225 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
1226 if (! service->handle) {
\r
1227 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1228 CloseServiceHandle(services);
\r
1232 if (edit_service(service, false)) {
\r
1233 DeleteService(service->handle);
\r
1234 CloseServiceHandle(services);
\r
1238 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1241 CloseServiceHandle(services);
\r
1246 /* Edit the service. */
\r
1247 int edit_service(nssm_service_t *service, bool editing) {
\r
1248 if (! service) return 1;
\r
1251 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1252 and SERVICE_INTERACTIVE_PROCESS.
\r
1254 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1255 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1257 /* Startup type. */
\r
1258 unsigned long startup;
\r
1259 switch (service->startup) {
\r
1260 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1261 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1262 default: startup = SERVICE_AUTO_START;
\r
1265 /* Display name. */
\r
1266 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1269 Username must be NULL if we aren't changing or an account name.
\r
1270 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1271 Password must be NULL if we aren't changing, a password or "".
\r
1272 Empty passwords are valid but we won't allow them in the GUI.
\r
1274 TCHAR *username = 0;
\r
1276 TCHAR *password = 0;
\r
1277 boolean virtual_account = false;
\r
1278 if (service->usernamelen) {
\r
1279 username = service->username;
\r
1280 if (is_virtual_account(service->name, username)) {
\r
1281 virtual_account = true;
\r
1282 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (service->usernamelen + 1) * sizeof(TCHAR));
\r
1284 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("edit_service()"));
\r
1287 memmove(canon, username, (service->usernamelen + 1) * sizeof(TCHAR));
\r
1290 if (canonicalise_username(username, &canon)) return 5;
\r
1291 if (service->passwordlen) password = service->password;
\r
1294 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1296 if (! virtual_account) {
\r
1297 if (well_known_username(canon)) password = _T("");
\r
1299 if (grant_logon_as_service(canon)) {
\r
1300 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1301 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1307 TCHAR *dependencies = _T("");
\r
1308 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1310 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1311 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1312 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1315 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1317 if (service->dependencieslen) {
\r
1318 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1321 if (service->description[0] || editing) {
\r
1322 set_service_description(service->name, service->handle, service->description);
\r
1325 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1326 ZeroMemory(&delayed, sizeof(delayed));
\r
1327 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1328 else delayed.fDelayedAutostart = 0;
\r
1329 /* Delayed startup isn't supported until Vista. */
\r
1330 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1331 unsigned long error = GetLastError();
\r
1332 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1333 if (error != ERROR_INVALID_LEVEL) {
\r
1334 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1338 /* Don't mess with parameters which aren't ours. */
\r
1339 if (! service->native) {
\r
1340 /* Now we need to put the parameters into the registry */
\r
1341 if (create_parameters(service, editing)) {
\r
1342 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1346 set_service_recovery(service);
\r
1352 /* Control a service. */
\r
1353 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1354 if (argc < 1) return usage(1);
\r
1355 TCHAR *service_name = argv[0];
\r
1356 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1358 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1360 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1364 unsigned long access = SERVICE_QUERY_STATUS;
\r
1365 switch (control) {
\r
1366 case NSSM_SERVICE_CONTROL_START:
\r
1367 access |= SERVICE_START;
\r
1370 case SERVICE_CONTROL_CONTINUE:
\r
1371 case SERVICE_CONTROL_PAUSE:
\r
1372 access |= SERVICE_PAUSE_CONTINUE;
\r
1375 case SERVICE_CONTROL_STOP:
\r
1376 access |= SERVICE_STOP;
\r
1379 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1380 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1384 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1385 if (! service_handle) {
\r
1386 CloseServiceHandle(services);
\r
1391 unsigned long error;
\r
1392 SERVICE_STATUS service_status;
\r
1393 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1394 unsigned long initial_status = SERVICE_STOPPED;
\r
1395 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1396 error = GetLastError();
\r
1397 CloseServiceHandle(services);
\r
1399 if (error == ERROR_IO_PENDING) {
\r
1401 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1402 indicate that the operation is still in progress. Newer versions
\r
1403 will return it if there really is a delay.
\r
1406 error = ERROR_SUCCESS;
\r
1410 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1411 CloseServiceHandle(service_handle);
\r
1414 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1417 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1421 CloseServiceHandle(service_handle);
\r
1422 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1426 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1428 We could actually send an INTERROGATE control but that won't return
\r
1429 any information if the service is stopped and we don't care about
\r
1430 the extra details it might give us in any case. So we'll fake it.
\r
1432 ret = QueryServiceStatus(service_handle, &service_status);
\r
1433 error = GetLastError();
\r
1436 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1440 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1445 ret = ControlService(service_handle, control, &service_status);
\r
1446 unsigned long initial_status = service_status.dwCurrentState;
\r
1447 error = GetLastError();
\r
1448 CloseServiceHandle(services);
\r
1450 if (error == ERROR_IO_PENDING) {
\r
1452 error = ERROR_SUCCESS;
\r
1456 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1457 CloseServiceHandle(service_handle);
\r
1460 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1463 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1467 CloseServiceHandle(service_handle);
\r
1468 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1469 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1470 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1477 /* Remove the service */
\r
1478 int remove_service(nssm_service_t *service) {
\r
1479 if (! service) return 1;
\r
1481 /* Open service manager */
\r
1482 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1484 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1488 /* Try to open the service */
\r
1489 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1490 if (! service->handle) {
\r
1491 CloseServiceHandle(services);
\r
1495 /* Get the canonical service name. We open it case insensitively. */
\r
1496 unsigned long bufsize = _countof(service->displayname);
\r
1497 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1498 bufsize = _countof(service->name);
\r
1499 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1501 /* Try to delete the service */
\r
1502 if (! DeleteService(service->handle)) {
\r
1503 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1504 CloseServiceHandle(services);
\r
1509 CloseServiceHandle(services);
\r
1511 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1515 /* Service initialisation */
\r
1516 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1517 nssm_service_t *service = alloc_nssm_service();
\r
1518 if (! service) return;
\r
1520 static volatile bool await_debugger = (argc > 1 && str_equiv(argv[1], _T("debug")));
\r
1521 while (await_debugger) Sleep(1000);
\r
1523 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1524 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1528 /* We can use a condition variable in a critical section on Vista or later. */
\r
1529 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1530 else use_critical_section = false;
\r
1532 /* Initialise status */
\r
1533 ZeroMemory(&service->status, sizeof(service->status));
\r
1534 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1535 service->status.dwControlsAccepted = 0;
\r
1536 service->status.dwWin32ExitCode = NO_ERROR;
\r
1537 service->status.dwServiceSpecificExitCode = 0;
\r
1538 service->status.dwCheckPoint = 0;
\r
1539 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1541 /* Signal we AREN'T running the server */
\r
1542 service->process_handle = 0;
\r
1545 /* Register control handler */
\r
1546 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1547 if (! service->status_handle) {
\r
1548 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1552 log_service_control(service->name, 0, true);
\r
1554 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1555 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1556 SetServiceStatus(service->status_handle, &service->status);
\r
1559 /* Try to create the exit action parameters; we don't care if it fails */
\r
1560 create_exit_action(service->name, exit_action_strings[0], false);
\r
1562 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1564 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1565 set_service_recovery(service);
\r
1567 /* Remember our display name. */
\r
1568 unsigned long displayname_len = _countof(service->displayname);
\r
1569 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1571 CloseServiceHandle(services);
\r
1575 /* Used for signalling a resume if the service pauses when throttled. */
\r
1576 if (use_critical_section) {
\r
1577 InitializeCriticalSection(&service->throttle_section);
\r
1578 service->throttle_section_initialised = true;
\r
1581 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1582 if (! service->throttle_timer) {
\r
1583 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1587 /* Critical section for hooks. */
\r
1588 InitializeCriticalSection(&service->hook_section);
\r
1589 service->hook_section_initialised = true;
\r
1591 /* Remember our initial environment. */
\r
1592 service->initial_env = copy_environment();
\r
1594 /* Remember our creation time. */
\r
1595 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1597 service->allow_restart = true;
\r
1598 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1599 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1600 stop_service(service, 0, true, true);
\r
1604 /* Make sure service recovery actions are taken where necessary */
\r
1605 void set_service_recovery(nssm_service_t *service) {
\r
1606 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1607 ZeroMemory(&flag, sizeof(flag));
\r
1608 flag.fFailureActionsOnNonCrashFailures = true;
\r
1610 /* This functionality was added in Vista so the call may fail */
\r
1611 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1612 unsigned long error = GetLastError();
\r
1613 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1614 if (error != ERROR_INVALID_LEVEL) {
\r
1615 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1620 int monitor_service(nssm_service_t *service) {
\r
1621 /* Set service status to started */
\r
1622 int ret = start_service(service);
\r
1625 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1626 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1629 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1631 /* Monitor service */
\r
1632 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1633 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1639 TCHAR *service_control_text(unsigned long control) {
\r
1640 switch (control) {
\r
1641 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1642 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1643 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1644 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1645 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1646 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1647 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1648 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1649 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1650 default: return 0;
\r
1654 TCHAR *service_status_text(unsigned long status) {
\r
1656 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1657 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1658 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1659 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1660 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1661 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1662 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1663 default: return 0;
\r
1667 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1668 TCHAR *text = service_control_text(control);
\r
1669 unsigned long event;
\r
1672 /* "0x" + 8 x hex + NULL */
\r
1673 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1675 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1678 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1679 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1680 HeapFree(GetProcessHeap(), 0, text);
\r
1684 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1686 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1687 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1689 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1691 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1692 HeapFree(GetProcessHeap(), 0, text);
\r
1696 /* Service control handler */
\r
1697 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1698 nssm_service_t *service = (nssm_service_t *) context;
\r
1700 switch (control) {
\r
1701 case SERVICE_CONTROL_INTERROGATE:
\r
1702 /* We always keep the service status up-to-date so this is a no-op. */
\r
1705 case SERVICE_CONTROL_SHUTDOWN:
\r
1706 case SERVICE_CONTROL_STOP:
\r
1707 service->last_control = control;
\r
1708 log_service_control(service->name, control, true);
\r
1710 /* Immediately block further controls. */
\r
1711 service->allow_restart = false;
\r
1712 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1713 service->status.dwControlsAccepted = 0;
\r
1714 SetServiceStatus(service->status_handle, &service->status);
\r
1716 /* Pre-stop hook. */
\r
1717 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1720 We MUST acknowledge the stop request promptly but we're committed to
\r
1721 waiting for the application to exit. Spawn a new thread to wait
\r
1722 while we acknowledge the request.
\r
1724 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1725 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1728 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1729 to complete in time in this thread.
\r
1731 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1732 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1733 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1735 stop_service(service, 0, true, true);
\r
1739 case SERVICE_CONTROL_CONTINUE:
\r
1740 service->last_control = control;
\r
1741 log_service_control(service->name, control, true);
\r
1742 service->throttle = 0;
\r
1743 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1745 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1746 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1747 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1749 /* We can't continue if the application is running! */
\r
1750 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1751 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1752 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1753 SetServiceStatus(service->status_handle, &service->status);
\r
1756 case SERVICE_CONTROL_PAUSE:
\r
1758 We don't accept pause messages but it isn't possible to register
\r
1759 only for continue messages so we have to handle this case.
\r
1761 log_service_control(service->name, control, false);
\r
1762 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1764 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1765 service->last_control = control;
\r
1766 log_service_control(service->name, control, true);
\r
1767 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1768 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1769 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1770 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1773 case SERVICE_CONTROL_POWEREVENT:
\r
1774 /* Resume from suspend. */
\r
1775 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1776 service->last_control = control;
\r
1777 log_service_control(service->name, control, true);
\r
1778 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1782 /* Battery low or changed to A/C power or something. */
\r
1783 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1784 service->last_control = control;
\r
1785 log_service_control(service->name, control, true);
\r
1786 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1789 log_service_control(service->name, control, false);
\r
1793 /* Unknown control */
\r
1794 log_service_control(service->name, control, false);
\r
1795 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1798 /* Start the service */
\r
1799 int start_service(nssm_service_t *service) {
\r
1800 service->stopping = false;
\r
1802 if (service->process_handle) return 0;
\r
1803 service->start_requested_count++;
\r
1805 /* Allocate a STARTUPINFO structure for a new process */
\r
1807 ZeroMemory(&si, sizeof(si));
\r
1808 si.cb = sizeof(si);
\r
1810 /* Allocate a PROCESSINFO structure for the process */
\r
1811 PROCESS_INFORMATION pi;
\r
1812 ZeroMemory(&pi, sizeof(pi));
\r
1814 /* Get startup parameters */
\r
1815 int ret = get_parameters(service, &si);
\r
1817 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1818 unset_service_environment(service);
\r
1819 return stop_service(service, 2, true, true);
\r
1822 /* Launch executable with arguments */
\r
1823 TCHAR cmd[CMD_LENGTH];
\r
1824 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1825 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1826 unset_service_environment(service);
\r
1827 return stop_service(service, 2, true, true);
\r
1830 throttle_restart(service);
\r
1832 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1833 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1834 SetServiceStatus(service->status_handle, &service->status);
\r
1836 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1838 /* Did another thread receive a stop control? */
\r
1839 if (service->allow_restart) {
\r
1840 /* Set up I/O redirection. */
\r
1841 if (get_output_handles(service, &si)) {
\r
1842 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1843 if (! service->no_console) FreeConsole();
\r
1844 close_output_handles(&si);
\r
1845 unset_service_environment(service);
\r
1846 return stop_service(service, 4, true, true);
\r
1849 /* Pre-start hook. May need I/O to have been redirected already. */
\r
1850 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
1852 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1853 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1854 unset_service_environment(service);
\r
1855 return stop_service(service, 5, true, true);
\r
1858 /* The pre-start hook will have cleaned the environment. */
\r
1859 set_service_environment(service);
\r
1861 bool inherit_handles = false;
\r
1862 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1863 unsigned long flags = service->priority & priority_mask();
\r
1864 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1865 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1866 unsigned long exitcode = 3;
\r
1867 unsigned long error = GetLastError();
\r
1868 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1869 close_output_handles(&si);
\r
1870 unset_service_environment(service);
\r
1871 return stop_service(service, exitcode, true, true);
\r
1873 service->start_count++;
\r
1874 service->process_handle = pi.hProcess;
\r
1875 service->pid = pi.dwProcessId;
\r
1877 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1879 close_output_handles(&si);
\r
1881 if (! service->no_console) FreeConsole();
\r
1883 if (service->affinity) {
\r
1885 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1886 so that we can parse it regardless of whether we're running in 32-bit
\r
1887 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1888 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1889 (or when running the 32-bit NSSM).
\r
1891 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1892 and potentially confusion when we actually try to start the service.
\r
1893 Having said that, however, it's unlikely that we're actually going to
\r
1894 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1895 likelihood of seeing a confusing situation is somewhat diminished.
\r
1897 DWORD_PTR affinity, system_affinity;
\r
1899 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1901 affinity = (DWORD_PTR) service->affinity;
\r
1902 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1905 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1906 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1909 ResumeThread(pi.hThread);
\r
1913 /* Restore our environment. */
\r
1914 unset_service_environment(service);
\r
1917 Wait for a clean startup before changing the service status to RUNNING
\r
1918 but be mindful of the fact that we are blocking the service control manager
\r
1919 so abandon the wait before too much time has elapsed.
\r
1921 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
1923 /* Did another thread receive a stop control? */
\r
1924 if (! service->allow_restart) return 0;
\r
1926 /* Signal successful start */
\r
1927 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1928 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1929 SetServiceStatus(service->status_handle, &service->status);
\r
1931 /* Post-start hook. */
\r
1932 if (! service->throttle) {
\r
1933 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1936 /* Ensure the restart delay is always applied. */
\r
1937 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1942 /* Stop the service */
\r
1943 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1944 service->allow_restart = false;
\r
1945 if (service->wait_handle) {
\r
1946 UnregisterWait(service->wait_handle);
\r
1947 service->wait_handle = 0;
\r
1950 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1952 if (default_action && ! exitcode && ! graceful) {
\r
1953 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
1957 /* Signal we are stopping */
\r
1959 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1960 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1961 SetServiceStatus(service->status_handle, &service->status);
\r
1964 /* Nothing to do if service isn't running */
\r
1965 if (service->pid) {
\r
1966 /* Shut down service */
\r
1967 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1969 service_kill_t(service, &k);
\r
1973 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1975 end_service((void *) service, true);
\r
1977 /* Signal we stopped */
\r
1979 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1980 wait_for_hooks(service, true);
\r
1981 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1983 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1984 service->status.dwServiceSpecificExitCode = exitcode;
\r
1987 service->status.dwWin32ExitCode = NO_ERROR;
\r
1988 service->status.dwServiceSpecificExitCode = 0;
\r
1990 SetServiceStatus(service->status_handle, &service->status);
\r
1996 /* Callback function triggered when the server exits */
\r
1997 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1998 nssm_service_t *service = (nssm_service_t *) arg;
\r
2000 if (service->stopping) return;
\r
2002 service->stopping = true;
\r
2004 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
2006 /* Use now as a dummy exit time. */
\r
2007 GetSystemTimeAsFileTime(&service->exit_time);
\r
2009 /* Check exit code */
\r
2010 unsigned long exitcode = 0;
\r
2012 if (service->process_handle) {
\r
2013 GetExitCodeProcess(service->process_handle, &exitcode);
\r
2014 service->exitcode = exitcode;
\r
2015 /* Check real exit time. */
\r
2016 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
2017 CloseHandle(service->process_handle);
\r
2020 service->process_handle = 0;
\r
2023 Log that the service ended BEFORE logging about killing the process
\r
2024 tree. See below for the possible values of the why argument.
\r
2027 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
2028 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
2032 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
2033 if (service->pid && service->kill_process_tree) {
\r
2035 service_kill_t(service, &k);
\r
2036 kill_process_tree(&k, service->pid);
\r
2041 service->exit_count++;
\r
2042 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
2045 The why argument is true if our wait timed out or false otherwise.
\r
2046 Our wait is infinite so why will never be true when called by the system.
\r
2047 If it is indeed true, assume we were called from stop_service() because
\r
2048 this is a controlled shutdown, and don't take any restart action.
\r
2051 if (! service->allow_restart) return;
\r
2053 /* What action should we take? */
\r
2054 int action = NSSM_EXIT_RESTART;
\r
2055 TCHAR action_string[ACTION_LEN];
\r
2056 bool default_action;
\r
2057 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
2058 for (int i = 0; exit_action_strings[i]; i++) {
\r
2059 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
2067 /* Try to restart the service or return failure code to service manager */
\r
2068 case NSSM_EXIT_RESTART:
\r
2069 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2070 while (monitor_service(service)) {
\r
2071 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
2076 /* Do nothing, just like srvany would */
\r
2077 case NSSM_EXIT_IGNORE:
\r
2078 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2079 wait_for_hooks(service, false);
\r
2083 /* Tell the service manager we are finished */
\r
2084 case NSSM_EXIT_REALLY:
\r
2085 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
2086 stop_service(service, exitcode, true, default_action);
\r
2089 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
2090 case NSSM_EXIT_UNCLEAN:
\r
2091 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
2092 stop_service(service, exitcode, false, default_action);
\r
2093 wait_for_hooks(service, false);
\r
2095 nssm_exit(exitcode);
\r
2099 void throttle_restart(nssm_service_t *service) {
\r
2100 /* This can't be a restart if the service is already running. */
\r
2101 if (! service->throttle++) return;
\r
2104 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
2105 TCHAR threshold[8], milliseconds[8];
\r
2107 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
2108 else ms = throttle_ms;
\r
2110 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2112 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2114 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2115 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2118 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2119 else if (service->throttle_timer) {
\r
2120 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2121 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2122 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2125 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2126 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2127 SetServiceStatus(service->status_handle, &service->status);
\r
2129 if (use_critical_section) {
\r
2130 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2131 LeaveCriticalSection(&service->throttle_section);
\r
2134 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2140 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2141 the number of milliseconds we expect the operation to take, and optionally
\r
2142 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2143 operation completing or dwCheckPoint increasing, the system will consider the
\r
2144 service to be hung.
\r
2146 However the system will consider the service to be hung after 30000
\r
2147 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2148 changed. Therefore if we want to wait longer than that we must periodically
\r
2149 increase dwCheckPoint.
\r
2151 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2152 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2153 time dwCheckPoint is also increased.
\r
2155 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2156 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2157 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2158 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2161 Only doing both these things will prevent the system from killing the service.
\r
2163 If the status_handle and service_status arguments are omitted, this function
\r
2164 will not try to update the service manager but it will still log to the
\r
2165 event log that it is waiting for a handle.
\r
2167 Returns: 1 if the wait timed out.
\r
2168 0 if the wait completed.
\r
2171 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2172 unsigned long interval;
\r
2173 unsigned long ret;
\r
2174 unsigned long waited;
\r
2175 TCHAR interval_milliseconds[16];
\r
2176 TCHAR timeout_milliseconds[16];
\r
2177 TCHAR waited_milliseconds[16];
\r
2178 TCHAR *function = function_name;
\r
2180 /* Add brackets to function name. */
\r
2181 size_t funclen = _tcslen(function_name) + 3;
\r
2182 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2184 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2187 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2190 while (waited < timeout) {
\r
2191 interval = timeout - waited;
\r
2192 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2195 status->dwWaitHint += interval;
\r
2196 status->dwCheckPoint++;
\r
2197 SetServiceStatus(status_handle, status);
\r
2201 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2202 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2203 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2206 switch (WaitForSingleObject(handle, interval)) {
\r
2207 case WAIT_OBJECT_0:
\r
2211 case WAIT_TIMEOUT:
\r
2220 waited += interval;
\r
2224 if (func) HeapFree(GetProcessHeap(), 0, func);
\r
2229 int list_nssm_services(int argc, TCHAR **argv) {
\r
2230 bool including_native = (argc > 0 && str_equiv(argv[0], _T("all")));
\r
2232 /* Open service manager. */
\r
2233 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
2235 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2239 unsigned long bufsize, required, count, i;
\r
2240 unsigned long resume = 0;
\r
2241 EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
2242 unsigned long error = GetLastError();
\r
2243 if (error != ERROR_MORE_DATA) {
\r
2244 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2248 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
2250 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));
\r
2254 bufsize = required;
\r
2256 int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
2258 error = GetLastError();
\r
2259 if (error != ERROR_MORE_DATA) {
\r
2260 HeapFree(GetProcessHeap(), 0, status);
\r
2261 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2266 for (i = 0; i < count; i++) {
\r
2267 /* Try to get the service parameters. */
\r
2268 nssm_service_t *service = alloc_nssm_service();
\r
2270 HeapFree(GetProcessHeap(), 0, status);
\r
2271 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));
\r
2274 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);
\r
2276 get_parameters(service, 0);
\r
2277 /* We manage the service if we have an Application. */
\r
2278 if (including_native || service->exe[0]) _tprintf(_T("%s\n"), service->name);
\r
2280 cleanup_nssm_service(service);
\r
2286 HeapFree(GetProcessHeap(), 0, status);
\r
2290 int service_process_tree(int argc, TCHAR **argv) {
\r
2292 if (argc < 1) return usage(1);
\r
2294 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
2296 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2301 We need SeDebugPrivilege to read the process tree.
\r
2302 We ignore failure here so that an error will be printed later when we
\r
2303 try to open a process handle.
\r
2305 HANDLE token = get_debug_token();
\r
2307 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
2308 SERVICE_STATUS_PROCESS service_status;
\r
2309 nssm_service_t *service;
\r
2313 for (i = 0; i < argc; i++) {
\r
2314 TCHAR *service_name = argv[i];
\r
2315 SC_HANDLE service_handle = open_service(services, service_name, SERVICE_QUERY_STATUS, canonical_name, _countof(canonical_name));
\r
2316 if (! service_handle) {
\r
2321 unsigned long size;
\r
2322 int ret = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (LPBYTE) &service_status, sizeof(service_status), &size);
\r
2323 long error = GetLastError();
\r
2324 CloseServiceHandle(service_handle);
\r
2326 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
2331 ZeroMemory(&k, sizeof(k));
\r
2332 k.pid = service_status.dwProcessId;
\r
2333 if (! k.pid) continue;
\r
2335 k.process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, k.pid);
\r
2336 if (! k.process_handle) {
\r
2337 _ftprintf(stderr, _T("%s: %lu: %s\n"), canonical_name, k.pid, error_string(GetLastError()));
\r
2341 if (get_process_creation_time(k.process_handle, &k.creation_time)) continue;
\r
2342 /* Dummy exit time so we can check processes' parents. */
\r
2343 GetSystemTimeAsFileTime(&k.exit_time);
\r
2345 service = alloc_nssm_service();
\r
2351 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), canonical_name);
\r
2352 k.name = service->name;
\r
2353 walk_process_tree(service, print_process, &k, k.pid);
\r
2355 cleanup_nssm_service(service);
\r
2358 CloseServiceHandle(services);
\r
2359 if (token != INVALID_HANDLE_VALUE) CloseHandle(token);
\r