X-Git-Url: http://git.iain.cx/?a=blobdiff_plain;f=service.cpp;h=d66b91606f5b5be28627b8cb9da954118e39d33f;hb=b3b29f0b27046a282e688c494def532252ad21c3;hp=a94e6984d41e0b8a79a003ae2898cf1b87b9bf2b;hpb=de47d8ddf95aeddbdcaf0d9ecf2d126aae9a5df7;p=nssm.git diff --git a/service.cpp b/service.cpp index a94e698..d66b916 100644 --- a/service.cpp +++ b/service.cpp @@ -83,10 +83,11 @@ static inline int service_control_response(unsigned long control, unsigned long return 0; } -static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) { +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) { int tries = 0; unsigned long checkpoint = 0; unsigned long waithint = 0; + unsigned long waited = 0; while (QueryServiceStatus(service_handle, service_status)) { int response = service_control_response(control, service_status->dwCurrentState); /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */ @@ -96,13 +97,22 @@ static inline int await_service_control_response(unsigned long control, SC_HANDL checkpoint = service_status->dwCheckPoint; waithint = service_status->dwWaitHint; if (++tries > 10) tries = 10; - Sleep(50 * tries); + unsigned long wait = 50 * tries; + if (cutoff) { + if (waited > cutoff) return response; + waited += wait; + } + Sleep(wait); } else return response; } return -1; } +static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) { + return await_service_control_response(control, service_handle, service_status, initial_status, 0); +} + static inline void wait_for_hooks(nssm_service_t *service, bool notify) { SERVICE_STATUS_HANDLE status_handle; SERVICE_STATUS *status; @@ -883,7 +893,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; @@ -906,6 +916,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; @@ -980,7 +995,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); @@ -990,7 +1005,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); @@ -999,7 +1014,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); @@ -1019,7 +1034,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; @@ -1027,7 +1042,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; @@ -1041,7 +1056,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. */ @@ -1050,6 +1065,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); @@ -1057,10 +1107,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); @@ -1083,7 +1129,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; } @@ -1238,19 +1284,33 @@ int edit_service(nssm_service_t *service, bool editing) { TCHAR *username = 0; TCHAR *canon = 0; TCHAR *password = 0; + boolean virtual_account = false; if (service->usernamelen) { username = service->username; - if (canonicalise_username(username, &canon)) return 5; - if (service->passwordlen) password = service->password; + if (is_virtual_account(service->name, username)) { + virtual_account = true; + canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (service->usernamelen + 1) * sizeof(TCHAR)); + if (! canon) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("edit_service()")); + return 5; + } + memmove(canon, username, (service->usernamelen + 1) * sizeof(TCHAR)); + } + else { + if (canonicalise_username(username, &canon)) return 5; + if (service->passwordlen) password = service->password; + } } else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT; - if (well_known_username(canon)) password = _T(""); - else { - if (grant_logon_as_service(canon)) { - if (canon != username) HeapFree(GetProcessHeap(), 0, canon); - print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); - return 5; + if (! virtual_account) { + if (well_known_username(canon)) password = _T(""); + else { + if (grant_logon_as_service(canon)) { + if (canon != username) HeapFree(GetProcessHeap(), 0, canon); + print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); + return 5; + } } } @@ -1357,7 +1417,16 @@ int control_service(unsigned long control, int argc, TCHAR **argv) { } if (ret) { - int response = await_service_control_response(control, service_handle, &service_status, initial_status); + unsigned long cutoff = 0; + + /* If we manage the service, respect the throttle time. */ + HKEY key = open_registry(service_name, 0, KEY_READ, false); + if (key) { + if (get_number(key, NSSM_REG_THROTTLE, &cutoff, false) != 1) cutoff = NSSM_RESET_THROTTLE_RESTART; + RegCloseKey(key); + } + + int response = await_service_control_response(control, service_handle, &service_status, initial_status, cutoff); CloseServiceHandle(service_handle); if (response) { @@ -1467,6 +1536,9 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) { nssm_service_t *service = alloc_nssm_service(); if (! service) return; + static volatile bool await_debugger = (argc > 1 && str_equiv(argv[1], _T("debug"))); + while (await_debugger) Sleep(1000); + if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0); return; @@ -2038,8 +2110,7 @@ void CALLBACK end_service(void *arg, unsigned char why) { log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0); stop_service(service, exitcode, false, default_action); wait_for_hooks(service, false); - free_imports(); - exit(exitcode); + nssm_exit(exitcode); } } @@ -2173,7 +2244,9 @@ awaited: return ret; } -int list_nssm_services() { +int list_nssm_services(int argc, TCHAR **argv) { + bool including_native = (argc > 0 && str_equiv(argv[0], _T("all"))); + /* Open service manager. */ SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); if (! services) { @@ -2220,7 +2293,7 @@ int list_nssm_services() { get_parameters(service, 0); /* We manage the service if we have an Application. */ - if (service->exe[0]) _tprintf(_T("%s\n"), service->name); + if (including_native || service->exe[0]) _tprintf(_T("%s\n"), service->name); cleanup_nssm_service(service); } @@ -2231,3 +2304,77 @@ int list_nssm_services() { HeapFree(GetProcessHeap(), 0, status); return 0; } + +int service_process_tree(int argc, TCHAR **argv) { + int errors = 0; + if (argc < 1) return usage(1); + + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 1; + } + + /* + We need SeDebugPrivilege to read the process tree. + We ignore failure here so that an error will be printed later when we + try to open a process handle. + */ + HANDLE token = get_debug_token(); + + TCHAR canonical_name[SERVICE_NAME_LENGTH]; + SERVICE_STATUS_PROCESS service_status; + nssm_service_t *service; + kill_t k; + + int i; + for (i = 0; i < argc; i++) { + TCHAR *service_name = argv[i]; + SC_HANDLE service_handle = open_service(services, service_name, SERVICE_QUERY_STATUS, canonical_name, _countof(canonical_name)); + if (! service_handle) { + errors++; + continue; + } + + unsigned long size; + int ret = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (LPBYTE) &service_status, sizeof(service_status), &size); + long error = GetLastError(); + CloseServiceHandle(service_handle); + if (! ret) { + _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error)); + errors++; + continue; + } + + ZeroMemory(&k, sizeof(k)); + k.pid = service_status.dwProcessId; + if (! k.pid) continue; + + k.process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, k.pid); + if (! k.process_handle) { + _ftprintf(stderr, _T("%s: %lu: %s\n"), canonical_name, k.pid, error_string(GetLastError())); + continue; + } + + if (get_process_creation_time(k.process_handle, &k.creation_time)) continue; + /* Dummy exit time so we can check processes' parents. */ + GetSystemTimeAsFileTime(&k.exit_time); + + service = alloc_nssm_service(); + if (! service) { + errors++; + continue; + } + + _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), canonical_name); + k.name = service->name; + walk_process_tree(service, print_process, &k, k.pid); + + cleanup_nssm_service(service); + } + + CloseServiceHandle(services); + if (token != INVALID_HANDLE_VALUE) CloseHandle(token); + + return errors; +}