Command to show processes started by the service.
authorIain Patterson <me@iain.cx>
Mon, 25 Jul 2016 16:11:59 +0000 (17:11 +0100)
committerIain Patterson <me@iain.cx>
Thu, 28 Jul 2016 15:44:34 +0000 (16:44 +0100)
Print process tree with PID and module path.

Thanks Bader Aldurai.

README.txt
nssm.cpp
nssm.vcproj
process.cpp
process.h
service.cpp
service.h

index f8906d3..cc63c5d 100644 (file)
@@ -72,6 +72,8 @@ Since version 2.25, NSSM can list services it manages.
 \r
 Since version 2.25, NSSM can dump the configuration of services it manages.\r
 \r
+Since version 2.25, NSSM can show the processes managed by a service.\r
+\r
 \r
 Usage\r
 -----\r
@@ -869,6 +871,14 @@ The following command will print the names of all services managed by NSSM:
     nssm list\r
 \r
 \r
+Showing processes started by a service\r
+--------------------------------------\r
+The following command will print the process ID and executable path of\r
+processes started by a given service:\r
+\r
+    nssm processes <servicename>\r
+\r
+\r
 Exporting service configuration\r
 -------------------------------\r
 NSSM can dump commands which would recreate the configuration of a service.\r
@@ -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.\r
 Thanks to David Bremner for general tidyups.\r
 Thanks to Nabil Redmann for suggesting redirecting hooks' output.\r
+Thanks to Bader Aldurai for suggesting the process tree.\r
 \r
 Licence\r
 -------\r
index 9314753..6aa9a88 100644 (file)
--- a/nssm.cpp
+++ b/nssm.cpp
@@ -273,6 +273,7 @@ int _tmain(int argc, TCHAR **argv) {
       exit(ret);\r
     }\r
     if (str_equiv(argv[1], _T("list"))) exit(list_nssm_services());\r
+    if (str_equiv(argv[1], _T("processes"))) exit(service_process_tree(argc - 2, argv + 2));\r
     if (str_equiv(argv[1], _T("remove"))) {\r
       if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE));\r
       exit(pre_remove_service(argc - 2, argv + 2));\r
index 5719628..d5b924e 100755 (executable)
@@ -78,7 +78,7 @@
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="shlwapi.lib"\r
+                               AdditionalDependencies="psapi.lib shlwapi.lib"\r
                                LinkIncremental="2"\r
                                SuppressStartupBanner="true"\r
                                GenerateDebugInformation="true"\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="shlwapi.lib"\r
+                               AdditionalDependencies="psapi.lib shlwapi.lib"\r
                                LinkIncremental="2"\r
                                SuppressStartupBanner="true"\r
                                GenerateDebugInformation="true"\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="shlwapi.lib"\r
+                               AdditionalDependencies="psapi.lib shlwapi.lib"\r
                                LinkIncremental="1"\r
                                SuppressStartupBanner="true"\r
                                ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="shlwapi.lib"\r
+                               AdditionalDependencies="psapi.lib shlwapi.lib"\r
                                LinkIncremental="1"\r
                                SuppressStartupBanner="true"\r
                                ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"\r
index 220411b..dc99597 100644 (file)
@@ -321,11 +321,12 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u
   /* Shouldn't happen unless the service failed to start. */\r
   if (! k->pid) return; /* XXX: needed? */\r
   unsigned long pid = k->pid;\r
+  unsigned long depth = k->depth;\r
 \r
   TCHAR pid_string[16], code[16];\r
   _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pid);\r
   _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), k->exitcode);\r
-  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0);\r
+  if (fn == kill_process) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0);\r
 \r
   /* We will need a process handle in order to call TerminateProcess() later. */\r
   HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid);\r
@@ -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. */\r
     TCHAR ppid_string[16];\r
     _sntprintf_s(ppid_string, _countof(ppid_string), _TRUNCATE, _T("%lu"), ppid);\r
-    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0);\r
+    if (fn == kill_process) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0);\r
     k->process_handle = process_handle; /* XXX: open directly? */\r
     if (! fn(service, k)) {\r
       /* Maybe it already died. */\r
@@ -366,6 +367,7 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u
   }\r
 \r
   /* This is a child of the doomed process so kill it. */\r
+  k->depth++;\r
   if (! check_parent(k, &pe, pid)) {\r
     k->pid = pe.th32ProcessID;\r
     walk_process_tree(service, fn, k, ppid);\r
@@ -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;\r
       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0);\r
       CloseHandle(snapshot);\r
+      k->depth = depth;\r
       return;\r
     }\r
 \r
@@ -388,6 +391,7 @@ void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, u
     }\r
     k->pid = pid;\r
   }\r
+  k->depth = depth;\r
 \r
   CloseHandle(snapshot);\r
 }\r
@@ -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) {\r
   return walk_process_tree(NULL, kill_process, k, ppid);\r
 }\r
+\r
+int print_process(nssm_service_t *service, kill_t *k) {\r
+  TCHAR exe[EXE_LENGTH];\r
+  TCHAR *buffer = 0;\r
+  if (k->depth) {\r
+    buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (k->depth + 1) * sizeof(TCHAR));\r
+    if (buffer) {\r
+      unsigned long i;\r
+      for (i = 0; i < k->depth; i++) buffer[i] = _T(' ');\r
+      buffer[i] = _T('\0');\r
+    }\r
+  }\r
+  if (! GetModuleFileNameEx(k->process_handle, NULL, exe, _countof(exe))) _sntprintf_s(exe, _countof(exe), _TRUNCATE, _T("???"));\r
+\r
+  _tprintf(_T("% 8lu %s%s\n"), k->pid, buffer ? buffer : _T(""), exe);\r
+\r
+  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+  return 1;\r
+}\r
+\r
+int print_process(kill_t *k) {\r
+  return print_process(NULL, k);\r
+}\r
index de39cf0..3b4aab9 100644 (file)
--- a/process.h
+++ b/process.h
@@ -1,11 +1,13 @@
 #ifndef PROCESS_H\r
 #define PROCESS_H\r
 \r
+#include <psapi.h>\r
 #include <tlhelp32.h>\r
 \r
 typedef struct {\r
   TCHAR *name;\r
   HANDLE process_handle;\r
+  unsigned long depth;\r
   unsigned long pid;\r
   unsigned long exitcode;\r
   unsigned long stop_method;\r
@@ -33,6 +35,8 @@ int kill_console(nssm_service_t *, kill_t *);
 int kill_console(kill_t *);\r
 int kill_process(nssm_service_t *, kill_t *);\r
 int kill_process(kill_t *);\r
+int print_process(nssm_service_t *, kill_t *);\r
+int print_process(kill_t *);\r
 void walk_process_tree(nssm_service_t *, walk_function_t, kill_t *, unsigned long);\r
 void kill_process_tree(kill_t *, unsigned long);\r
 \r
index f18626c..849f790 100644 (file)
@@ -2267,3 +2267,77 @@ int list_nssm_services() {
   HeapFree(GetProcessHeap(), 0, status);\r
   return 0;\r
 }\r
+\r
+int service_process_tree(int argc, TCHAR **argv) {\r
+  int errors = 0;\r
+  if (argc < 1) return usage(1);\r
+\r
+  SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);\r
+  if (! services) {\r
+    print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+    return 1;\r
+  }\r
+\r
+  /*\r
+    We need SeDebugPrivilege to read the process tree.\r
+    We ignore failure here so that an error will be printed later when we\r
+    try to open a process handle.\r
+  */\r
+  HANDLE token = get_debug_token();\r
+\r
+  TCHAR canonical_name[SERVICE_NAME_LENGTH];\r
+  SERVICE_STATUS_PROCESS service_status;\r
+  nssm_service_t *service;\r
+  kill_t k;\r
+\r
+  int i;\r
+  for (i = 0; i < argc; i++) {\r
+    TCHAR *service_name = argv[i];\r
+    SC_HANDLE service_handle = open_service(services, service_name, SERVICE_QUERY_STATUS, canonical_name, _countof(canonical_name));\r
+    if (! service_handle) {\r
+      errors++;\r
+      continue;\r
+    }\r
+\r
+    unsigned long size;\r
+    int ret = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (LPBYTE) &service_status, sizeof(service_status), &size);\r
+    long error = GetLastError();\r
+    CloseServiceHandle(service_handle);\r
+    if (! ret) {\r
+      _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));\r
+      errors++;\r
+      continue;\r
+    }\r
+\r
+    ZeroMemory(&k, sizeof(k));\r
+    k.pid = service_status.dwProcessId;\r
+    if (! k.pid) continue;\r
+\r
+    k.process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, k.pid);\r
+    if (! k.process_handle) {\r
+      _ftprintf(stderr, _T("%s: %lu: %s\n"), canonical_name, k.pid, error_string(GetLastError()));\r
+      continue;\r
+    }\r
+\r
+    if (get_process_creation_time(k.process_handle, &k.creation_time)) continue;\r
+    /* Dummy exit time so we can check processes' parents. */\r
+    GetSystemTimeAsFileTime(&k.exit_time);\r
+\r
+    service = alloc_nssm_service();\r
+    if (! service) {\r
+      errors++;\r
+      continue;\r
+    }\r
+\r
+    _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), canonical_name);\r
+    k.name = service->name;\r
+    walk_process_tree(service, print_process, &k, k.pid);\r
+\r
+    cleanup_nssm_service(service);\r
+  }\r
+\r
+  CloseServiceHandle(services);\r
+  if (token != INVALID_HANDLE_VALUE) CloseHandle(token);\r
+\r
+  return errors;\r
+}\r
index 7e8d0ab..fa8ac78 100644 (file)
--- a/service.h
+++ b/service.h
@@ -164,5 +164,6 @@ void CALLBACK end_service(void *, unsigned char);
 void throttle_restart(nssm_service_t *);\r
 int await_single_handle(SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, TCHAR *, TCHAR *, unsigned long);\r
 int list_nssm_services();\r
+int service_process_tree(int, TCHAR **);\r
 \r
 #endif\r