X-Git-Url: http://git.iain.cx/?a=blobdiff_plain;f=service.cpp;h=f18626ced304d34fbb3752ba0a010c9b7d74fbdc;hb=7bc41a82f696699aface2103f277e7ebf1808b2f;hp=b24eae2a4a7a5b4565a461e23693703c685e6766;hpb=095331018c46d251f6db151572bb1c2e76e911fa;p=nssm.git diff --git a/service.cpp b/service.cpp index b24eae2..f18626c 100644 --- a/service.cpp +++ b/service.cpp @@ -240,7 +240,7 @@ int affinity_string_to_mask(TCHAR *string, __int64 *mask) { return 0; } -inline unsigned long priority_mask() { +unsigned long priority_mask() { return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS; } @@ -429,6 +429,56 @@ QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE return qsc; } +/* WILL NOT allocate a new string if the identifier is already present. */ +int prepend_service_group_identifier(TCHAR *group, TCHAR **canon) { + if (! group || ! group[0] || group[0] == SC_GROUP_IDENTIFIER) { + *canon = group; + return 0; + } + + size_t len = _tcslen(group) + 1; + *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR)); + if (! *canon) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("prepend_service_group_identifier()")); + return 1; + } + + TCHAR *s = *canon; + *s++ = SC_GROUP_IDENTIFIER; + memmove(s, group, len * sizeof(TCHAR)); + (*canon)[len] = _T('\0'); + + return 0; +} + +int append_to_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) { + *newlen = 0; + + TCHAR *canon = 0; + if (type == DEPENDENCY_GROUPS) { + if (prepend_service_group_identifier(string, &canon)) return 1; + } + else canon = string; + int ret = append_to_double_null(dependencies, dependencieslen, newdependencies, newlen, canon, 0, false); + if (canon && canon != string) HeapFree(GetProcessHeap(), 0, canon); + + return ret; +} + +int remove_from_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) { + *newlen = 0; + + TCHAR *canon = 0; + if (type == DEPENDENCY_GROUPS) { + if (prepend_service_group_identifier(string, &canon)) return 1; + } + else canon = string; + int ret = remove_from_double_null(dependencies, dependencieslen, newdependencies, newlen, canon, 0, false); + if (canon && canon != string) HeapFree(GetProcessHeap(), 0, canon); + + return ret; +} + int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) { TCHAR *dependencies = _T(""); unsigned long num_dependencies = 0; @@ -562,8 +612,10 @@ int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); if (! qsc) return 3; - if (! qsc->lpDependencies) return 0; - if (! qsc->lpDependencies[0]) return 0; + if (! qsc->lpDependencies || ! qsc->lpDependencies[0]) { + HeapFree(GetProcessHeap(), 0, qsc); + return 0; + } /* lpDependencies is doubly NULL terminated. */ while (qsc->lpDependencies[*bufsize]) { @@ -577,6 +629,7 @@ int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle if (! *buffer) { *bufsize = 0; print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()")); + HeapFree(GetProcessHeap(), 0, qsc); return 4; } @@ -601,6 +654,12 @@ int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle HeapFree(GetProcessHeap(), 0, qsc); + if (! *buffer[0]) { + HeapFree(GetProcessHeap(), 0, *buffer); + *buffer = 0; + *bufsize = 0; + } + return 0; } @@ -653,8 +712,6 @@ int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error)); return 4; } - - return 0; } int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) { @@ -762,7 +819,7 @@ void cleanup_nssm_service(nssm_service_t *service) { if (! service) return; if (service->username) HeapFree(GetProcessHeap(), 0, service->username); if (service->password) { - SecureZeroMemory(service->password, service->passwordlen); + SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR)); HeapFree(GetProcessHeap(), 0, service->password); } if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies); @@ -826,7 +883,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (argc < 2) return usage(1); /* Are we editing on the command line? */ - enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING; + enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING, MODE_DUMPING } mode = MODE_EDITING; const TCHAR *verb = argv[0]; const TCHAR *service_name = argv[1]; bool getting = false; @@ -849,6 +906,11 @@ int pre_edit_service(int argc, TCHAR **argv) { mandatory = 3; mode = MODE_RESETTING; } + else if (str_equiv(verb, _T("dump"))) { + mandatory = 1; + remainder = 2; + mode = MODE_DUMPING; + } if (argc < mandatory) return usage(1); const TCHAR *parameter = 0; @@ -923,7 +985,7 @@ int pre_edit_service(int argc, TCHAR **argv) { service->type = qsc->dwServiceType; if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) { - if (mode != MODE_GETTING) { + if (mode != MODE_GETTING && mode != MODE_DUMPING) { HeapFree(GetProcessHeap(), 0, qsc); CloseServiceHandle(service->handle); CloseServiceHandle(services); @@ -933,7 +995,7 @@ int pre_edit_service(int argc, TCHAR **argv) { } if (get_service_startup(service->name, service->handle, qsc, &service->startup)) { - if (mode != MODE_GETTING) { + if (mode != MODE_GETTING && mode != MODE_DUMPING) { HeapFree(GetProcessHeap(), 0, qsc); CloseServiceHandle(service->handle); CloseServiceHandle(services); @@ -942,7 +1004,7 @@ int pre_edit_service(int argc, TCHAR **argv) { } if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) { - if (mode != MODE_GETTING) { + if (mode != MODE_GETTING && mode != MODE_DUMPING) { HeapFree(GetProcessHeap(), 0, qsc); CloseServiceHandle(service->handle); CloseServiceHandle(services); @@ -962,7 +1024,7 @@ int pre_edit_service(int argc, TCHAR **argv) { /* Get extended system details. */ if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) { - if (mode != MODE_GETTING) { + if (mode != MODE_GETTING && mode != MODE_DUMPING) { CloseServiceHandle(service->handle); CloseServiceHandle(services); return 6; @@ -970,7 +1032,7 @@ int pre_edit_service(int argc, TCHAR **argv) { } if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) { - if (mode != MODE_GETTING) { + if (mode != MODE_GETTING && mode != MODE_DUMPING) { CloseServiceHandle(service->handle); CloseServiceHandle(services); return 7; @@ -984,7 +1046,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (! service->exe[0]) { service->native = true; - if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image); + if (mode != MODE_GETTING && mode != MODE_DUMPING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image); } /* Editing with the GUI. */ @@ -993,6 +1055,41 @@ int pre_edit_service(int argc, TCHAR **argv) { return 0; } + HKEY key; + value_t value; + int ret; + + if (mode == MODE_DUMPING) { + TCHAR *service_name = service->name; + if (argc > remainder) service_name = argv[remainder]; + if (service->native) key = 0; + else { + key = open_registry(service->name, KEY_READ); + if (! key) return 4; + } + + TCHAR quoted_service_name[SERVICE_NAME_LENGTH * 2]; + TCHAR quoted_exe[EXE_LENGTH * 2]; + TCHAR quoted_nssm[EXE_LENGTH * 2]; + if (quote(service_name, quoted_service_name, _countof(quoted_service_name))) return 5; + if (quote(service->exe, quoted_exe, _countof(quoted_exe))) return 6; + if (quote(nssm_exe(), quoted_nssm, _countof(quoted_nssm))) return 6; + _tprintf(_T("%s install %s %s\n"), quoted_nssm, quoted_service_name, quoted_exe); + + ret = 0; + for (i = 0; settings[i].name; i++) { + setting = &settings[i]; + if (! setting->native && service->native) continue; + if (dump_setting(service_name, key, service->handle, setting)) ret++; + } + + if (! service->native) RegCloseKey(key); + CloseServiceHandle(service->handle); + + if (ret) return 1; + return 0; + } + /* Trying to manage App* parameters for a non-NSSM service. */ if (! setting->native && service->native) { CloseServiceHandle(service->handle); @@ -1000,10 +1097,6 @@ int pre_edit_service(int argc, TCHAR **argv) { return 1; } - HKEY key; - value_t value; - int ret; - if (mode == MODE_GETTING) { if (! service->native) { key = open_registry(service->name, KEY_READ); @@ -1026,7 +1119,7 @@ int pre_edit_service(int argc, TCHAR **argv) { break; case REG_DWORD: - _tprintf(_T("%u\n"), value.numeric); + _tprintf(_T("%lu\n"), value.numeric); break; } @@ -1074,7 +1167,7 @@ int pre_edit_service(int argc, TCHAR **argv) { } if (! service->native) { - key = open_registry(service->name, KEY_WRITE); + key = open_registry(service->name, KEY_READ | KEY_WRITE); if (! key) { if (value.string) HeapFree(GetProcessHeap(), 0, value.string); return 4; @@ -1723,15 +1816,7 @@ int start_service(nssm_service_t *service) { service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; SetServiceStatus(service->status_handle, &service->status); - /* Pre-start hook. */ unsigned long control = NSSM_SERVICE_CONTROL_START; - if (nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false) == NSSM_HOOK_STATUS_ABORT) { - TCHAR code[16]; - _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT); - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0); - unset_service_environment(service); - return stop_service(service, 5, true, true); - } /* Did another thread receive a stop control? */ if (service->allow_restart) { @@ -1744,6 +1829,15 @@ int start_service(nssm_service_t *service) { return stop_service(service, 4, true, true); } + /* Pre-start hook. May need I/O to have been redirected already. */ + if (nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false) == NSSM_HOOK_STATUS_ABORT) { + TCHAR code[16]; + _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0); + unset_service_environment(service); + return stop_service(service, 5, true, true); + } + /* The pre-start hook will have cleaned the environment. */ set_service_environment(service); @@ -1982,7 +2076,6 @@ void CALLBACK end_service(void *arg, unsigned char why) { wait_for_hooks(service, false); free_imports(); exit(exitcode); - break; } } @@ -2115,3 +2208,62 @@ awaited: return ret; } + +int list_nssm_services() { + /* Open service manager. */ + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 1; + } + + unsigned long bufsize, required, count, i; + unsigned long resume = 0; + EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume); + unsigned long error = GetLastError(); + if (error != ERROR_MORE_DATA) { + print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError())); + return 2; + } + + ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required); + if (! status) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()")); + return 3; + } + + bufsize = required; + while (true) { + int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume); + if (! ret) { + error = GetLastError(); + if (error != ERROR_MORE_DATA) { + HeapFree(GetProcessHeap(), 0, status); + print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError())); + return 4; + } + } + + for (i = 0; i < count; i++) { + /* Try to get the service parameters. */ + nssm_service_t *service = alloc_nssm_service(); + if (! service) { + HeapFree(GetProcessHeap(), 0, status); + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()")); + return 5; + } + _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName); + + get_parameters(service, 0); + /* We manage the service if we have an Application. */ + if (service->exe[0]) _tprintf(_T("%s\n"), service->name); + + cleanup_nssm_service(service); + } + + if (ret) break; + } + + HeapFree(GetProcessHeap(), 0, status); + return 0; +}