From 586ea54f696a562ed3837b6b55e3fa1bbe1b2b22 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Mon, 25 Jul 2016 17:11:59 +0100 Subject: [PATCH] Command to show processes started by the service. Print process tree with PID and module path. Thanks Bader Aldurai. --- README.txt | 11 +++++++++ nssm.cpp | 1 + nssm.vcproj | 8 +++---- process.cpp | 31 ++++++++++++++++++++++++-- process.h | 4 ++++ service.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ service.h | 1 + 7 files changed, 124 insertions(+), 6 deletions(-) diff --git a/README.txt b/README.txt index f8906d3..cc63c5d 100644 --- a/README.txt +++ b/README.txt @@ -72,6 +72,8 @@ Since version 2.25, NSSM can list services it manages. Since version 2.25, NSSM can dump the configuration of services it manages. +Since version 2.25, NSSM can show the processes managed by a service. + Usage ----- @@ -869,6 +871,14 @@ The following command will print the names of all services managed by NSSM: nssm list +Showing processes started by a service +-------------------------------------- +The following command will print the process ID and executable path of +processes started by a given service: + + nssm processes + + Exporting service configuration ------------------------------- NSSM can dump commands which would recreate the configuration of a service. @@ -985,6 +995,7 @@ Thanks to Paul Baxter for help with Visual Studio 2015. Thanks to Mathias Breiner for help with Visual Studio and some registry fixes. Thanks to David Bremner for general tidyups. Thanks to Nabil Redmann for suggesting redirecting hooks' output. +Thanks to Bader Aldurai for suggesting the process tree. Licence ------- diff --git a/nssm.cpp b/nssm.cpp index 9314753..6aa9a88 100644 --- a/nssm.cpp +++ b/nssm.cpp @@ -273,6 +273,7 @@ int _tmain(int argc, TCHAR **argv) { exit(ret); } if (str_equiv(argv[1], _T("list"))) exit(list_nssm_services()); + if (str_equiv(argv[1], _T("processes"))) exit(service_process_tree(argc - 2, argv + 2)); if (str_equiv(argv[1], _T("remove"))) { if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE)); exit(pre_remove_service(argc - 2, argv + 2)); diff --git a/nssm.vcproj b/nssm.vcproj index 5719628..d5b924e 100755 --- a/nssm.vcproj +++ b/nssm.vcproj @@ -78,7 +78,7 @@ /> pid) return; /* XXX: needed? */ unsigned long pid = k->pid; + unsigned long depth = k->depth; TCHAR pid_string[16], code[16]; _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pid); _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), k->exitcode); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0); + if (fn == kill_process) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0); /* We will need a process handle in order to call TerminateProcess() later. */ HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid); @@ -333,7 +334,7 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u /* Kill this process first, then its descendents. */ TCHAR ppid_string[16]; _sntprintf_s(ppid_string, _countof(ppid_string), _TRUNCATE, _T("%lu"), ppid); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0); + if (fn == kill_process) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0); k->process_handle = process_handle; /* XXX: open directly? */ if (! fn(service, k)) { /* Maybe it already died. */ @@ -366,6 +367,7 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u } /* This is a child of the doomed process so kill it. */ + k->depth++; if (! check_parent(k, &pe, pid)) { k->pid = pe.th32ProcessID; walk_process_tree(service, fn, k, ppid); @@ -379,6 +381,7 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u if (ret == ERROR_NO_MORE_FILES) break; log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); CloseHandle(snapshot); + k->depth = depth; return; } @@ -388,6 +391,7 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u } k->pid = pid; } + k->depth = depth; CloseHandle(snapshot); } @@ -395,3 +399,26 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u void kill_process_tree(kill_t *k, unsigned long ppid) { return walk_process_tree(NULL, kill_process, k, ppid); } + +int print_process(nssm_service_t *service, kill_t *k) { + TCHAR exe[EXE_LENGTH]; + TCHAR *buffer = 0; + if (k->depth) { + buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (k->depth + 1) * sizeof(TCHAR)); + if (buffer) { + unsigned long i; + for (i = 0; i < k->depth; i++) buffer[i] = _T(' '); + buffer[i] = _T('\0'); + } + } + if (! GetModuleFileNameEx(k->process_handle, NULL, exe, _countof(exe))) _sntprintf_s(exe, _countof(exe), _TRUNCATE, _T("???")); + + _tprintf(_T("% 8lu %s%s\n"), k->pid, buffer ? buffer : _T(""), exe); + + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return 1; +} + +int print_process(kill_t *k) { + return print_process(NULL, k); +} diff --git a/process.h b/process.h index de39cf0..3b4aab9 100644 --- a/process.h +++ b/process.h @@ -1,11 +1,13 @@ #ifndef PROCESS_H #define PROCESS_H +#include #include typedef struct { TCHAR *name; HANDLE process_handle; + unsigned long depth; unsigned long pid; unsigned long exitcode; unsigned long stop_method; @@ -33,6 +35,8 @@ int kill_console(nssm_service_t *, kill_t *); int kill_console(kill_t *); int kill_process(nssm_service_t *, kill_t *); int kill_process(kill_t *); +int print_process(nssm_service_t *, kill_t *); +int print_process(kill_t *); void walk_process_tree(nssm_service_t *, walk_function_t, kill_t *, unsigned long); void kill_process_tree(kill_t *, unsigned long); diff --git a/service.cpp b/service.cpp index f18626c..849f790 100644 --- a/service.cpp +++ b/service.cpp @@ -2267,3 +2267,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; +} diff --git a/service.h b/service.h index 7e8d0ab..fa8ac78 100644 --- a/service.h +++ b/service.h @@ -164,5 +164,6 @@ void CALLBACK end_service(void *, unsigned char); void throttle_restart(nssm_service_t *); int await_single_handle(SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, TCHAR *, TCHAR *, unsigned long); int list_nssm_services(); +int service_process_tree(int, TCHAR **); #endif -- 2.7.4