diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index 6edd5816ed..003ff1ecc3 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 2dd355a037..6707eb9f5f 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1547,7 +1547,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 f304995056..6c636dde85 100644 --- a/winsup/cygwin/include/cygwin/signal.h +++ b/winsup/cygwin/include/cygwin/signal.h @@ -410,9 +410,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 27454965b7..994a2fed40 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -470,12 +470,13 @@ details. */ 303: Export pthread_getname_np, pthread_setname_np. 304: Export strerror_l, strptime_l, wcsftime_l. 305: [f]pathconf flag _PC_CASE_INSENSITIVE added. + 306: Export kill_process_tree. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 305 +#define CYGWIN_VERSION_API_MINOR 306 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index f371a231bb..45cf5d4c46 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -10,6 +10,7 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" +#include #include #include #include "pinfo.h" @@ -358,6 +359,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 c6adaa2810..6698f912be 100644 --- a/winsup/utils/kill.cc +++ b/winsup/utils/kill.cc @@ -171,10 +171,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); }