From 20b6a57af64506a4152b145b031aad2946a37ca8 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Tue, 23 May 2023 10:57:56 +0100 Subject: win32: crtl-c interrupts non-console applications Commit 9db9b34ad (win32: ignore ctrl-c in parent of execve(2)) prevented a parent process from reacting to Ctrl-C while it was waiting for its child to complete. This avoids the problem where a shell and an interactive child end up competing for input after a Ctrl-C. However, a child process which isn't attached to the console (a GUI application, for example) can't then be killed by Ctrl-C. Instead of completely ignoring Ctrl-C give the parent a handler which detects if its child is attached to the console. If so it's left to handle Ctrl-C itself and the parent ignores the interrupt. If not the parent terminates the child and all its children as if by SIGINT. Costs 200 bytes. --- include/mingw.h | 1 + win32/process.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/mingw.h b/include/mingw.h index f94804d91..13adf9017 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -540,6 +540,7 @@ int mingw_execve(const char *cmd, char *const *argv, char *const *envp); #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') +BOOL WINAPI kill_child_ctrl_handler(DWORD dwCtrlType); int kill_signal_by_handle(HANDLE process, int sig); int FAST_FUNC is_valid_signal(int number); diff --git a/win32/process.c b/win32/process.c index ebc006d4e..6abc329da 100644 --- a/win32/process.c +++ b/win32/process.c @@ -369,11 +369,46 @@ mingw_spawn_proc(const char **argv) return mingw_spawnvp(P_NOWAIT, argv[0], (char *const *)argv); } +BOOL WINAPI kill_child_ctrl_handler(DWORD dwCtrlType) +{ + static pid_t child_pid = 0; + DWORD dummy, *procs, count, rcount, i; + + if (child_pid == 0) { + // First call sets child pid + child_pid = dwCtrlType; + return FALSE; + } + + if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { + count = GetConsoleProcessList(&dummy, 1) + 16; + procs = malloc(sizeof(DWORD) * count); + rcount = GetConsoleProcessList(procs, count); + if (rcount != 0 && rcount <= count) { + for (i = 0; i < rcount; i++) { + if (procs[i] == child_pid) { + // Child is attached to our console + break; + } + } + if (i == rcount) { + // Kill non-console child; console children can + // handle Ctrl-C as they see fit. + kill(-child_pid, SIGINT); + } + } + free(procs); + return TRUE; + } + return FALSE; +} + static NORETURN void wait_for_child(HANDLE child) { DWORD code; - SetConsoleCtrlHandler(NULL, TRUE); + kill_child_ctrl_handler(GetProcessId(child)); + SetConsoleCtrlHandler(kill_child_ctrl_handler, TRUE); WaitForSingleObject(child, INFINITE); GetExitCodeProcess(child, &code); exit((int)code); -- cgit v1.2.3-55-g6feb