| #ifndef EXIT_PROCESS_H | |
| #define EXIT_PROCESS_H | |
| /* | |
| * This file contains functions to terminate a Win32 process, as gently as | |
| * possible. | |
| * | |
| * At first, we will attempt to inject a thread that calls ExitProcess(). If | |
| * that fails, we will fall back to terminating the entire process tree. | |
| * | |
| * For simplicity, these functions are marked as file-local. | |
| */ | |
| #include <tlhelp32.h> | |
| /* | |
| * Terminates the process corresponding to the process ID and all of its | |
| * directly and indirectly spawned subprocesses. | |
| * | |
| * This way of terminating the processes is not gentle: the processes get | |
| * no chance of cleaning up after themselves (closing file handles, removing | |
| * .lock files, terminating spawned processes (if any), etc). | |
| */ | |
| static int terminate_process_tree(HANDLE main_process, int exit_status) | |
| { | |
| HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | |
| PROCESSENTRY32 entry; | |
| DWORD pids[16384]; | |
| int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; | |
| pid_t pid = GetProcessId(main_process); | |
| pids[0] = (DWORD)pid; | |
| len = 1; | |
| /* | |
| * Even if Process32First()/Process32Next() seem to traverse the | |
| * processes in topological order (i.e. parent processes before | |
| * child processes), there is nothing in the Win32 API documentation | |
| * suggesting that this is guaranteed. | |
| * | |
| * Therefore, run through them at least twice and stop when no more | |
| * process IDs were added to the list. | |
| */ | |
| for (;;) { | |
| int orig_len = len; | |
| memset(&entry, 0, sizeof(entry)); | |
| entry.dwSize = sizeof(entry); | |
| if (!Process32First(snapshot, &entry)) | |
| break; | |
| do { | |
| for (i = len - 1; i >= 0; i--) { | |
| if (pids[i] == entry.th32ProcessID) | |
| break; | |
| if (pids[i] == entry.th32ParentProcessID) | |
| pids[len++] = entry.th32ProcessID; | |
| } | |
| } while (len < max_len && Process32Next(snapshot, &entry)); | |
| if (orig_len == len || len >= max_len) | |
| break; | |
| } | |
| for (i = len - 1; i > 0; i--) { | |
| HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pids[i]); | |
| if (process) { | |
| if (!TerminateProcess(process, exit_status)) | |
| ret = -1; | |
| CloseHandle(process); | |
| } | |
| } | |
| if (!TerminateProcess(main_process, exit_status)) | |
| ret = -1; | |
| CloseHandle(main_process); | |
| return ret; | |
| } | |
| /** | |
| * Determine whether a process runs in the same architecture as the current | |
| * one. That test is required before we assume that GetProcAddress() returns | |
| * a valid address *for the target process*. | |
| */ | |
| static inline int process_architecture_matches_current(HANDLE process) | |
| { | |
| static BOOL current_is_wow = -1; | |
| BOOL is_wow; | |
| if (current_is_wow == -1 && | |
| !IsWow64Process (GetCurrentProcess(), ¤t_is_wow)) | |
| current_is_wow = -2; | |
| if (current_is_wow == -2) | |
| return 0; /* could not determine current process' WoW-ness */ | |
| if (!IsWow64Process (process, &is_wow)) | |
| return 0; /* cannot determine */ | |
| return is_wow == current_is_wow; | |
| } | |
| /** | |
| * Inject a thread into the given process that runs ExitProcess(). | |
| * | |
| * Note: as kernel32.dll is loaded before any process, the other process and | |
| * this process will have ExitProcess() at the same address. | |
| * | |
| * This function expects the process handle to have the access rights for | |
| * CreateRemoteThread(): PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, | |
| * PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ. | |
| * | |
| * The idea comes from the Dr Dobb's article "A Safer Alternative to | |
| * TerminateProcess()" by Andrew Tucker (July 1, 1999), | |
| * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 | |
| * | |
| * If this method fails, we fall back to running terminate_process_tree(). | |
| */ | |
| static int exit_process(HANDLE process, int exit_code) | |
| { | |
| DWORD code; | |
| if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) { | |
| static int initialized; | |
| static LPTHREAD_START_ROUTINE exit_process_address; | |
| PVOID arg = (PVOID)(intptr_t)exit_code; | |
| DWORD thread_id; | |
| HANDLE thread = NULL; | |
| if (!initialized) { | |
| HINSTANCE kernel32 = GetModuleHandleA("kernel32"); | |
| if (!kernel32) | |
| die("BUG: cannot find kernel32"); | |
| exit_process_address = | |
| (LPTHREAD_START_ROUTINE)(void (*)(void)) | |
| GetProcAddress(kernel32, "ExitProcess"); | |
| initialized = 1; | |
| } | |
| if (!exit_process_address || | |
| !process_architecture_matches_current(process)) | |
| return terminate_process_tree(process, exit_code); | |
| thread = CreateRemoteThread(process, NULL, 0, | |
| exit_process_address, | |
| arg, 0, &thread_id); | |
| if (thread) { | |
| CloseHandle(thread); | |
| /* | |
| * If the process survives for 10 seconds (a completely | |
| * arbitrary value picked from thin air), fall back to | |
| * killing the process tree via TerminateProcess(). | |
| */ | |
| if (WaitForSingleObject(process, 10000) == | |
| WAIT_OBJECT_0) { | |
| CloseHandle(process); | |
| return 0; | |
| } | |
| } | |
| return terminate_process_tree(process, exit_code); | |
| } | |
| CloseHandle(process); | |
| return 0; | |
| } | |
| #endif |