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, unsigned long cutoff) {
\r
88 unsigned long checkpoint = 0;
\r
89 unsigned long waithint = 0;
\r
90 unsigned long waited = 0;
\r
91 while (QueryServiceStatus(service_handle, service_status)) {
\r
92 int response = service_control_response(control, service_status->dwCurrentState);
\r
93 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
94 if (! response) return response;
\r
95 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
96 if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0;
\r
97 checkpoint = service_status->dwCheckPoint;
\r
98 waithint = service_status->dwWaitHint;
\r
99 if (++tries > 10) tries = 10;
\r
100 unsigned long wait = 50 * tries;
\r
102 if (waited > cutoff) return response;
\r
107 else return response;
\r
112 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
113 return await_service_control_response(control, service_handle, service_status, initial_status, 0);
\r
116 static inline void wait_for_hooks(nssm_service_t *service, bool notify) {
\r
117 SERVICE_STATUS_HANDLE status_handle;
\r
118 SERVICE_STATUS *status;
\r
120 /* On a clean shutdown we need to keep the service's status up-to-date. */
\r
122 status_handle = service->status_handle;
\r
123 status = &service->status;
\r
126 status_handle = NULL;
\r
130 EnterCriticalSection(&service->hook_section);
\r
131 await_hook_threads(&hook_threads, status_handle, status, NSSM_HOOK_THREAD_DEADLINE);
\r
132 LeaveCriticalSection(&service->hook_section);
\r
135 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
136 if (! string) return 1;
\r
144 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
146 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
148 for (i = 0, n = 0; i < _countof(set); i++) {
\r
149 if (mask & (1LL << i)) {
\r
150 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
151 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
154 set[n].first = set[n].last = (int) i;
\r
159 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
160 size_t len = (size_t) (n + 1) * 6;
\r
161 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
162 if (! string) return 2;
\r
166 for (i = 0; i <= n; i++) {
\r
167 if (i) (*string)[s++] = _T(',');
\r
168 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
170 HeapFree(GetProcessHeap(), 0, *string);
\r
175 if (set[i].last != set[i].first) {
\r
176 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
178 HeapFree(GetProcessHeap(), 0, *string);
\r
189 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
190 if (! mask) return 1;
\r
193 if (! string) return 0;
\r
202 unsigned long number;
\r
204 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
208 ret = str_number(s, &number, &end);
\r
210 if (ret == 0 || ret == 2) {
\r
211 if (number >= _countof(set)) return 2;
\r
212 set[n].first = set[n].last = (int) number;
\r
224 if (! *(++s)) return 3;
\r
225 ret = str_number(s, &number, &end);
\r
226 if (ret == 0 || ret == 2) {
\r
228 if (! *s || *s == _T(',')) {
\r
229 set[n].last = (int) number;
\r
246 for (i = 0; i <= n; i++) {
\r
247 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
253 unsigned long priority_mask() {
\r
254 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
257 int priority_constant_to_index(unsigned long constant) {
\r
258 switch (constant & priority_mask()) {
\r
259 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
260 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
261 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
262 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
263 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
265 return NSSM_NORMAL_PRIORITY;
\r
268 unsigned long priority_index_to_constant(int index) {
\r
270 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
271 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
272 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
273 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
274 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
276 return NORMAL_PRIORITY_CLASS;
\r
279 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
280 if (throttle > 7) throttle = 8;
\r
281 /* pow() operates on doubles. */
\r
282 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
286 void set_service_environment(nssm_service_t *service) {
\r
287 if (! service) return;
\r
290 We have to duplicate the block because this function will be called
\r
291 multiple times between registry reads.
\r
293 if (service->env) duplicate_environment_strings(service->env);
\r
294 if (! service->env_extra) return;
\r
295 TCHAR *env_extra = copy_environment_block(service->env_extra);
\r
296 if (! env_extra) return;
\r
298 set_environment_block(env_extra);
\r
299 HeapFree(GetProcessHeap(), 0, env_extra);
\r
302 void unset_service_environment(nssm_service_t *service) {
\r
303 if (! service) return;
\r
304 duplicate_environment_strings(service->initial_env);
\r
308 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
309 control immediately.
\r
311 static unsigned long WINAPI shutdown_service(void *arg) {
\r
312 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
316 Wrapper to be called in a new thread so that we can acknowledge start
\r
319 static unsigned long WINAPI launch_service(void *arg) {
\r
320 return monitor_service((nssm_service_t *) arg);
\r
323 /* Connect to the service manager */
\r
324 SC_HANDLE open_service_manager(unsigned long access) {
\r
325 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
327 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
334 /* Open a service by name or display name. */
\r
335 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
336 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
337 if (service_handle) {
\r
338 if (canonical_name && canonical_name != service_name) {
\r
339 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
340 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
341 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
342 unsigned long keyname_len = canonical_namelen;
\r
343 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
345 return service_handle;
\r
348 unsigned long error = GetLastError();
\r
349 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
350 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
354 /* We can't look for a display name because there's no buffer to store it. */
\r
355 if (! canonical_name) {
\r
356 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
360 unsigned long bufsize, required, count, i;
\r
361 unsigned long resume = 0;
\r
362 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
363 error = GetLastError();
\r
364 if (error != ERROR_MORE_DATA) {
\r
365 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
369 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
371 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
375 bufsize = required;
\r
378 EnumServicesStatus() returns:
\r
379 1 when it retrieved data and there's no more data to come.
\r
380 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
381 there's more data to come.
\r
382 0 and sets last error to something else on error.
\r
384 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
386 error = GetLastError();
\r
387 if (error != ERROR_MORE_DATA) {
\r
388 HeapFree(GetProcessHeap(), 0, status);
\r
389 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
394 for (i = 0; i < count; i++) {
\r
395 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
396 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
397 HeapFree(GetProcessHeap(), 0, status);
\r
398 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
402 HeapFree(GetProcessHeap(), 0, status);
\r
403 return open_service(services, canonical_name, access, 0, 0);
\r
410 /* Recurse so we can get an error message. */
\r
411 return open_service(services, service_name, access, 0, 0);
\r
414 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
415 QUERY_SERVICE_CONFIG *qsc;
\r
416 unsigned long bufsize;
\r
417 unsigned long error;
\r
419 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
420 error = GetLastError();
\r
421 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
422 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
424 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
429 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
433 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
434 HeapFree(GetProcessHeap(), 0, qsc);
\r
435 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
442 /* WILL NOT allocate a new string if the identifier is already present. */
\r
443 int prepend_service_group_identifier(TCHAR *group, TCHAR **canon) {
\r
444 if (! group || ! group[0] || group[0] == SC_GROUP_IDENTIFIER) {
\r
449 size_t len = _tcslen(group) + 1;
\r
450 *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
452 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("prepend_service_group_identifier()"));
\r
457 *s++ = SC_GROUP_IDENTIFIER;
\r
458 memmove(s, group, len * sizeof(TCHAR));
\r
459 (*canon)[len] = _T('\0');
\r
464 int append_to_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) {
\r
468 if (type == DEPENDENCY_GROUPS) {
\r
469 if (prepend_service_group_identifier(string, &canon)) return 1;
\r
471 else canon = string;
\r
472 int ret = append_to_double_null(dependencies, dependencieslen, newdependencies, newlen, canon, 0, false);
\r
473 if (canon && canon != string) HeapFree(GetProcessHeap(), 0, canon);
\r
478 int remove_from_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) {
\r
482 if (type == DEPENDENCY_GROUPS) {
\r
483 if (prepend_service_group_identifier(string, &canon)) return 1;
\r
485 else canon = string;
\r
486 int ret = remove_from_double_null(dependencies, dependencieslen, newdependencies, newlen, canon, 0, false);
\r
487 if (canon && canon != string) HeapFree(GetProcessHeap(), 0, canon);
\r
492 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
493 TCHAR *dependencies = _T("");
\r
494 unsigned long num_dependencies = 0;
\r
496 if (buffer && buffer[0]) {
\r
497 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
499 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
504 Count the dependencies then allocate a buffer big enough for their
\r
505 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
509 for (s = buffer; *s; s++) {
\r
510 num_dependencies++;
\r
511 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
515 /* At least one dependency is a group so we need to verify them. */
\r
518 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
519 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
523 unsigned long type;
\r
524 unsigned long groupslen;
\r
525 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
526 if (ret == ERROR_SUCCESS) {
\r
527 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
529 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
533 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
534 if (ret != ERROR_SUCCESS) {
\r
535 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
536 HeapFree(GetProcessHeap(), 0, groups);
\r
541 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
542 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
551 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
552 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
555 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
556 for (s = buffer; *s; s++) {
\r
558 if (*s == SC_GROUP_IDENTIFIER) {
\r
559 TCHAR *group = s + 1;
\r
563 for (TCHAR *g = groups; *g; g++) {
\r
564 if (str_equiv(g, group)) {
\r
566 /* Set canonical name. */
\r
567 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
575 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
577 HeapFree(GetProcessHeap(), 0, dependencies);
\r
578 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
579 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
584 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
585 if (! dependency_handle) {
\r
586 HeapFree(GetProcessHeap(), 0, dependencies);
\r
587 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
588 CloseServiceHandle(services);
\r
589 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
594 size_t len = _tcslen(dependency) + 1;
\r
595 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
601 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
602 CloseServiceHandle(services);
\r
605 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
606 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
607 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
611 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
615 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
616 if (! buffer) return 1;
\r
617 if (! bufsize) return 2;
\r
622 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
623 if (! qsc) return 3;
\r
625 if (! qsc->lpDependencies || ! qsc->lpDependencies[0]) {
\r
626 HeapFree(GetProcessHeap(), 0, qsc);
\r
630 /* lpDependencies is doubly NULL terminated. */
\r
631 while (qsc->lpDependencies[*bufsize]) {
\r
632 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
638 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
641 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
642 HeapFree(GetProcessHeap(), 0, qsc);
\r
646 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
651 for (s = qsc->lpDependencies; *s; s++) {
\r
652 /* Only copy the appropriate type of dependency. */
\r
653 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
654 size_t len = _tcslen(s) + 1;
\r
655 *bufsize += (unsigned long) len;
\r
656 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
665 HeapFree(GetProcessHeap(), 0, qsc);
\r
667 if (! *buffer[0]) {
\r
668 HeapFree(GetProcessHeap(), 0, *buffer);
\r
676 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
677 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
680 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
681 SERVICE_DESCRIPTION description;
\r
682 ZeroMemory(&description, sizeof(description));
\r
684 lpDescription must be NULL if we aren't changing, the new description
\r
687 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
688 else description.lpDescription = _T("");
\r
690 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
692 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
696 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
697 if (! buffer) return 1;
\r
699 unsigned long bufsize;
\r
700 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
701 unsigned long error = GetLastError();
\r
702 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
703 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
704 if (! description) {
\r
705 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
709 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
710 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
711 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
712 HeapFree(GetProcessHeap(), 0, description);
\r
716 HeapFree(GetProcessHeap(), 0, description);
\r
717 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
722 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
727 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
728 if (! qsc) return 1;
\r
730 switch (qsc->dwStartType) {
\r
731 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
732 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
733 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
736 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
738 /* Check for delayed start. */
\r
739 unsigned long bufsize;
\r
740 unsigned long error;
\r
741 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
742 error = GetLastError();
\r
743 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
744 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
746 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
750 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
751 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
752 HeapFree(GetProcessHeap(), 0, info);
\r
756 error = GetLastError();
\r
757 if (error != ERROR_INVALID_LEVEL) {
\r
758 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
763 else if (error != ERROR_INVALID_LEVEL) {
\r
764 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
771 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
772 if (! username) return 1;
\r
773 if (! usernamelen) return 1;
\r
778 if (! qsc) return 1;
\r
780 if (qsc->lpServiceStartName[0]) {
\r
781 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
783 size_t len = _tcslen(qsc->lpServiceStartName);
\r
784 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
786 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
790 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
791 *usernamelen = len;
\r
797 /* Set default values which aren't zero. */
\r
798 void set_nssm_service_defaults(nssm_service_t *service) {
\r
799 if (! service) return;
\r
801 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
802 service->priority = NORMAL_PRIORITY_CLASS;
\r
803 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
804 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
805 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
806 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
807 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
808 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
809 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
810 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
811 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
812 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
813 service->stop_method = ~0;
\r
814 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
815 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
816 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
817 service->kill_process_tree = 1;
\r
820 /* Allocate and zero memory for a service. */
\r
821 nssm_service_t *alloc_nssm_service() {
\r
822 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
823 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
827 /* Free memory for a service. */
\r
828 void cleanup_nssm_service(nssm_service_t *service) {
\r
829 if (! service) return;
\r
830 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
831 if (service->password) {
\r
832 SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));
\r
833 HeapFree(GetProcessHeap(), 0, service->password);
\r
835 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
836 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
837 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
838 if (service->handle) CloseServiceHandle(service->handle);
\r
839 if (service->process_handle) CloseHandle(service->process_handle);
\r
840 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
841 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
842 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
843 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
844 if (service->initial_env) HeapFree(GetProcessHeap(), 0, service->initial_env);
\r
845 HeapFree(GetProcessHeap(), 0, service);
\r
848 /* About to install the service */
\r
849 int pre_install_service(int argc, TCHAR **argv) {
\r
850 nssm_service_t *service = alloc_nssm_service();
\r
851 set_nssm_service_defaults(service);
\r
852 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
854 /* Show the dialogue box if we didn't give the service name and path */
\r
855 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
858 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
861 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
863 /* Arguments are optional */
\r
864 size_t flagslen = 0;
\r
867 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
868 if (! flagslen) flagslen = 1;
\r
869 if (flagslen > _countof(service->flags)) {
\r
870 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
874 for (i = 2; i < argc; i++) {
\r
875 size_t len = _tcslen(argv[i]);
\r
876 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
878 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
881 /* Work out directory name */
\r
882 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
883 strip_basename(service->dir);
\r
885 int ret = install_service(service);
\r
886 cleanup_nssm_service(service);
\r
890 /* About to edit the service. */
\r
891 int pre_edit_service(int argc, TCHAR **argv) {
\r
892 /* Require service name. */
\r
893 if (argc < 2) return usage(1);
\r
895 /* Are we editing on the command line? */
\r
896 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING, MODE_DUMPING } mode = MODE_EDITING;
\r
897 const TCHAR *verb = argv[0];
\r
898 const TCHAR *service_name = argv[1];
\r
899 bool getting = false;
\r
900 bool unsetting = false;
\r
902 /* Minimum number of arguments. */
\r
904 /* Index of first value. */
\r
907 if (str_equiv(verb, _T("get"))) {
\r
909 mode = MODE_GETTING;
\r
911 else if (str_equiv(verb, _T("set"))) {
\r
913 mode = MODE_SETTING;
\r
915 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
917 mode = MODE_RESETTING;
\r
919 else if (str_equiv(verb, _T("dump"))) {
\r
922 mode = MODE_DUMPING;
\r
924 if (argc < mandatory) return usage(1);
\r
926 const TCHAR *parameter = 0;
\r
927 settings_t *setting = 0;
\r
930 /* Validate the parameter. */
\r
931 if (mandatory > 2) {
\r
932 bool additional_mandatory = false;
\r
934 parameter = argv[2];
\r
935 for (i = 0; settings[i].name; i++) {
\r
936 setting = &settings[i];
\r
937 if (! str_equiv(setting->name, parameter)) continue;
\r
938 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
939 additional_mandatory = true;
\r
944 if (! settings[i].name) {
\r
945 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
946 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
951 if (additional_mandatory) {
\r
952 if (argc < mandatory) {
\r
953 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
956 additional = argv[3];
\r
959 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
960 additional = argv[3];
\r
964 additional = argv[remainder];
\r
965 if (argc < mandatory) return usage(1);
\r
969 nssm_service_t *service = alloc_nssm_service();
\r
970 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
972 /* Open service manager */
\r
973 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
975 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
979 /* Try to open the service */
\r
980 unsigned long access = SERVICE_QUERY_CONFIG;
\r
981 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
982 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
983 if (! service->handle) {
\r
984 CloseServiceHandle(services);
\r
988 /* Get system details. */
\r
989 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
991 CloseServiceHandle(service->handle);
\r
992 CloseServiceHandle(services);
\r
996 service->type = qsc->dwServiceType;
\r
997 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\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
1002 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
1007 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
1008 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1009 HeapFree(GetProcessHeap(), 0, qsc);
\r
1010 CloseServiceHandle(service->handle);
\r
1011 CloseServiceHandle(services);
\r
1016 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
1017 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1018 HeapFree(GetProcessHeap(), 0, qsc);
\r
1019 CloseServiceHandle(service->handle);
\r
1020 CloseServiceHandle(services);
\r
1025 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
1027 /* Get the canonical service name. We open it case insensitively. */
\r
1028 unsigned long bufsize = _countof(service->name);
\r
1029 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1031 /* Remember the executable in case it isn't NSSM. */
\r
1032 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
1033 HeapFree(GetProcessHeap(), 0, qsc);
\r
1035 /* Get extended system details. */
\r
1036 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
1037 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1038 CloseServiceHandle(service->handle);
\r
1039 CloseServiceHandle(services);
\r
1044 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
1045 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1046 CloseServiceHandle(service->handle);
\r
1047 CloseServiceHandle(services);
\r
1052 /* Get NSSM details. */
\r
1053 get_parameters(service, 0);
\r
1055 CloseServiceHandle(services);
\r
1057 if (! service->exe[0]) {
\r
1058 service->native = true;
\r
1059 if (mode != MODE_GETTING && mode != MODE_DUMPING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
1062 /* Editing with the GUI. */
\r
1063 if (mode == MODE_EDITING) {
\r
1064 nssm_gui(IDD_EDIT, service);
\r
1072 if (mode == MODE_DUMPING) {
\r
1073 TCHAR *service_name = service->name;
\r
1074 if (argc > remainder) service_name = argv[remainder];
\r
1075 if (service->native) key = 0;
\r
1077 key = open_registry(service->name, KEY_READ);
\r
1078 if (! key) return 4;
\r
1081 TCHAR quoted_service_name[SERVICE_NAME_LENGTH * 2];
\r
1082 TCHAR quoted_exe[EXE_LENGTH * 2];
\r
1083 TCHAR quoted_nssm[EXE_LENGTH * 2];
\r
1084 if (quote(service_name, quoted_service_name, _countof(quoted_service_name))) return 5;
\r
1085 if (quote(service->exe, quoted_exe, _countof(quoted_exe))) return 6;
\r
1086 if (quote(nssm_exe(), quoted_nssm, _countof(quoted_nssm))) return 6;
\r
1087 _tprintf(_T("%s install %s %s\n"), quoted_nssm, quoted_service_name, quoted_exe);
\r
1090 for (i = 0; settings[i].name; i++) {
\r
1091 setting = &settings[i];
\r
1092 if (! setting->native && service->native) continue;
\r
1093 if (dump_setting(service_name, key, service->handle, setting)) ret++;
\r
1096 if (! service->native) RegCloseKey(key);
\r
1097 CloseServiceHandle(service->handle);
\r
1099 if (ret) return 1;
\r
1103 /* Trying to manage App* parameters for a non-NSSM service. */
\r
1104 if (! setting->native && service->native) {
\r
1105 CloseServiceHandle(service->handle);
\r
1106 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1110 if (mode == MODE_GETTING) {
\r
1111 if (! service->native) {
\r
1112 key = open_registry(service->name, KEY_READ);
\r
1113 if (! key) return 4;
\r
1116 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1117 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1119 CloseServiceHandle(service->handle);
\r
1123 switch (setting->type) {
\r
1124 case REG_EXPAND_SZ:
\r
1125 case REG_MULTI_SZ:
\r
1127 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1128 HeapFree(GetProcessHeap(), 0, value.string);
\r
1132 _tprintf(_T("%lu\n"), value.numeric);
\r
1136 if (! service->native) RegCloseKey(key);
\r
1137 CloseServiceHandle(service->handle);
\r
1141 /* Build the value. */
\r
1142 if (mode == MODE_RESETTING) {
\r
1143 /* Unset the parameter. */
\r
1146 else if (remainder == argc) {
\r
1150 /* Set the parameter. */
\r
1152 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1153 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1156 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1157 if (! value.string) {
\r
1158 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1159 CloseServiceHandle(service->handle);
\r
1164 for (i = remainder; i < argc; i++) {
\r
1165 size_t len = _tcslen(argv[i]);
\r
1166 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1168 if (i < argc - 1) {
\r
1169 if (setting->additional & ADDITIONAL_CRLF) {
\r
1170 value.string[s++] = _T('\r');
\r
1171 value.string[s++] = _T('\n');
\r
1173 else value.string[s++] = _T(' ');
\r
1176 value.string[s] = _T('\0');
\r
1179 if (! service->native) {
\r
1180 key = open_registry(service->name, KEY_READ | KEY_WRITE);
\r
1182 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1187 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1188 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1189 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1191 if (! service->native) RegCloseKey(key);
\r
1192 CloseServiceHandle(service->handle);
\r
1196 if (! service->native) RegCloseKey(key);
\r
1197 CloseServiceHandle(service->handle);
\r
1202 /* About to remove the service */
\r
1203 int pre_remove_service(int argc, TCHAR **argv) {
\r
1204 nssm_service_t *service = alloc_nssm_service();
\r
1205 set_nssm_service_defaults(service);
\r
1206 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1208 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1209 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1210 if (str_equiv(argv[1], _T("confirm"))) {
\r
1211 int ret = remove_service(service);
\r
1212 cleanup_nssm_service(service);
\r
1215 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1219 /* Install the service */
\r
1220 int install_service(nssm_service_t *service) {
\r
1221 if (! service) return 1;
\r
1223 /* Open service manager */
\r
1224 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1226 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1227 cleanup_nssm_service(service);
\r
1231 /* Get path of this program */
\r
1232 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1234 /* Create the service - settings will be changed in edit_service() */
\r
1235 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
1236 if (! service->handle) {
\r
1237 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1238 CloseServiceHandle(services);
\r
1242 if (edit_service(service, false)) {
\r
1243 DeleteService(service->handle);
\r
1244 CloseServiceHandle(services);
\r
1248 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1251 CloseServiceHandle(services);
\r
1256 /* Edit the service. */
\r
1257 int edit_service(nssm_service_t *service, bool editing) {
\r
1258 if (! service) return 1;
\r
1261 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1262 and SERVICE_INTERACTIVE_PROCESS.
\r
1264 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1265 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1267 /* Startup type. */
\r
1268 unsigned long startup;
\r
1269 switch (service->startup) {
\r
1270 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1271 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1272 default: startup = SERVICE_AUTO_START;
\r
1275 /* Display name. */
\r
1276 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1279 Username must be NULL if we aren't changing or an account name.
\r
1280 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1281 Password must be NULL if we aren't changing, a password or "".
\r
1282 Empty passwords are valid but we won't allow them in the GUI.
\r
1284 TCHAR *username = 0;
\r
1286 TCHAR *password = 0;
\r
1287 boolean virtual_account = false;
\r
1288 if (service->usernamelen) {
\r
1289 username = service->username;
\r
1290 if (is_virtual_account(service->name, username)) {
\r
1291 virtual_account = true;
\r
1292 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (service->usernamelen + 1) * sizeof(TCHAR));
\r
1294 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("edit_service()"));
\r
1297 memmove(canon, username, (service->usernamelen + 1) * sizeof(TCHAR));
\r
1300 if (canonicalise_username(username, &canon)) return 5;
\r
1301 if (service->passwordlen) password = service->password;
\r
1304 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1306 if (! virtual_account) {
\r
1307 if (well_known_username(canon)) password = _T("");
\r
1309 if (grant_logon_as_service(canon)) {
\r
1310 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1311 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1317 TCHAR *dependencies = _T("");
\r
1318 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1320 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1321 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1322 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1325 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1327 if (service->dependencieslen) {
\r
1328 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1331 if (service->description[0] || editing) {
\r
1332 set_service_description(service->name, service->handle, service->description);
\r
1335 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1336 ZeroMemory(&delayed, sizeof(delayed));
\r
1337 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1338 else delayed.fDelayedAutostart = 0;
\r
1339 /* Delayed startup isn't supported until Vista. */
\r
1340 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1341 unsigned long error = GetLastError();
\r
1342 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1343 if (error != ERROR_INVALID_LEVEL) {
\r
1344 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1348 /* Don't mess with parameters which aren't ours. */
\r
1349 if (! service->native) {
\r
1350 /* Now we need to put the parameters into the registry */
\r
1351 if (create_parameters(service, editing)) {
\r
1352 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1356 set_service_recovery(service);
\r
1362 /* Control a service. */
\r
1363 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1364 if (argc < 1) return usage(1);
\r
1365 TCHAR *service_name = argv[0];
\r
1366 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1368 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1370 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1374 unsigned long access = SERVICE_QUERY_STATUS;
\r
1375 switch (control) {
\r
1376 case NSSM_SERVICE_CONTROL_START:
\r
1377 access |= SERVICE_START;
\r
1380 case SERVICE_CONTROL_CONTINUE:
\r
1381 case SERVICE_CONTROL_PAUSE:
\r
1382 access |= SERVICE_PAUSE_CONTINUE;
\r
1385 case SERVICE_CONTROL_STOP:
\r
1386 access |= SERVICE_STOP;
\r
1389 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1390 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1394 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1395 if (! service_handle) {
\r
1396 CloseServiceHandle(services);
\r
1401 unsigned long error;
\r
1402 SERVICE_STATUS service_status;
\r
1403 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1404 unsigned long initial_status = SERVICE_STOPPED;
\r
1405 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1406 error = GetLastError();
\r
1407 CloseServiceHandle(services);
\r
1409 if (error == ERROR_IO_PENDING) {
\r
1411 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1412 indicate that the operation is still in progress. Newer versions
\r
1413 will return it if there really is a delay.
\r
1416 error = ERROR_SUCCESS;
\r
1420 unsigned long cutoff = 0;
\r
1422 /* If we manage the service, respect the throttle time. */
\r
1423 HKEY key = open_registry(service_name, 0, KEY_READ, false);
\r
1425 if (get_number(key, NSSM_REG_THROTTLE, &cutoff, false) != 1) cutoff = NSSM_RESET_THROTTLE_RESTART;
\r
1429 int response = await_service_control_response(control, service_handle, &service_status, initial_status, cutoff);
\r
1430 CloseServiceHandle(service_handle);
\r
1433 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1436 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1440 CloseServiceHandle(service_handle);
\r
1441 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1445 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1447 We could actually send an INTERROGATE control but that won't return
\r
1448 any information if the service is stopped and we don't care about
\r
1449 the extra details it might give us in any case. So we'll fake it.
\r
1451 ret = QueryServiceStatus(service_handle, &service_status);
\r
1452 error = GetLastError();
\r
1455 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1459 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1464 ret = ControlService(service_handle, control, &service_status);
\r
1465 unsigned long initial_status = service_status.dwCurrentState;
\r
1466 error = GetLastError();
\r
1467 CloseServiceHandle(services);
\r
1469 if (error == ERROR_IO_PENDING) {
\r
1471 error = ERROR_SUCCESS;
\r
1475 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1476 CloseServiceHandle(service_handle);
\r
1479 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1482 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1486 CloseServiceHandle(service_handle);
\r
1487 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1488 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1489 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1496 /* Remove the service */
\r
1497 int remove_service(nssm_service_t *service) {
\r
1498 if (! service) return 1;
\r
1500 /* Open service manager */
\r
1501 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1503 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1507 /* Try to open the service */
\r
1508 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1509 if (! service->handle) {
\r
1510 CloseServiceHandle(services);
\r
1514 /* Get the canonical service name. We open it case insensitively. */
\r
1515 unsigned long bufsize = _countof(service->displayname);
\r
1516 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1517 bufsize = _countof(service->name);
\r
1518 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1520 /* Try to delete the service */
\r
1521 if (! DeleteService(service->handle)) {
\r
1522 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1523 CloseServiceHandle(services);
\r
1528 CloseServiceHandle(services);
\r
1530 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1534 /* Service initialisation */
\r
1535 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1536 nssm_service_t *service = alloc_nssm_service();
\r
1537 if (! service) return;
\r
1539 static volatile bool await_debugger = (argc > 1 && str_equiv(argv[1], _T("debug")));
\r
1540 while (await_debugger) Sleep(1000);
\r
1542 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1543 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1547 /* We can use a condition variable in a critical section on Vista or later. */
\r
1548 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1549 else use_critical_section = false;
\r
1551 /* Initialise status */
\r
1552 ZeroMemory(&service->status, sizeof(service->status));
\r
1553 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1554 service->status.dwControlsAccepted = 0;
\r
1555 service->status.dwWin32ExitCode = NO_ERROR;
\r
1556 service->status.dwServiceSpecificExitCode = 0;
\r
1557 service->status.dwCheckPoint = 0;
\r
1558 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1560 /* Signal we AREN'T running the server */
\r
1561 service->process_handle = 0;
\r
1564 /* Register control handler */
\r
1565 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1566 if (! service->status_handle) {
\r
1567 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1571 log_service_control(service->name, 0, true);
\r
1573 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1574 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1575 SetServiceStatus(service->status_handle, &service->status);
\r
1578 /* Try to create the exit action parameters; we don't care if it fails */
\r
1579 create_exit_action(service->name, exit_action_strings[0], false);
\r
1581 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1583 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1584 set_service_recovery(service);
\r
1586 /* Remember our display name. */
\r
1587 unsigned long displayname_len = _countof(service->displayname);
\r
1588 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1590 CloseServiceHandle(services);
\r
1594 /* Used for signalling a resume if the service pauses when throttled. */
\r
1595 if (use_critical_section) {
\r
1596 InitializeCriticalSection(&service->throttle_section);
\r
1597 service->throttle_section_initialised = true;
\r
1600 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1601 if (! service->throttle_timer) {
\r
1602 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1606 /* Critical section for hooks. */
\r
1607 InitializeCriticalSection(&service->hook_section);
\r
1608 service->hook_section_initialised = true;
\r
1610 /* Remember our initial environment. */
\r
1611 service->initial_env = copy_environment();
\r
1613 /* Remember our creation time. */
\r
1614 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1616 service->allow_restart = true;
\r
1617 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1618 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1619 stop_service(service, 0, true, true);
\r
1623 /* Make sure service recovery actions are taken where necessary */
\r
1624 void set_service_recovery(nssm_service_t *service) {
\r
1625 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1626 ZeroMemory(&flag, sizeof(flag));
\r
1627 flag.fFailureActionsOnNonCrashFailures = true;
\r
1629 /* This functionality was added in Vista so the call may fail */
\r
1630 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1631 unsigned long error = GetLastError();
\r
1632 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1633 if (error != ERROR_INVALID_LEVEL) {
\r
1634 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1639 int monitor_service(nssm_service_t *service) {
\r
1640 /* Set service status to started */
\r
1641 int ret = start_service(service);
\r
1644 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1645 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1648 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1650 /* Monitor service */
\r
1651 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1652 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1658 TCHAR *service_control_text(unsigned long control) {
\r
1659 switch (control) {
\r
1660 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1661 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1662 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1663 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1664 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1665 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1666 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1667 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1668 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1669 default: return 0;
\r
1673 TCHAR *service_status_text(unsigned long status) {
\r
1675 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1676 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1677 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1678 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1679 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1680 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1681 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1682 default: return 0;
\r
1686 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1687 TCHAR *text = service_control_text(control);
\r
1688 unsigned long event;
\r
1691 /* "0x" + 8 x hex + NULL */
\r
1692 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1694 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1697 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1698 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1699 HeapFree(GetProcessHeap(), 0, text);
\r
1703 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1705 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1706 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1708 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1710 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1711 HeapFree(GetProcessHeap(), 0, text);
\r
1715 /* Service control handler */
\r
1716 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1717 nssm_service_t *service = (nssm_service_t *) context;
\r
1719 switch (control) {
\r
1720 case SERVICE_CONTROL_INTERROGATE:
\r
1721 /* We always keep the service status up-to-date so this is a no-op. */
\r
1724 case SERVICE_CONTROL_SHUTDOWN:
\r
1725 case SERVICE_CONTROL_STOP:
\r
1726 service->last_control = control;
\r
1727 log_service_control(service->name, control, true);
\r
1729 /* Immediately block further controls. */
\r
1730 service->allow_restart = false;
\r
1731 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1732 service->status.dwControlsAccepted = 0;
\r
1733 SetServiceStatus(service->status_handle, &service->status);
\r
1735 /* Pre-stop hook. */
\r
1736 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1739 We MUST acknowledge the stop request promptly but we're committed to
\r
1740 waiting for the application to exit. Spawn a new thread to wait
\r
1741 while we acknowledge the request.
\r
1743 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1744 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1747 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1748 to complete in time in this thread.
\r
1750 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1751 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1752 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1754 stop_service(service, 0, true, true);
\r
1758 case SERVICE_CONTROL_CONTINUE:
\r
1759 service->last_control = control;
\r
1760 log_service_control(service->name, control, true);
\r
1761 service->throttle = 0;
\r
1762 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1764 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1765 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1766 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1768 /* We can't continue if the application is running! */
\r
1769 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1770 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1771 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1772 SetServiceStatus(service->status_handle, &service->status);
\r
1775 case SERVICE_CONTROL_PAUSE:
\r
1777 We don't accept pause messages but it isn't possible to register
\r
1778 only for continue messages so we have to handle this case.
\r
1780 log_service_control(service->name, control, false);
\r
1781 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1783 case NSSM_SERVICE_CONTROL_ROTATE:
\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_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1787 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1788 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1789 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1792 case SERVICE_CONTROL_POWEREVENT:
\r
1793 /* Resume from suspend. */
\r
1794 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1795 service->last_control = control;
\r
1796 log_service_control(service->name, control, true);
\r
1797 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1801 /* Battery low or changed to A/C power or something. */
\r
1802 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1803 service->last_control = control;
\r
1804 log_service_control(service->name, control, true);
\r
1805 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1808 log_service_control(service->name, control, false);
\r
1812 /* Unknown control */
\r
1813 log_service_control(service->name, control, false);
\r
1814 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1817 /* Start the service */
\r
1818 int start_service(nssm_service_t *service) {
\r
1819 service->stopping = false;
\r
1821 if (service->process_handle) return 0;
\r
1822 service->start_requested_count++;
\r
1824 /* Allocate a STARTUPINFO structure for a new process */
\r
1826 ZeroMemory(&si, sizeof(si));
\r
1827 si.cb = sizeof(si);
\r
1829 /* Allocate a PROCESSINFO structure for the process */
\r
1830 PROCESS_INFORMATION pi;
\r
1831 ZeroMemory(&pi, sizeof(pi));
\r
1833 /* Get startup parameters */
\r
1834 int ret = get_parameters(service, &si);
\r
1836 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1837 unset_service_environment(service);
\r
1838 return stop_service(service, 2, true, true);
\r
1841 /* Launch executable with arguments */
\r
1842 TCHAR cmd[CMD_LENGTH];
\r
1843 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1844 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1845 unset_service_environment(service);
\r
1846 return stop_service(service, 2, true, true);
\r
1849 throttle_restart(service);
\r
1851 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1852 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1853 SetServiceStatus(service->status_handle, &service->status);
\r
1855 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1857 /* Did another thread receive a stop control? */
\r
1858 if (service->allow_restart) {
\r
1859 /* Set up I/O redirection. */
\r
1860 if (get_output_handles(service, &si)) {
\r
1861 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1862 if (! service->no_console) FreeConsole();
\r
1863 close_output_handles(&si);
\r
1864 unset_service_environment(service);
\r
1865 return stop_service(service, 4, true, true);
\r
1868 /* Pre-start hook. May need I/O to have been redirected already. */
\r
1869 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
1871 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1872 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1873 unset_service_environment(service);
\r
1874 return stop_service(service, 5, true, true);
\r
1877 /* The pre-start hook will have cleaned the environment. */
\r
1878 set_service_environment(service);
\r
1880 bool inherit_handles = false;
\r
1881 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1882 unsigned long flags = service->priority & priority_mask();
\r
1883 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1884 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1885 unsigned long exitcode = 3;
\r
1886 unsigned long error = GetLastError();
\r
1887 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1888 close_output_handles(&si);
\r
1889 unset_service_environment(service);
\r
1890 return stop_service(service, exitcode, true, true);
\r
1892 service->start_count++;
\r
1893 service->process_handle = pi.hProcess;
\r
1894 service->pid = pi.dwProcessId;
\r
1896 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1898 close_output_handles(&si);
\r
1900 if (! service->no_console) FreeConsole();
\r
1902 if (service->affinity) {
\r
1904 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1905 so that we can parse it regardless of whether we're running in 32-bit
\r
1906 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1907 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1908 (or when running the 32-bit NSSM).
\r
1910 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1911 and potentially confusion when we actually try to start the service.
\r
1912 Having said that, however, it's unlikely that we're actually going to
\r
1913 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1914 likelihood of seeing a confusing situation is somewhat diminished.
\r
1916 DWORD_PTR affinity, system_affinity;
\r
1918 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1920 affinity = (DWORD_PTR) service->affinity;
\r
1921 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1924 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1925 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1928 ResumeThread(pi.hThread);
\r
1932 /* Restore our environment. */
\r
1933 unset_service_environment(service);
\r
1936 Wait for a clean startup before changing the service status to RUNNING
\r
1937 but be mindful of the fact that we are blocking the service control manager
\r
1938 so abandon the wait before too much time has elapsed.
\r
1940 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
1942 /* Did another thread receive a stop control? */
\r
1943 if (! service->allow_restart) return 0;
\r
1945 /* Signal successful start */
\r
1946 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1947 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1948 SetServiceStatus(service->status_handle, &service->status);
\r
1950 /* Post-start hook. */
\r
1951 if (! service->throttle) {
\r
1952 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1955 /* Ensure the restart delay is always applied. */
\r
1956 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1961 /* Stop the service */
\r
1962 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1963 service->allow_restart = false;
\r
1964 if (service->wait_handle) {
\r
1965 UnregisterWait(service->wait_handle);
\r
1966 service->wait_handle = 0;
\r
1969 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1971 if (default_action && ! exitcode && ! graceful) {
\r
1972 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
1976 /* Signal we are stopping */
\r
1978 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1979 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1980 SetServiceStatus(service->status_handle, &service->status);
\r
1983 /* Nothing to do if service isn't running */
\r
1984 if (service->pid) {
\r
1985 /* Shut down service */
\r
1986 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1988 service_kill_t(service, &k);
\r
1992 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1994 end_service((void *) service, true);
\r
1996 /* Signal we stopped */
\r
1998 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1999 wait_for_hooks(service, true);
\r
2000 service->status.dwCurrentState = SERVICE_STOPPED;
\r
2002 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
2003 service->status.dwServiceSpecificExitCode = exitcode;
\r
2006 service->status.dwWin32ExitCode = NO_ERROR;
\r
2007 service->status.dwServiceSpecificExitCode = 0;
\r
2009 SetServiceStatus(service->status_handle, &service->status);
\r
2015 /* Callback function triggered when the server exits */
\r
2016 void CALLBACK end_service(void *arg, unsigned char why) {
\r
2017 nssm_service_t *service = (nssm_service_t *) arg;
\r
2019 if (service->stopping) return;
\r
2021 service->stopping = true;
\r
2023 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
2025 /* Use now as a dummy exit time. */
\r
2026 GetSystemTimeAsFileTime(&service->exit_time);
\r
2028 /* Check exit code */
\r
2029 unsigned long exitcode = 0;
\r
2031 if (service->process_handle) {
\r
2032 GetExitCodeProcess(service->process_handle, &exitcode);
\r
2033 service->exitcode = exitcode;
\r
2034 /* Check real exit time. */
\r
2035 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
2036 CloseHandle(service->process_handle);
\r
2039 service->process_handle = 0;
\r
2042 Log that the service ended BEFORE logging about killing the process
\r
2043 tree. See below for the possible values of the why argument.
\r
2046 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
2047 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
2051 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
2052 if (service->pid && service->kill_process_tree) {
\r
2054 service_kill_t(service, &k);
\r
2055 kill_process_tree(&k, service->pid);
\r
2060 service->exit_count++;
\r
2061 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
2064 The why argument is true if our wait timed out or false otherwise.
\r
2065 Our wait is infinite so why will never be true when called by the system.
\r
2066 If it is indeed true, assume we were called from stop_service() because
\r
2067 this is a controlled shutdown, and don't take any restart action.
\r
2070 if (! service->allow_restart) return;
\r
2072 /* What action should we take? */
\r
2073 int action = NSSM_EXIT_RESTART;
\r
2074 TCHAR action_string[ACTION_LEN];
\r
2075 bool default_action;
\r
2076 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
2077 for (int i = 0; exit_action_strings[i]; i++) {
\r
2078 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
2086 /* Try to restart the service or return failure code to service manager */
\r
2087 case NSSM_EXIT_RESTART:
\r
2088 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2089 while (monitor_service(service)) {
\r
2090 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
2095 /* Do nothing, just like srvany would */
\r
2096 case NSSM_EXIT_IGNORE:
\r
2097 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2098 wait_for_hooks(service, false);
\r
2102 /* Tell the service manager we are finished */
\r
2103 case NSSM_EXIT_REALLY:
\r
2104 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
2105 stop_service(service, exitcode, true, default_action);
\r
2108 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
2109 case NSSM_EXIT_UNCLEAN:
\r
2110 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
2111 stop_service(service, exitcode, false, default_action);
\r
2112 wait_for_hooks(service, false);
\r
2114 nssm_exit(exitcode);
\r
2118 void throttle_restart(nssm_service_t *service) {
\r
2119 /* This can't be a restart if the service is already running. */
\r
2120 if (! service->throttle++) return;
\r
2123 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
2124 TCHAR threshold[8], milliseconds[8];
\r
2126 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
2127 else ms = throttle_ms;
\r
2129 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2131 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2133 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2134 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2137 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2138 else if (service->throttle_timer) {
\r
2139 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2140 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2141 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2144 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2145 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2146 SetServiceStatus(service->status_handle, &service->status);
\r
2148 if (use_critical_section) {
\r
2149 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2150 LeaveCriticalSection(&service->throttle_section);
\r
2153 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2159 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2160 the number of milliseconds we expect the operation to take, and optionally
\r
2161 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2162 operation completing or dwCheckPoint increasing, the system will consider the
\r
2163 service to be hung.
\r
2165 However the system will consider the service to be hung after 30000
\r
2166 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2167 changed. Therefore if we want to wait longer than that we must periodically
\r
2168 increase dwCheckPoint.
\r
2170 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2171 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2172 time dwCheckPoint is also increased.
\r
2174 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2175 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2176 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2177 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2180 Only doing both these things will prevent the system from killing the service.
\r
2182 If the status_handle and service_status arguments are omitted, this function
\r
2183 will not try to update the service manager but it will still log to the
\r
2184 event log that it is waiting for a handle.
\r
2186 Returns: 1 if the wait timed out.
\r
2187 0 if the wait completed.
\r
2190 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2191 unsigned long interval;
\r
2192 unsigned long ret;
\r
2193 unsigned long waited;
\r
2194 TCHAR interval_milliseconds[16];
\r
2195 TCHAR timeout_milliseconds[16];
\r
2196 TCHAR waited_milliseconds[16];
\r
2197 TCHAR *function = function_name;
\r
2199 /* Add brackets to function name. */
\r
2200 size_t funclen = _tcslen(function_name) + 3;
\r
2201 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2203 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2206 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2209 while (waited < timeout) {
\r
2210 interval = timeout - waited;
\r
2211 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2214 status->dwWaitHint += interval;
\r
2215 status->dwCheckPoint++;
\r
2216 SetServiceStatus(status_handle, status);
\r
2220 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2221 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2222 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2225 switch (WaitForSingleObject(handle, interval)) {
\r
2226 case WAIT_OBJECT_0:
\r
2230 case WAIT_TIMEOUT:
\r
2239 waited += interval;
\r
2243 if (func) HeapFree(GetProcessHeap(), 0, func);
\r
2248 int list_nssm_services(int argc, TCHAR **argv) {
\r
2249 bool including_native = (argc > 0 && str_equiv(argv[0], _T("all")));
\r
2251 /* Open service manager. */
\r
2252 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
2254 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2258 unsigned long bufsize, required, count, i;
\r
2259 unsigned long resume = 0;
\r
2260 EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
2261 unsigned long error = GetLastError();
\r
2262 if (error != ERROR_MORE_DATA) {
\r
2263 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2267 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
2269 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));
\r
2273 bufsize = required;
\r
2275 int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
2277 error = GetLastError();
\r
2278 if (error != ERROR_MORE_DATA) {
\r
2279 HeapFree(GetProcessHeap(), 0, status);
\r
2280 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2285 for (i = 0; i < count; i++) {
\r
2286 /* Try to get the service parameters. */
\r
2287 nssm_service_t *service = alloc_nssm_service();
\r
2289 HeapFree(GetProcessHeap(), 0, status);
\r
2290 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));
\r
2293 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);
\r
2295 get_parameters(service, 0);
\r
2296 /* We manage the service if we have an Application. */
\r
2297 if (including_native || service->exe[0]) _tprintf(_T("%s\n"), service->name);
\r
2299 cleanup_nssm_service(service);
\r
2305 HeapFree(GetProcessHeap(), 0, status);
\r
2309 int service_process_tree(int argc, TCHAR **argv) {
\r
2311 if (argc < 1) return usage(1);
\r
2313 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
2315 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2320 We need SeDebugPrivilege to read the process tree.
\r
2321 We ignore failure here so that an error will be printed later when we
\r
2322 try to open a process handle.
\r
2324 HANDLE token = get_debug_token();
\r
2326 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
2327 SERVICE_STATUS_PROCESS service_status;
\r
2328 nssm_service_t *service;
\r
2332 for (i = 0; i < argc; i++) {
\r
2333 TCHAR *service_name = argv[i];
\r
2334 SC_HANDLE service_handle = open_service(services, service_name, SERVICE_QUERY_STATUS, canonical_name, _countof(canonical_name));
\r
2335 if (! service_handle) {
\r
2340 unsigned long size;
\r
2341 int ret = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (LPBYTE) &service_status, sizeof(service_status), &size);
\r
2342 long error = GetLastError();
\r
2343 CloseServiceHandle(service_handle);
\r
2345 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
2350 ZeroMemory(&k, sizeof(k));
\r
2351 k.pid = service_status.dwProcessId;
\r
2352 if (! k.pid) continue;
\r
2354 k.process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, k.pid);
\r
2355 if (! k.process_handle) {
\r
2356 _ftprintf(stderr, _T("%s: %lu: %s\n"), canonical_name, k.pid, error_string(GetLastError()));
\r
2360 if (get_process_creation_time(k.process_handle, &k.creation_time)) continue;
\r
2361 /* Dummy exit time so we can check processes' parents. */
\r
2362 GetSystemTimeAsFileTime(&k.exit_time);
\r
2364 service = alloc_nssm_service();
\r
2370 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), canonical_name);
\r
2371 k.name = service->name;
\r
2372 walk_process_tree(service, print_process, &k, k.pid);
\r
2374 cleanup_nssm_service(service);
\r
2377 CloseServiceHandle(services);
\r
2378 if (token != INVALID_HANDLE_VALUE) CloseHandle(token);
\r