[TASKMGR] Implement proper process tree ending
[reactos.git] / base / applications / taskmgr / endproc.c
index a26fd76..74ce95f 100644 (file)
@@ -3,8 +3,9 @@
  *
  *  endproc.c
  *
- *  Copyright (C) 1999 - 2001  Brian Palmer  <brianp@reactos.org>
- *                2005         Klemens Friedl <frik85@reactos.at>
+ *  Copyright (C) 1999 - 2001  Brian Palmer               <brianp@reactos.org>
+ *                2005         Klemens Friedl             <frik85@reactos.at>
+ *                2014         Ismael Ferreras Morezuelas <swyterzone+ros@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -23,6 +24,9 @@
 
 #include "precomp.h"
 
+#define NTOS_MODE_USER
+#include <ndk/psfuncs.h>
+
 void ProcessPage_OnEndProcess(void)
 {
     DWORD   dwProcessId;
@@ -35,31 +39,125 @@ void ProcessPage_OnEndProcess(void)
     if (dwProcessId == 0)
         return;
 
+    hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
+
+    /* forbid killing system processes even if we have privileges -- sigh, windows kludge! */
+    if (hProcess && IsCriticalProcess(hProcess))
+    {
+        LoadStringW(hInst, IDS_MSG_UNABLETERMINATEPRO, szTitle, 256);
+        LoadStringW(hInst, IDS_MSG_CLOSESYSTEMPROCESS, strErrorText, 256);
+        MessageBoxW(hMainWnd, strErrorText, szTitle, MB_OK|MB_ICONWARNING|MB_TOPMOST);
+        CloseHandle(hProcess);
+        return;
+    }
+
+    /* if this is a standard process just ask for confirmation before doing it */
     LoadStringW(hInst, IDS_MSG_WARNINGTERMINATING, strErrorText, 256);
     LoadStringW(hInst, IDS_MSG_TASKMGRWARNING, szTitle, 256);
-    if (MessageBoxW(hMainWnd, strErrorText, szTitle, MB_YESNO|MB_ICONWARNING) != IDYES)
+    if (MessageBoxW(hMainWnd, strErrorText, szTitle, MB_YESNO|MB_ICONWARNING|MB_TOPMOST) != IDYES)
+    {
+        if (hProcess) CloseHandle(hProcess);
         return;
+    }
 
-    hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
-
+    /* no such process or not enough privileges to open its token */
     if (!hProcess)
     {
         GetLastErrorText(strErrorText, 260);
         LoadStringW(hInst, IDS_MSG_UNABLETERMINATEPRO, szTitle, 256);
-        MessageBoxW(hMainWnd, strErrorText, szTitle, MB_OK|MB_ICONSTOP);
+        MessageBoxW(hMainWnd, strErrorText, szTitle, MB_OK|MB_ICONSTOP|MB_TOPMOST);
         return;
     }
 
+    /* try to kill it, and notify the user if didn't work */
     if (!TerminateProcess(hProcess, 1))
     {
         GetLastErrorText(strErrorText, 260);
         LoadStringW(hInst, IDS_MSG_UNABLETERMINATEPRO, szTitle, 256);
-        MessageBoxW(hMainWnd, strErrorText, szTitle, MB_OK|MB_ICONSTOP);
+        MessageBoxW(hMainWnd, strErrorText, szTitle, MB_OK|MB_ICONSTOP|MB_TOPMOST);
     }
 
     CloseHandle(hProcess);
 }
 
+BOOL IsCriticalProcess(HANDLE hProcess)
+{
+    NTSTATUS status;
+    ULONG BreakOnTermination;
+
+    /* return early if the process handle does not exist */
+    if (!hProcess)
+        return FALSE;
+
+    /* the important system processes that we don't want to let the user
+       kill come marked as critical, this simplifies the check greatly.
+
+       a critical process brings the system down when is terminated:
+       <http://www.geoffchappell.com/studies/windows/win32/ntdll/api/rtl/peb/setprocessiscritical.htm> */
+
+    status = NtQueryInformationProcess(hProcess,
+                                       ProcessBreakOnTermination,
+                                       &BreakOnTermination,
+                                       sizeof(ULONG),
+                                       NULL);
+
+    if (NT_SUCCESS(status) && BreakOnTermination)
+        return TRUE;
+
+    return FALSE;
+}
+
+BOOL ShutdownProcessTreeHelper(HANDLE hSnapshot, HANDLE hParentProcess, DWORD dwParentPID)
+{
+    HANDLE hChildHandle;
+    PROCESSENTRY32W ProcessEntry = {0};
+    ProcessEntry.dwSize = sizeof(ProcessEntry);
+
+    if (Process32FirstW(hSnapshot, &ProcessEntry))
+    {
+        do
+        {
+            if (ProcessEntry.th32ParentProcessID == dwParentPID)
+            {
+                hChildHandle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
+                                           FALSE,
+                                           ProcessEntry.th32ProcessID);
+                if (!hChildHandle || IsCriticalProcess(hChildHandle))
+                {
+                    if (hChildHandle)
+                    {
+                        CloseHandle(hChildHandle);
+                    }
+                    continue;
+                }
+                if (!ShutdownProcessTreeHelper(hSnapshot, hChildHandle, ProcessEntry.th32ProcessID))
+                {
+                    CloseHandle(hChildHandle);
+                    return FALSE;
+                }
+                CloseHandle(hChildHandle);
+            }
+        } while (Process32NextW(hSnapshot, &ProcessEntry));
+    }
+
+    return TerminateProcess(hParentProcess, 0);
+}
+
+BOOL ShutdownProcessTree(HANDLE hParentProcess, DWORD dwParentPID)
+{
+    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    BOOL bResult;
+
+    if (!hSnapshot)
+    {
+        return FALSE;
+    }
+
+    bResult = ShutdownProcessTreeHelper(hSnapshot, hParentProcess, dwParentPID);
+    CloseHandle(hSnapshot);
+    return bResult;
+}
+
 void ProcessPage_OnEndProcessTree(void)
 {
     DWORD   dwProcessId;
@@ -72,12 +170,25 @@ void ProcessPage_OnEndProcessTree(void)
     if (dwProcessId == 0)
         return;
 
+    hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
+
+    /* forbid killing system processes even if we have privileges -- sigh, windows kludge! */
+    if (hProcess && IsCriticalProcess(hProcess))
+    {
+        LoadStringW(hInst, IDS_MSG_UNABLETERMINATEPRO, szTitle, 256);
+        LoadStringW(hInst, IDS_MSG_CLOSESYSTEMPROCESS, strErrorText, 256);
+        MessageBoxW(hMainWnd, strErrorText, szTitle, MB_OK|MB_ICONWARNING|MB_TOPMOST);
+        CloseHandle(hProcess);
+        return;
+    }
+
     LoadStringW(hInst, IDS_MSG_WARNINGTERMINATING, strErrorText, 256);
     LoadStringW(hInst, IDS_MSG_TASKMGRWARNING, szTitle, 256);
     if (MessageBoxW(hMainWnd, strErrorText, szTitle, MB_YESNO|MB_ICONWARNING) != IDYES)
+    {
+        if (hProcess) CloseHandle(hProcess);
         return;
-
-    hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
+    }
 
     if (!hProcess)
     {
@@ -87,7 +198,7 @@ void ProcessPage_OnEndProcessTree(void)
         return;
     }
 
-    if (!TerminateProcess(hProcess, 0))
+    if (!ShutdownProcessTree(hProcess, dwProcessId))
     {
         GetLastErrorText(strErrorText, 260);
         LoadStringW(hInst, IDS_MSG_UNABLETERMINATEPRO, szTitle, 256);