Browse Source

Command to show processes started by the service.

Print process tree with PID and module path.

Thanks Bader Aldurai.
Iain Patterson 5 years ago
parent
commit
586ea54f69
7 changed files with 124 additions and 6 deletions
  1. 11 0
      README.txt
  2. 1 0
      nssm.cpp
  3. 4 4
      nssm.vcproj
  4. 29 2
      process.cpp
  5. 4 0
      process.h
  6. 74 0
      service.cpp
  7. 1 0
      service.h

+ 11 - 0
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 <servicename>
+
+
 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
 -------

+ 1 - 0
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));

+ 4 - 4
nssm.vcproj

@@ -78,7 +78,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="shlwapi.lib"
+				AdditionalDependencies="psapi.lib shlwapi.lib"
 				LinkIncremental="2"
 				SuppressStartupBanner="true"
 				GenerateDebugInformation="true"
@@ -173,7 +173,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="shlwapi.lib"
+				AdditionalDependencies="psapi.lib shlwapi.lib"
 				LinkIncremental="2"
 				SuppressStartupBanner="true"
 				GenerateDebugInformation="true"
@@ -267,7 +267,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="shlwapi.lib"
+				AdditionalDependencies="psapi.lib shlwapi.lib"
 				LinkIncremental="1"
 				SuppressStartupBanner="true"
 				ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
@@ -361,7 +361,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="shlwapi.lib"
+				AdditionalDependencies="psapi.lib shlwapi.lib"
 				LinkIncremental="1"
 				SuppressStartupBanner="true"
 				ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"

+ 29 - 2
process.cpp

@@ -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. */
   if (! k->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);
+}

+ 4 - 0
process.h

@@ -1,11 +1,13 @@
 #ifndef PROCESS_H
 #define PROCESS_H
 
+#include <psapi.h>
 #include <tlhelp32.h>
 
 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);
 

+ 74 - 0
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;
+}

+ 1 - 0
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