From 4c41c03abe7a7dabe87699243ee89f5b158fda2c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 17 May 2017 18:14:34 +0200 Subject: [PATCH] mingw: kill unterminated child processes on signals Git for Windows' MSYS2 runtime was just adjusted to kill processes gently, by injecting a thread that calls ExitProcess(). In case of signals (such as when handling Ctrl+C in a MinTTY window), the exit code is 128 + sign_no, as expected by Git's source code. However, as there is no POSIX signal handling on Windows, no signal handlers are called. Instead, functions registered via atexit() are called. We work around that by testing the exit code explicitly. This fixes the Git for Windows side of the bug where interrupting `git clone https://...` would send the spawned-off `git remote-https` process into the background instead of interrupting it, i.e. the clone would continue and its progress would be reported mercilessly to the console window without the user being able to do anything about it (short of firing up the task manager and killing the appropriate task manually). Signed-off-by: Johannes Schindelin --- compat/mingw.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/compat/mingw.c b/compat/mingw.c index 7c83dc918aa249..db0e420d3de434 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1461,10 +1461,44 @@ struct pinfo_t { static struct pinfo_t *pinfo = NULL; CRITICAL_SECTION pinfo_cs; +#ifndef SIGRTMAX +#define SIGRTMAX 63 +#endif + +static void kill_child_processes_on_signal(void) +{ + DWORD status; + + /* + * Only continue if the process was terminated by a signal, as + * indicated by the exit status (128 + sig_no). + * + * As we are running in an atexit() handler, the exit code has been + * set at this stage by the ExitProcess() function already. + */ + if (!GetExitCodeProcess(GetCurrentProcess(), &status) || + status <= 128 || status > 128 + SIGRTMAX) + return; + + EnterCriticalSection(&pinfo_cs); + + while (pinfo) { + struct pinfo_t *info = pinfo; + pinfo = pinfo->next; + if (exit_process(info->proc, status)) + /* the handle is still valid in case of error */ + CloseHandle(info->proc); + free(info); + } + + LeaveCriticalSection(&pinfo_cs); +} + static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv, const char *dir, int prepend_cmd, int fhin, int fhout, int fherr) { + static int atexit_handler_initialized; STARTUPINFOW si; PROCESS_INFORMATION pi; struct strbuf args; @@ -1474,6 +1508,17 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen HANDLE cons; const char *strace_env; + if (!atexit_handler_initialized) { + atexit_handler_initialized = 1; + /* + * On Windows, there is no POSIX signaling. Instead, we inject + * a thread calling ExitProcess(128 + sig_no); and that calls + * the *atexit* handlers. Catch this condition and kill child + * processes with the same signal. + */ + atexit(kill_child_processes_on_signal); + } + do_unset_environment_variables(); /* Determine whether or not we are associated to a console */