#include "nssm.h"\r
\r
-/* This is explicitly a wide string. */\r
-#define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"\r
-\r
bool is_admin;\r
bool use_critical_section;\r
\r
}\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
\r
if (! qsc) return 1;\r
\r
- if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;\r
-\r
- size_t len = _tcslen(qsc->lpServiceStartName);\r
- *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));\r
- if (! *username) {\r
- print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));\r
- return 2;\r
- }\r
-\r
- memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));\r
- *usernamelen = len;\r
-\r
- return 0;\r
-}\r
-\r
-int grant_logon_as_service(const TCHAR *username) {\r
- if (! username) return 0;\r
- if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;\r
-\r
- /* Open Policy object. */\r
- LSA_OBJECT_ATTRIBUTES attributes;\r
- ZeroMemory(&attributes, sizeof(attributes));\r
-\r
- LSA_HANDLE policy;\r
-\r
- NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);\r
- if (status) {\r
- print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));\r
- return 1;\r
- }\r
-\r
- /* Look up SID for the account. */\r
- LSA_UNICODE_STRING lsa_username;\r
-#ifdef UNICODE\r
- lsa_username.Buffer = (wchar_t *) username;\r
- lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);\r
- lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);\r
-#else\r
- size_t buflen;\r
- mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);\r
- lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);\r
- lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);\r
- lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);\r
- if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);\r
- else {\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));\r
- return 2;\r
- }\r
-#endif\r
-\r
- LSA_REFERENCED_DOMAIN_LIST *translated_domains;\r
- LSA_TRANSLATED_SID *translated_sid;\r
- status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);\r
-#ifndef UNICODE\r
- HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);\r
-#endif\r
- if (status) {\r
- LsaFreeMemory(translated_domains);\r
- LsaFreeMemory(translated_sid);\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));\r
- return 3;\r
- }\r
-\r
- if (translated_sid->Use != SidTypeUser) {\r
- LsaFreeMemory(translated_domains);\r
- LsaFreeMemory(translated_sid);\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);\r
- return 4;\r
- }\r
-\r
- LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];\r
- if (! trust || ! IsValidSid(trust->Sid)) {\r
- LsaFreeMemory(translated_domains);\r
- LsaFreeMemory(translated_sid);\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);\r
- return 4;\r
- }\r
-\r
- /* GetSidSubAuthority*() return pointers! */\r
- unsigned char *n = GetSidSubAuthorityCount(trust->Sid);\r
+ if (qsc->lpServiceStartName[0]) {\r
+ if (is_localsystem(qsc->lpServiceStartName)) return 0;\r
\r
- /* Convert translated SID to SID. */\r
- SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));\r
- if (! sid) {\r
- LsaFreeMemory(translated_domains);\r
- LsaFreeMemory(translated_sid);\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));\r
- return 4;\r
- }\r
-\r
- unsigned long error;\r
- if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {\r
- error = GetLastError();\r
- HeapFree(GetProcessHeap(), 0, sid);\r
- LsaFreeMemory(translated_domains);\r
- LsaFreeMemory(translated_sid);\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));\r
- return 5;\r
- }\r
-\r
- for (unsigned char i = 0; i <= *n; i++) {\r
- unsigned long *sub = GetSidSubAuthority(sid, i);\r
- if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);\r
- else *sub = translated_sid->RelativeId;\r
- }\r
-\r
- LsaFreeMemory(translated_domains);\r
- LsaFreeMemory(translated_sid);\r
-\r
- /* Check if the SID has the "Log on as a service" right. */\r
- LSA_UNICODE_STRING lsa_right;\r
- lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;\r
- lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);\r
- lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);\r
-\r
- LSA_UNICODE_STRING *rights;\r
- unsigned long count = ~0;\r
- status = LsaEnumerateAccountRights(policy, sid, &rights, &count);\r
- if (status) {\r
- /*\r
- If the account has no rights set LsaEnumerateAccountRights() will return\r
- STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.\r
- */\r
- error = LsaNtStatusToWinError(status);\r
- if (error != ERROR_FILE_NOT_FOUND) {\r
- HeapFree(GetProcessHeap(), 0, sid);\r
- LsaClose(policy);\r
- print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));\r
- return 4;\r
+ size_t len = _tcslen(qsc->lpServiceStartName);\r
+ *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));\r
+ if (! *username) {\r
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));\r
+ return 2;\r
}\r
- }\r
-\r
- for (unsigned long i = 0; i < count; i++) {\r
- if (rights[i].Length != lsa_right.Length) continue;\r
- if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;\r
- /* The SID has the right. */\r
- HeapFree(GetProcessHeap(), 0, sid);\r
- LsaFreeMemory(rights);\r
- LsaClose(policy);\r
- return 0;\r
- }\r
- LsaFreeMemory(rights);\r
\r
- /* Add the right. */\r
- status = LsaAddAccountRights(policy, sid, &lsa_right, 1);\r
- HeapFree(GetProcessHeap(), 0, sid);\r
- LsaClose(policy);\r
- if (status) {\r
- print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));\r
- return 5;\r
+ memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));\r
+ *usernamelen = len;\r
}\r
\r
- print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);\r
return 0;\r
}\r
\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
additional = argv[3];\r
remainder = 4;\r
}\r
+ else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {\r
+ additional = argv[3];\r
+ remainder = 4;\r
+ }\r
else {\r
additional = argv[remainder];\r
if (argc < mandatory) return usage(1);\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
/* Unset the parameter. */\r
value.string = 0;\r
}\r
+ else if (remainder == argc) {\r
+ value.string = 0;\r
+ }\r
else {\r
/* Set the parameter. */\r
size_t len = 0;\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 (grant_logon_as_service(username)) {\r
- print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
- return 5;\r
+ if (well_known_username(canon)) password = _T("");\r
+ else {\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
TCHAR cmd[CMD_LENGTH];\r
if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);\r
- close_output_handles(&si);\r
return stop_service(service, 2, true, true);\r
}\r
\r
if (service->env) duplicate_environment(service->env);\r
if (service->env_extra) set_environment_block(service->env_extra);\r
\r
+ /* Set up I/O redirection. */\r
+ if (get_output_handles(service, &si)) {\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
+ if (! service->no_console) FreeConsole();\r
+ close_output_handles(&si);\r
+ return stop_service(service, 4, true, true);\r
+ }\r
+\r
bool inherit_handles = false;\r
if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
unsigned long flags = service->priority & priority_mask();\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
\r
close_output_handles(&si);\r
\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
service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
\r
+ /* Use now as a dummy exit time. */\r
+ GetSystemTimeAsFileTime(&service->exit_time);\r
+\r
/* Check exit code */\r
unsigned long exitcode = 0;\r
TCHAR code[16];\r
if (service->process_handle) {\r
GetExitCodeProcess(service->process_handle, &exitcode);\r
- if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);\r
+ /* Check real exit time. */\r
+ if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);\r
CloseHandle(service->process_handle);\r
}\r
- else GetSystemTimeAsFileTime(&service->exit_time);\r
\r
service->process_handle = 0;\r
\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
- if (! service->no_console) FreeConsole();\r
-\r
/*\r
The why argument is true if our wait timed out or false otherwise.\r
Our wait is infinite so why will never be true when called by the system.\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