зеркало из https://github.com/microsoft/git.git
Merge pull request #1170 from dscho/mingw-kill-process
Handle Ctrl+C in Git Bash nicely Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Коммит
4df560e2de
|
@ -10,6 +10,7 @@
|
|||
#include "../cache.h"
|
||||
#include "../abspath.h"
|
||||
#include "../alloc.h"
|
||||
#include "win32/exit-process.h"
|
||||
#include "win32/lazyload.h"
|
||||
#include "../config.h"
|
||||
#include "../environment.h"
|
||||
|
@ -2307,16 +2308,28 @@ int mingw_execvp(const char *cmd, char *const *argv)
|
|||
int mingw_kill(pid_t pid, int sig)
|
||||
{
|
||||
if (pid > 0 && sig == SIGTERM) {
|
||||
HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
|
||||
HANDLE h = OpenProcess(PROCESS_CREATE_THREAD |
|
||||
PROCESS_QUERY_INFORMATION |
|
||||
PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
|
||||
PROCESS_VM_READ | PROCESS_TERMINATE,
|
||||
FALSE, pid);
|
||||
int ret;
|
||||
|
||||
if (TerminateProcess(h, -1)) {
|
||||
CloseHandle(h);
|
||||
return 0;
|
||||
if (h)
|
||||
ret = exit_process(h, 128 + sig);
|
||||
else {
|
||||
h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
|
||||
if (!h) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
ret = terminate_process_tree(h, 128 + sig);
|
||||
}
|
||||
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
CloseHandle(h);
|
||||
return -1;
|
||||
if (ret) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
CloseHandle(h);
|
||||
}
|
||||
return ret;
|
||||
} else if (pid > 0 && sig == 0) {
|
||||
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||
if (h) {
|
||||
|
@ -3929,7 +3942,14 @@ static void adjust_symlink_flags(void)
|
|||
symlink_file_flags |= 2;
|
||||
symlink_directory_flags |= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL WINAPI handle_ctrl_c(DWORD ctrl_type)
|
||||
{
|
||||
if (ctrl_type != CTRL_C_EVENT)
|
||||
return FALSE; /* we did not handle this */
|
||||
mingw_raise(SIGINT);
|
||||
return TRUE; /* we did handle this */
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -3965,6 +3985,8 @@ int wmain(int argc, const wchar_t **wargv)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
SetConsoleCtrlHandler(handle_ctrl_c, TRUE);
|
||||
|
||||
maybe_redirect_std_handles();
|
||||
adjust_symlink_flags();
|
||||
fsync_object_files = 1;
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче