Allow listing all services.
[nssm.git] / process.cpp
index d1a9a49..e78283c 100644 (file)
@@ -2,6 +2,47 @@
 \r
 extern imports_t imports;\r
 \r
+HANDLE get_debug_token() {\r
+  long error;\r
+  HANDLE token;\r
+  if (! OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, false, &token)) {\r
+    error = GetLastError();\r
+    if (error == ERROR_NO_TOKEN) {\r
+      (void) ImpersonateSelf(SecurityImpersonation);\r
+      (void) OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, false, &token);\r
+    }\r
+  }\r
+  if (! token) return INVALID_HANDLE_VALUE;\r
+\r
+  TOKEN_PRIVILEGES privileges, old;\r
+  unsigned long size = sizeof(TOKEN_PRIVILEGES);\r
+  LUID luid;\r
+  if (! LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {\r
+    CloseHandle(token);\r
+    return INVALID_HANDLE_VALUE;\r
+  }\r
+\r
+  privileges.PrivilegeCount = 1;\r
+  privileges.Privileges[0].Luid = luid;\r
+  privileges.Privileges[0].Attributes = 0;\r
+\r
+  if (! AdjustTokenPrivileges(token, false, &privileges, size, &old, &size)) {\r
+    CloseHandle(token);\r
+    return INVALID_HANDLE_VALUE;\r
+  }\r
+\r
+  old.PrivilegeCount = 1;\r
+  old.Privileges[0].Luid = luid;\r
+  old.Privileges[0].Attributes |= SE_PRIVILEGE_ENABLED;\r
+\r
+  if (! AdjustTokenPrivileges(token, false, &old, size, NULL, NULL)) {\r
+    CloseHandle(token);\r
+    return INVALID_HANDLE_VALUE;\r
+  }\r
+\r
+  return token;\r
+}\r
+\r
 void service_kill_t(nssm_service_t *service, kill_t *k) {\r
   if (! service) return;\r
   if (! k) return;\r
@@ -115,7 +156,7 @@ int kill_threads(nssm_service_t *service, kill_t *k) {
 \r
   /* Get a snapshot of all threads in the system. */\r
   HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);\r
-  if (! snapshot) {\r
+  if (snapshot == INVALID_HANDLE_VALUE) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_THREAD_FAILED, k->name, error_string(GetLastError()), 0);\r
     return 0;\r
   }\r
@@ -241,7 +282,8 @@ int kill_console(nssm_service_t *service, kill_t *k) {
 \r
   /* Ignore the event ourselves. */\r
   ret = 0;\r
-  if (! SetConsoleCtrlHandler(0, TRUE)) {\r
+  BOOL ignored = SetConsoleCtrlHandler(0, TRUE);\r
+  if (! ignored) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, k->name, error_string(GetLastError()), 0);\r
     ret = 4;\r
   }\r
@@ -262,6 +304,11 @@ int kill_console(nssm_service_t *service, kill_t *k) {
   /* Wait for process to exit. */\r
   if (await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_console_delay)) ret = 6;\r
 \r
+  /* Remove our handler. */\r
+  if (ignored && ! SetConsoleCtrlHandler(0, FALSE)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, k->name, error_string(GetLastError()), 0);\r
+  }\r
+\r
   return ret;\r
 }\r
 \r
@@ -269,16 +316,17 @@ int kill_console(kill_t *k) {
   return kill_console(NULL, k);\r
 }\r
 \r
-void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid) {\r
+void walk_process_tree(nssm_service_t *service, walk_function_t fn, kill_t *k, unsigned long ppid) {\r
   if (! k) return;\r
   /* 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
@@ -286,9 +334,9 @@ void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid)
     /* 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 (! kill_process(k)) {\r
+    if (! fn(service, k)) {\r
       /* Maybe it already died. */\r
       unsigned long ret;\r
       if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) {\r
@@ -303,7 +351,7 @@ void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid)
 \r
   /* Get a snapshot of all processes in the system. */\r
   HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);\r
-  if (! snapshot) {\r
+  if (snapshot == INVALID_HANDLE_VALUE) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, k->name, error_string(GetLastError()), 0);\r
     return;\r
   }\r
@@ -319,9 +367,10 @@ void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid)
   }\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
-    kill_process_tree(k, ppid);\r
+    walk_process_tree(service, fn, k, ppid);\r
   }\r
   k->pid = pid;\r
 \r
@@ -332,19 +381,55 @@ void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid)
       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
     if (! check_parent(k, &pe, pid)) {\r
       k->pid = pe.th32ProcessID;\r
-      kill_process_tree(k, ppid);\r
+      walk_process_tree(service, fn, k, ppid);\r
     }\r
     k->pid = pid;\r
   }\r
+  k->depth = depth;\r
 \r
   CloseHandle(snapshot);\r
 }\r
 \r
 void kill_process_tree(kill_t *k, unsigned long ppid) {\r
-  return kill_process_tree(NULL, k, 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
+\r
+  unsigned long size = _countof(exe);\r
+  if (! imports.QueryFullProcessImageName || ! imports.QueryFullProcessImageName(k->process_handle, 0, exe, &size)) {\r
+    /*\r
+      Fall back to GetModuleFileNameEx(), which won't work for WOW64 processes.\r
+    */\r
+    if (! GetModuleFileNameEx(k->process_handle, NULL, exe, _countof(exe))) {\r
+      long error = GetLastError();\r
+      if (error == ERROR_PARTIAL_COPY) _sntprintf_s(exe, _countof(exe), _TRUNCATE, _T("[WOW64]"));\r
+      else _sntprintf_s(exe, _countof(exe), _TRUNCATE, _T("???"));\r
+    }\r
+  }\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