diff --git a/winsup/cygwin/common-msys.din b/winsup/cygwin/common-msys.din index 9cdb6dd889..831a15c26f 100644 --- a/winsup/cygwin/common-msys.din +++ b/winsup/cygwin/common-msys.din @@ -33,6 +33,7 @@ sys_errlist = _sys_errlist DATA sys_nerr = _sys_nerr DATA sys_sigabbrev DATA sys_siglist DATA +kill_process_tree DATA # Exported functions _Exit SIGFE diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index d7f4d24955..2b2ddce18a 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -33,6 +33,7 @@ sys_errlist = _sys_errlist DATA sys_nerr = _sys_nerr DATA sys_sigabbrev DATA sys_siglist DATA +kill_process_tree DATA # Exported functions _Exit SIGFE diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 0105ca9583..f333ec3cd7 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1559,7 +1559,10 @@ sigpacket::process () if (have_execed) { sigproc_printf ("terminating captive process"); - TerminateProcess (ch_spawn, sigExeced = si.si_signo); + if ((sigExeced = si.si_signo) == SIGINT) + kill_process_tree (GetProcessId (ch_spawn), sigExeced = si.si_signo); + else + TerminateProcess (ch_spawn, sigExeced = si.si_signo); } /* Dispatch to the appropriate function. */ sigproc_printf ("signal %d, signal handler %p", si.si_signo, handler); diff --git a/winsup/cygwin/include/cygwin/signal.h b/winsup/cygwin/include/cygwin/signal.h index 19dd0a64ad..ead45a13d0 100644 --- a/winsup/cygwin/include/cygwin/signal.h +++ b/winsup/cygwin/include/cygwin/signal.h @@ -420,9 +420,11 @@ int siginterrupt (int, int); #ifdef __INSIDE_CYGWIN__ extern const char *sys_sigabbrev[]; extern const char *sys_siglist[]; +extern void kill_process_tree(pid_t pid, int sig); #else extern const char __declspec(dllimport) *sys_sigabbrev[]; extern const char __declspec(dllimport) *sys_siglist[]; +extern void __declspec(dllimport) kill_process_tree(pid_t pid, int sig); #endif #ifdef __cplusplus diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index ea7700a2c0..a80856a305 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -474,6 +474,7 @@ details. */ 290: Add sysconf cache handling. 291: Export aligned_alloc, at_quick_exit, quick_exit. 292: Export rpmatch. + 293: Export kill_process_tree. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull, diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index 8dfd4ab63c..fdb882a184 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -13,6 +13,7 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" +#include #include #include #include "pinfo.h" @@ -361,6 +362,62 @@ killpg (pid_t pgrp, int sig) return kill (-pgrp, sig); } +/** + * Terminates the process corresponding to the process ID and all of its + * directly and indirectly spawned subprocesses. + */ +extern "C" void +kill_process_tree(pid_t pid, int sig) +{ + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 entry; + DWORD pids[16384]; + int max_len = sizeof(pids) / sizeof(*pids), i, len; + + 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) { + TerminateProcess(process, sig << 8); + CloseHandle(process); + } + } +} + extern "C" void abort (void) { diff --git a/winsup/utils/kill.cc b/winsup/utils/kill.cc index edabff3214..d34b115baa 100644 --- a/winsup/utils/kill.cc +++ b/winsup/utils/kill.cc @@ -174,10 +174,14 @@ forcekill (int pid, int sig, int wait) return; } if (!wait || WaitForSingleObject (h, 200) != WAIT_OBJECT_0) - if (sig && !TerminateProcess (h, sig << 8) - && WaitForSingleObject (h, 200) != WAIT_OBJECT_0) - fprintf (stderr, "%s: couldn't kill pid %u, %u\n", + { + if (sig == SIGINT || sig == SIGTERM) + kill_process_tree (dwpid, sig); + else if (sig && !TerminateProcess (h, sig << 8) + && WaitForSingleObject (h, 200) != WAIT_OBJECT_0) + fprintf (stderr, "%s: couldn't kill pid %u, %u\n", prog_name, (unsigned) dwpid, (unsigned int) GetLastError ()); + } CloseHandle (h); }