}\r
\r
/* Connect to the service manager */\r
-SC_HANDLE open_service_manager() {\r
- SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);\r
+SC_HANDLE open_service_manager(unsigned long access) {\r
+ SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);\r
if (! ret) {\r
if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);\r
return 0;\r
}\r
\r
/* Open a service by name or display name. */\r
-SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, TCHAR *canonical_name, unsigned long canonical_namelen) {\r
- SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_ACCESS);\r
+SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {\r
+ SC_HANDLE service_handle = OpenService(services, service_name, access);\r
if (service_handle) {\r
if (canonical_name && canonical_name != service_name) {\r
- if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {\r
- print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));\r
- return 0;\r
- }\r
+ TCHAR displayname[SERVICE_NAME_LENGTH];\r
+ unsigned long displayname_len = (unsigned long) _countof(displayname);\r
+ GetServiceDisplayName(services, service_name, displayname, &displayname_len);\r
+ unsigned long keyname_len = canonical_namelen;\r
+ GetServiceKeyName(services, displayname, canonical_name, &keyname_len);\r
}\r
return service_handle;\r
}\r
}\r
\r
HeapFree(GetProcessHeap(), 0, status);\r
- return open_service(services, canonical_name, 0, 0);\r
+ return open_service(services, canonical_name, access, 0, 0);\r
}\r
}\r
\r
}\r
\r
/* Recurse so we can get an error message. */\r
- return open_service(services, service_name, 0, 0);\r
+ return open_service(services, service_name, access, 0, 0);\r
}\r
\r
QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {\r
return qsc;\r
}\r
\r
+int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {\r
+ TCHAR *dependencies = _T("");\r
+ unsigned long num_dependencies = 0;\r
+\r
+ if (buffer && buffer[0]) {\r
+ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
+ if (! services) {\r
+ print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+ return 1;\r
+ }\r
+\r
+ /*\r
+ Count the dependencies then allocate a buffer big enough for their\r
+ canonical names, ie n * SERVICE_NAME_LENGTH.\r
+ */\r
+ TCHAR *s;\r
+ TCHAR *groups = 0;\r
+ for (s = buffer; *s; s++) {\r
+ num_dependencies++;\r
+ if (*s == SC_GROUP_IDENTIFIER) groups = s;\r
+ while (*s) s++;\r
+ }\r
+\r
+ /* At least one dependency is a group so we need to verify them. */\r
+ if (groups) {\r
+ HKEY key;\r
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {\r
+ _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));\r
+ return 2;\r
+ }\r
+\r
+ unsigned long type;\r
+ unsigned long groupslen;\r
+ unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);\r
+ if (ret == ERROR_SUCCESS) {\r
+ groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);\r
+ if (! groups) {\r
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));\r
+ return 3;\r
+ }\r
+\r
+ ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);\r
+ if (ret != ERROR_SUCCESS) {\r
+ _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));\r
+ HeapFree(GetProcessHeap(), 0, groups);\r
+ RegCloseKey(key);\r
+ return 4;\r
+ }\r
+ }\r
+ else if (ret != ERROR_FILE_NOT_FOUND) {\r
+ _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));\r
+ RegCloseKey(key);\r
+ return 4;\r
+ }\r
+\r
+ RegCloseKey(key);\r
+\r
+ }\r
+\r
+ unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;\r
+ dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));\r
+ size_t i = 0;\r
+\r
+ TCHAR dependency[SERVICE_NAME_LENGTH];\r
+ for (s = buffer; *s; s++) {\r
+ /* Group? */\r
+ if (*s == SC_GROUP_IDENTIFIER) {\r
+ TCHAR *group = s + 1;\r
+\r
+ bool ok = false;\r
+ if (*group) {\r
+ for (TCHAR *g = groups; *g; g++) {\r
+ if (str_equiv(g, group)) {\r
+ ok = true;\r
+ /* Set canonical name. */\r
+ memmove(group, g, _tcslen(g) * sizeof(TCHAR));\r
+ break;\r
+ }\r
+\r
+ while (*g) g++;\r
+ }\r
+ }\r
+\r
+ if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);\r
+ else {\r
+ HeapFree(GetProcessHeap(), 0, dependencies);\r
+ if (groups) HeapFree(GetProcessHeap(), 0, groups);\r
+ _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));\r
+ return 5;\r
+ }\r
+ }\r
+ else {\r
+ SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));\r
+ if (! dependency_handle) {\r
+ HeapFree(GetProcessHeap(), 0, dependencies);\r
+ if (groups) HeapFree(GetProcessHeap(), 0, groups);\r
+ CloseServiceHandle(services);\r
+ _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));\r
+ return 5;\r
+ }\r
+ }\r
+\r
+ size_t len = _tcslen(dependency) + 1;\r
+ memmove(dependencies + i, dependency, len * sizeof(TCHAR));\r
+ i += len;\r
+\r
+ while (*s) s++;\r
+ }\r
+\r
+ if (groups) HeapFree(GetProcessHeap(), 0, groups);\r
+ CloseServiceHandle(services);\r
+ }\r
+\r
+ if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {\r
+ if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);\r
+ print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+ return -1;\r
+ }\r
+\r
+ if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);\r
+ return 0;\r
+}\r
+\r
+int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {\r
+ if (! buffer) return 1;\r
+ if (! bufsize) return 2;\r
+\r
+ *buffer = 0;\r
+ *bufsize = 0;\r
+\r
+ QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+ if (! qsc) return 3;\r
+\r
+ if (! qsc->lpDependencies) return 0;\r
+ if (! qsc->lpDependencies[0]) return 0;\r
+\r
+ /* lpDependencies is doubly NULL terminated. */\r
+ while (qsc->lpDependencies[*bufsize]) {\r
+ while (qsc->lpDependencies[*bufsize]) ++*bufsize;\r
+ ++*bufsize;\r
+ }\r
+\r
+ *bufsize += 2;\r
+\r
+ *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));\r
+ if (! *buffer) {\r
+ *bufsize = 0;\r
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));\r
+ return 4;\r
+ }\r
+\r
+ if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));\r
+ else {\r
+ TCHAR *s;\r
+ size_t i = 0;\r
+ *bufsize = 0;\r
+ for (s = qsc->lpDependencies; *s; s++) {\r
+ /* Only copy the appropriate type of dependency. */\r
+ if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {\r
+ size_t len = _tcslen(s) + 1;\r
+ *bufsize += (unsigned long) len;\r
+ memmove(*buffer + i, s, len * sizeof(TCHAR));\r
+ i += len;\r
+ }\r
+\r
+ while (*s) s++;\r
+ }\r
+ ++*bufsize;\r
+ }\r
+\r
+ HeapFree(GetProcessHeap(), 0, qsc);\r
+\r
+ return 0;\r
+}\r
+\r
+int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {\r
+ return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);\r
+}\r
+\r
int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {\r
SERVICE_DESCRIPTION description;\r
ZeroMemory(&description, sizeof(description));\r
service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
+ service->kill_process_tree = 1;\r
}\r
\r
/* Allocate and zero memory for a service. */\r
SecureZeroMemory(service->password, service->passwordlen);\r
HeapFree(GetProcessHeap(), 0, service->password);\r
}\r
+ if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);\r
if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);\r
if (service->handle) CloseServiceHandle(service->handle);\r
if (service->process_handle) CloseHandle(service->process_handle);\r
- if (service->wait_handle) UnregisterWait(service->process_handle);\r
+ if (service->wait_handle) UnregisterWait(service->wait_handle);\r
if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);\r
if (service->throttle_timer) CloseHandle(service->throttle_timer);\r
if (service->initial_env) FreeEnvironmentStrings(service->initial_env);\r
_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);\r
\r
/* Open service manager */\r
- SC_HANDLE services = open_service_manager();\r
+ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
if (! services) {\r
print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
return 2;\r
}\r
\r
/* Try to open the service */\r
- service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
+ unsigned long access = SERVICE_QUERY_CONFIG;\r
+ if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;\r
+ service->handle = open_service(services, service->name, access, service->name, _countof(service->name));\r
if (! service->handle) {\r
CloseServiceHandle(services);\r
return 3;\r
/* Get system details. */\r
QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);\r
if (! qsc) {\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
CloseServiceHandle(services);\r
return 4;\r
}\r
if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
if (mode != MODE_GETTING) {\r
HeapFree(GetProcessHeap(), 0, qsc);\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
CloseServiceHandle(services);\r
print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);\r
return 3;\r
if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
if (mode != MODE_GETTING) {\r
HeapFree(GetProcessHeap(), 0, qsc);\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
CloseServiceHandle(services);\r
return 4;\r
}\r
if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
if (mode != MODE_GETTING) {\r
HeapFree(GetProcessHeap(), 0, qsc);\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
CloseServiceHandle(services);\r
return 5;\r
}\r
/* Get extended system details. */\r
if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
if (mode != MODE_GETTING) {\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
CloseServiceHandle(services);\r
return 6;\r
}\r
}\r
\r
+ if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {\r
+ if (mode != MODE_GETTING) {\r
+ CloseServiceHandle(service->handle);\r
+ CloseServiceHandle(services);\r
+ return 7;\r
+ }\r
+ }\r
+\r
/* Get NSSM details. */\r
get_parameters(service, 0);\r
\r
\r
/* Trying to manage App* parameters for a non-NSSM service. */\r
if (! setting->native && service->native) {\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);\r
return 1;\r
}\r
if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);\r
else ret = get_setting(service->name, key, setting, &value, additional);\r
if (ret < 0) {\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
return 5;\r
}\r
\r
}\r
\r
if (! service->native) RegCloseKey(key);\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
return 0;\r
}\r
\r
value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
if (! value.string) {\r
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
return 2;\r
}\r
\r
if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
if (ret < 0) {\r
if (! service->native) RegCloseKey(key);\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
return 6;\r
}\r
\r
if (! service->native) RegCloseKey(key);\r
- CloseHandle(service->handle);\r
+ CloseServiceHandle(service->handle);\r
\r
return 0;\r
}\r
if (! service) return 1;\r
\r
/* Open service manager */\r
- SC_HANDLE services = open_service_manager();\r
+ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);\r
if (! services) {\r
print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
cleanup_nssm_service(service);\r
Empty passwords are valid but we won't allow them in the GUI.\r
*/\r
TCHAR *username = 0;\r
+ TCHAR *canon = 0;\r
TCHAR *password = 0;\r
if (service->usernamelen) {\r
username = service->username;\r
+ if (canonicalise_username(username, &canon)) return 5;\r
if (service->passwordlen) password = service->password;\r
- else password = _T("");\r
}\r
- else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;\r
+ else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;\r
\r
- if (well_known_username(username)) password = _T("");\r
+ if (well_known_username(canon)) password = _T("");\r
else {\r
- if (grant_logon_as_service(username)) {\r
+ if (grant_logon_as_service(canon)) {\r
+ if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
return 5;\r
}\r
}\r
\r
- if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {\r
+ TCHAR *dependencies = _T("");\r
+ if (service->dependencieslen) dependencies = 0; /* Change later. */\r
+\r
+ if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {\r
+ if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
return 5;\r
}\r
+ if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
+\r
+ if (service->dependencieslen) {\r
+ if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;\r
+ }\r
\r
if (service->description[0] || editing) {\r
set_service_description(service->name, service->handle, service->description);\r
TCHAR *service_name = argv[0];\r
TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
\r
- SC_HANDLE services = open_service_manager();\r
+ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
if (! services) {\r
print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
return 2;\r
}\r
\r
- SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));\r
+ unsigned long access = SERVICE_QUERY_STATUS;\r
+ switch (control) {\r
+ case NSSM_SERVICE_CONTROL_START:\r
+ access |= SERVICE_START;\r
+ break;\r
+\r
+ case SERVICE_CONTROL_CONTINUE:\r
+ case SERVICE_CONTROL_PAUSE:\r
+ access |= SERVICE_PAUSE_CONTINUE;\r
+ break;\r
+\r
+ case SERVICE_CONTROL_STOP:\r
+ access |= SERVICE_STOP;\r
+ break;\r
+\r
+ case NSSM_SERVICE_CONTROL_ROTATE:\r
+ access |= SERVICE_USER_DEFINED_CONTROL;\r
+ break;\r
+ }\r
+\r
+ SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));\r
if (! service_handle) {\r
CloseServiceHandle(services);\r
return 3;\r
\r
if (ret) {\r
int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
- CloseHandle(service_handle);\r
+ CloseServiceHandle(service_handle);\r
\r
if (response) {\r
print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
return 0;\r
}\r
else {\r
- CloseHandle(service_handle);\r
+ CloseServiceHandle(service_handle);\r
_ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
return 1;\r
}\r
\r
if (ret) {\r
int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
- CloseHandle(service_handle);\r
+ CloseServiceHandle(service_handle);\r
\r
if (response) {\r
print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
return 0;\r
}\r
else {\r
- CloseHandle(service_handle);\r
+ CloseServiceHandle(service_handle);\r
_ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
if (! service) return 1;\r
\r
/* Open service manager */\r
- SC_HANDLE services = open_service_manager();\r
+ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);\r
if (! services) {\r
print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
return 2;\r
}\r
\r
/* Try to open the service */\r
- service->handle = open_service(services, service->name, service->name, _countof(service->name));\r
+ service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));\r
if (! service->handle) {\r
CloseServiceHandle(services);\r
return 3;\r
/* Initialise status */\r
ZeroMemory(&service->status, sizeof(service->status));\r
service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
- service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
+ service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
service->status.dwWin32ExitCode = NO_ERROR;\r
service->status.dwServiceSpecificExitCode = 0;\r
service->status.dwCheckPoint = 0;\r
/* Try to create the exit action parameters; we don't care if it fails */\r
create_exit_action(service->name, exit_action_strings[0], false);\r
\r
- SC_HANDLE services = open_service_manager();\r
+ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);\r
if (services) {\r
- service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);\r
+ service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);\r
set_service_recovery(service);\r
+\r
CloseServiceHandle(services);\r
}\r
}\r
case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
+ case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");\r
default: return 0;\r
}\r
}\r
if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
return NO_ERROR;\r
+\r
+ case SERVICE_CONTROL_POWEREVENT:\r
+ if (event != PBT_APMRESUMEAUTOMATIC) {\r
+ log_service_control(service->name, control, false);\r
+ return NO_ERROR;\r
+ }\r
+ log_service_control(service->name, control, true);\r
+ end_service((void *) service, false);\r
+ return NO_ERROR;\r
}\r
\r
/* Unknown control */\r
unsigned long error = GetLastError();\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
close_output_handles(&si);\r
- duplicate_environment(service->initial_env);\r
+ duplicate_environment_strings(service->initial_env);\r
return stop_service(service, exitcode, true, true);\r
}\r
service->process_handle = pi.hProcess;\r
if (! service->no_console) FreeConsole();\r
\r
/* Restore our environment. */\r
- duplicate_environment(service->initial_env);\r
+ duplicate_environment_strings(service->initial_env);\r
\r
if (service->affinity) {\r
/*\r
but be mindful of the fact that we are blocking the service control manager\r
so abandon the wait before too much time has elapsed.\r
*/\r
- unsigned long delay = service->throttle_delay;\r
- if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
- TCHAR delay_milliseconds[16];\r
- _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
- TCHAR deadline_milliseconds[16];\r
- _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
- delay = NSSM_SERVICE_STATUS_DEADLINE;\r
- }\r
- unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
+ await_startup(service);\r
\r
/* Signal successful start */\r
service->status.dwCurrentState = SERVICE_RUNNING;\r
\r
/* Clean up. */\r
if (exitcode == STILL_ACTIVE) exitcode = 0;\r
- if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
+ if (service->pid && service->kill_process_tree) kill_process_tree(service, service->pid, exitcode, service->pid);\r
service->pid = 0;\r
\r
/*\r
\r
return ret;\r
}\r
+\r
+int await_startup(nssm_service_t *service) {\r
+ unsigned long interval;\r
+ unsigned long waithint;\r
+ unsigned long waited;\r
+\r
+ waithint = service->status.dwWaitHint;\r
+ waited = 0;\r
+ while (waited < service->throttle_delay) {\r
+ interval = service->throttle_delay - waited;\r
+ if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
+\r
+ service->status.dwCurrentState = SERVICE_START_PENDING;\r
+ service->status.dwWaitHint += interval;\r
+ service->status.dwCheckPoint++;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
+\r
+ switch (WaitForSingleObject(service->process_handle, interval)) {\r
+ case WAIT_OBJECT_0:\r
+ return 1;\r
+\r
+ case WAIT_TIMEOUT:\r
+ break;\r
+\r
+ default:\r
+ return -1;\r
+ }\r
+\r
+ waited += interval;\r
+ }\r
+\r
+ service->throttle = 0;\r
+\r
+ return 0;\r
+}\r