diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 6c99b6852c..ae1291446f 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -28,6 +28,7 @@ details. */ #include "ntdll.h" #include "exception.h" #include "posix_timer.h" +#include "cygwin/exit_process.h" /* Definitions for code simplification */ #ifdef __x86_64__ @@ -1545,8 +1546,23 @@ sigpacket::process () dosig: if (have_execed) { - sigproc_printf ("terminating captive process"); - TerminateProcess (ch_spawn, sigExeced = si.si_signo); + switch (si.si_signo) + { + case SIGUSR1: + case SIGUSR2: + case SIGCONT: + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + system_printf ("Suppressing signal %d to win32 process (pid %u)", + (int)si.si_signo, (unsigned int)GetProcessId(ch_spawn)); + goto done; + default: + sigproc_printf ("terminating captive process"); + rc = exit_process_tree (ch_spawn, 128 + (sigExeced = si.si_signo)); + goto done; + } } /* Dispatch to the appropriate function. */ sigproc_printf ("signal %d, signal handler %p", si.si_signo, handler); diff --git a/winsup/cygwin/include/cygwin/exit_process.h b/winsup/cygwin/include/cygwin/exit_process.h new file mode 100644 index 0000000000..e0981bbe4a --- /dev/null +++ b/winsup/cygwin/include/cygwin/exit_process.h @@ -0,0 +1,371 @@ +#ifndef EXIT_PROCESS_H +#define EXIT_PROCESS_H + +/* + * This file contains functions to terminate a Win32 process, as gently as + * possible. + * + * If appropriate, we will attempt to emulate a console Ctrl event for the + * process and its (transitive) children. Otherwise we will fall back to + * terminating the process(es). + * + * As we do not want to export this function in the MSYS2 runtime, these + * functions are marked as file-local. + * + * The idea is to inject a thread into the given process that runs either + * kernel32!CtrlRoutine() (i.e. the work horse of GenerateConsoleCtrlEvent()) + * for SIGINT (Ctrl+C) and SIGQUIT (Ctrl+Break), or ExitProcess() for SIGTERM. + * + * For SIGKILL, we run TerminateProcess() without injecting anything, and this + * is also the fall-back when the previous methods are unavailable. + * + * Note: as kernel32.dll is loaded before any process, the other process and + * this process will have ExitProcess() at the same address. The same holds + * true for kernel32!CtrlRoutine(), of course, but it is an internal API + * function, so we cannot look it up directly. Instead, we launch + * cygwin-console-helper.exe to find out (which has been modified to offer the + * option to print the address to stdout). + * + * 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 for the injected remote thread 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. + * + * The idea to use kernel32!CtrlRoutine for the other signals comes from + * SendSignal (https://github.com/AutoSQA/SendSignal/ and + * http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/). + */ + +#include + +#ifndef __INSIDE_CYGWIN__ +/* To help debugging via kill.exe */ +#define small_printf(...) fprintf(stderr, __VA_ARGS__) +#endif + +static LPTHREAD_START_ROUTINE +get_address_from_cygwin_console_helper(int bitness, wchar_t *function_name) +{ + const char *name; + if (bitness == 32) + name = "/usr/libexec/getprocaddr32.exe"; + else if (bitness == 64) + name = "/usr/libexec/getprocaddr64.exe"; + else + return NULL; /* what?!? */ + wchar_t wbuf[PATH_MAX]; + if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, name, wbuf, PATH_MAX) || + GetFileAttributesW (wbuf) == INVALID_FILE_ATTRIBUTES) + return NULL; + + HANDLE h[2]; + SECURITY_ATTRIBUTES attrs; + attrs.nLength = sizeof(attrs); + attrs.bInheritHandle = TRUE; + attrs.lpSecurityDescriptor = NULL; + if (!CreatePipe(&h[0], &h[1], &attrs, 0)) + return NULL; + + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi; + size_t len = wcslen (wbuf) + wcslen (function_name) + 1; + WCHAR cmd[len + 1]; + WCHAR title[] = L"cygwin-console-helper"; + + swprintf (cmd, len + 1, L"%S %S", wbuf, function_name); + + si.cb = sizeof (si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.lpTitle = title; + si.hStdInput = si.hStdError = INVALID_HANDLE_VALUE; + si.hStdOutput = h[1]; + + /* Create a new hidden process. Use the two event handles as + argv[1] and argv[2]. */ + BOOL x = CreateProcessW (NULL, cmd, NULL, NULL, TRUE, + CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP, + NULL, NULL, &si, &pi); + CloseHandle(h[1]); + if (!x) + { + CloseHandle(h[0]); + return NULL; + } + + CloseHandle (pi.hThread); + CloseHandle (pi.hProcess); + + char buffer[64]; + DWORD offset = 0, count = 0; + while (offset < sizeof(buffer) - 1) + { + if (!ReadFile(h[0], buffer + offset, 1, &count, NULL)) + { + offset = 0; + break; + } + if (!count || buffer[offset] == '\n') + break; + offset += count; + } + CloseHandle(h[0]); + + buffer[offset] = '\0'; + + return (LPTHREAD_START_ROUTINE) strtoull(buffer, NULL, 16); +} + +static int current_is_wow = -1; +static int is_32_bit_os = -1; + +static BOOL get_wow(HANDLE process, BOOL& is_wow, int& bitness) +{ + if (is_32_bit_os == -1) + { + SYSTEM_INFO info; + + GetNativeSystemInfo(&info); + if (info.wProcessorArchitecture == 0) + is_32_bit_os = 1; + else if (info.wProcessorArchitecture == 9) + is_32_bit_os = 0; + else + is_32_bit_os = -2; + } + + if (current_is_wow == -1 && + !IsWow64Process (GetCurrentProcess (), ¤t_is_wow)) + current_is_wow = -2; + + if (is_32_bit_os == -2 || current_is_wow == -2) + return FALSE; + + if (!IsWow64Process (process, &is_wow)) + return FALSE; + + bitness = is_32_bit_os || is_wow ? 32 : 64; + return TRUE; +} + +static LPTHREAD_START_ROUTINE +get_exit_process_address_for_process(HANDLE process) +{ + BOOL is_wow; + int bitness; + if (!get_wow (process, is_wow, bitness)) + return NULL; /* could not determine */ + + if (is_wow == current_is_wow) + { + static LPTHREAD_START_ROUTINE exit_process_address; + if (!exit_process_address) + { + HINSTANCE kernel32 = GetModuleHandle ("kernel32"); + exit_process_address = (LPTHREAD_START_ROUTINE) + GetProcAddress (kernel32, "ExitProcess"); + } + return exit_process_address; + } + + /* Trying to terminate a 32-bit process from 64-bit or vice versa */ + static LPTHREAD_START_ROUTINE exit_process_address; + if (!exit_process_address) + { + exit_process_address = + get_address_from_cygwin_console_helper(bitness, L"ExitProcess"); + } + return exit_process_address; +} + +static LPTHREAD_START_ROUTINE +get_ctrl_routine_address_for_process(HANDLE process) +{ + BOOL is_wow; + int bitness; + if (!get_wow (process, is_wow, bitness)) + return NULL; /* could not determine */ + + if (bitness == 64) + { + static LPTHREAD_START_ROUTINE ctrl_routine_address; + if (!ctrl_routine_address) + ctrl_routine_address = + get_address_from_cygwin_console_helper(64, L"CtrlRoutine"); + return ctrl_routine_address; + } + + static LPTHREAD_START_ROUTINE ctrl_routine_address; + if (!ctrl_routine_address) + { + ctrl_routine_address = + get_address_from_cygwin_console_helper(32, L"CtrlRoutine"); + } + return ctrl_routine_address; +} + +static int +inject_remote_thread_into_process(HANDLE process, LPTHREAD_START_ROUTINE address, int exit_code) +{ + int res = -1; + + if (!address) + return res; + + DWORD thread_id; + HANDLE thread = CreateRemoteThread (process, NULL, 1024 * 1024, address, + (PVOID) exit_code, 0, &thread_id); + if (thread) + { + /* + * Wait up to 10 seconds (arbitrary constant) for the thread to finish; + * After that grace period, fall back to terminating non-gently. + */ + if (WaitForSingleObject (thread, 10000) == WAIT_OBJECT_0) + res = 0; + CloseHandle (thread); + } + + return res; +} + +/** + * Terminates the process corresponding to the process ID + * + * This way of terminating the processes is not gentle: the process gets + * no chance of cleaning up after itself (closing file handles, removing + * .lock files, terminating spawned processes (if any), etc). + */ +static int +exit_one_process(HANDLE process, int exit_code) +{ + LPTHREAD_START_ROUTINE address = NULL; + int signo = exit_code & 0x7f; + + switch (signo) + { + case SIGINT: + case SIGQUIT: + address = get_ctrl_routine_address_for_process(process); + if (address && + !inject_remote_thread_into_process(process, address, + signo == SIGINT ? + CTRL_C_EVENT : CTRL_BREAK_EVENT)) + return 0; + /* fall-through */ + case SIGTERM: + address = get_exit_process_address_for_process(process); + if (address && !inject_remote_thread_into_process(process, address, exit_code)) + return 0; + break; + default: + break; + } + + return int(TerminateProcess (process, exit_code)); +} + +#include +#include + +/** + * Terminates the process corresponding to the process ID and all of its + * directly and indirectly spawned subprocesses using the + * exit_one_process() function. + */ +static int +exit_process_tree(HANDLE main_process, int exit_code) +{ + 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); + int signo = exit_code & 0x7f; + + 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 (;;) + { + memset (&entry, 0, sizeof (entry)); + entry.dwSize = sizeof (entry); + + if (!Process32First (snapshot, &entry)) + break; + + int orig_len = len; + do + { + /** + * Look for the parent process ID in the list of pids to kill, and if + * found, add it to the list. + */ + for (i = len - 1; i >= 0; i--) + { + if (pids[i] == entry.th32ProcessID) + break; + if (pids[i] != entry.th32ParentProcessID) + continue; + + /* We found a process to kill; is it an MSYS2 process? */ + pid_t cyg_pid = cygwin_winpid_to_pid(entry.th32ProcessID); + if (cyg_pid > -1) + { + if (cyg_pid == getpgid (cyg_pid)) + kill(cyg_pid, signo); + break; + } + pids[len++] = entry.th32ProcessID; + break; + } + } + while (len < max_len && Process32Next (snapshot, &entry)); + + if (orig_len == len || len >= max_len) + break; + } + + for (i = len - 1; i >= 0; i--) + { + HANDLE process; + + if (!i) + process = main_process; + else + { + process = OpenProcess (PROCESS_CREATE_THREAD | + PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_WRITE | + PROCESS_VM_READ, FALSE, pids[i]); + if (!process) + process = OpenProcess (PROCESS_TERMINATE, FALSE, pids[i]); + } + DWORD code; + + if (process && + (!GetExitCodeProcess (process, &code) || code == STILL_ACTIVE)) + { + if (!exit_one_process (process, exit_code)) + ret = -1; + } + if (process) + CloseHandle (process); + } + + return ret; +} + +#endif diff --git a/winsup/cygwin/include/cygwin/signal.h b/winsup/cygwin/include/cygwin/signal.h index e659d7ae06..081b0a5d67 100644 --- a/winsup/cygwin/include/cygwin/signal.h +++ b/winsup/cygwin/include/cygwin/signal.h @@ -478,6 +478,7 @@ extern const char *sys_siglist[]; extern const char __declspec(dllimport) *sys_sigabbrev[]; extern const char __declspec(dllimport) *sys_siglist[]; #endif +void kill_process_tree(pid_t pid, int sig); #ifdef __cplusplus } diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in index 49bb7ddb66..bbf493be63 100644 --- a/winsup/utils/Makefile.in +++ b/winsup/utils/Makefile.in @@ -35,6 +35,7 @@ prefix:=@prefix@ exec_prefix:=@exec_prefix@ bindir:=@bindir@ +libexecdir = @libexecdir@ program_transform_name:=@program_transform_name@ override INSTALL:=@INSTALL@ @@ -51,6 +52,8 @@ CYGWIN_LDFLAGS := -static -Wl,--enable-auto-import -L${WINDOWS_LIBDIR} $(LDLIBS) DEP_LDLIBS := $(cygwin_build)/libmsys-2.0.a MINGW_CXX := @MINGW_CXX@ +MINGW32_CC := @MINGW32_CC@ +MINGW64_CC := @MINGW64_CC@ # List all binaries to be linked in Cygwin mode. Each binary on this list # must have a corresponding .o of the same name. @@ -121,7 +124,7 @@ else all: warn_dumper endif -all: Makefile $(CYGWIN_BINS) $(MINGW_BINS) +all: Makefile $(CYGWIN_BINS) $(MINGW_BINS) getprocaddr32.exe getprocaddr64.exe # test harness support (note: the "MINGW_BINS +=" should come after the # "all:" above so that the testsuite is not run for "make" but only @@ -160,6 +163,19 @@ $(CYGWIN_BINS): %.exe: %.o $(MINGW_BINS): $(DEP_LDLIBS) $(CYGWIN_BINS): $(DEP_LDLIBS) +# Must *not* use -O2 here, as it screws up the stack backtrace +getprocaddr32.o: %32.o: %.c + $(MINGW32_CC) -c -o $@ $< + +getprocaddr32.exe: %.exe: %.o + $(MINGW32_CC) -o $@ $^ -static -ldbghelp + +getprocaddr64.o: %64.o: %.c + $(MINGW64_CC) -c -o $@ $< + +getprocaddr64.exe: %.exe: %.o + $(MINGW64_CC) -o $@ $^ -static -ldbghelp + cygcheck.o cygpath.o module_info.o path.o ps.o regtool.o strace.o: loadlib.h .PHONY: clean @@ -177,6 +193,10 @@ install: all n=`echo $$i | sed '$(program_transform_name)'`; \ $(INSTALL_PROGRAM) $$i $(DESTDIR)$(bindir)/$$n; \ done + /bin/mkdir -p ${DESTDIR}${libexecdir} + for n in getprocaddr32 getprocaddr64; do \ + $(INSTALL_PROGRAM) $$n $(DESTDIR)$(libexecdir)/$$n; \ + done $(cygwin_build)/libmsys-2.0.a: $(cygwin_build)/Makefile @$(MAKE) -C $(@D) $(@F) diff --git a/winsup/utils/configure b/winsup/utils/configure index 32f75d6e00..7ee4ee47e7 100755 --- a/winsup/utils/configure +++ b/winsup/utils/configure @@ -589,6 +589,8 @@ ac_no_link=no ac_subst_vars='LTLIBOBJS LIBOBJS configure_args +MINGW64_CC +MINGW32_CC MINGW_CXX INSTALL_DATA INSTALL_SCRIPT @@ -3303,6 +3305,93 @@ done test -n "$MINGW_CXX" || as_fn_error $? "no acceptable mingw g++ found in \$PATH" "$LINENO" 5 +for ac_prog in i686-w64-mingw32-gcc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MINGW32_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MINGW32_CC"; then + ac_cv_prog_MINGW32_CC="$MINGW32_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MINGW32_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MINGW32_CC=$ac_cv_prog_MINGW32_CC +if test -n "$MINGW32_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MINGW32_CC" >&5 +$as_echo "$MINGW32_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$MINGW32_CC" && break +done + +test -n "$MINGW32_CC" || as_fn_error $? "no acceptable mingw32 gcc found in \$PATH" "$LINENO" 5 +for ac_prog in x86_64-w64-mingw32-gcc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MINGW64_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MINGW64_CC"; then + ac_cv_prog_MINGW64_CC="$MINGW64_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MINGW64_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MINGW64_CC=$ac_cv_prog_MINGW64_CC +if test -n "$MINGW64_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MINGW64_CC" >&5 +$as_echo "$MINGW64_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$MINGW64_CC" && break +done + +test -n "$MINGW64_CC" || as_fn_error $? "no acceptable mingw64 gcc found in \$PATH" "$LINENO" 5 + configure_args=X diff --git a/winsup/utils/configure.ac b/winsup/utils/configure.ac index 63fc55e562..1cad2b9c05 100644 --- a/winsup/utils/configure.ac +++ b/winsup/utils/configure.ac @@ -35,6 +35,11 @@ AC_PROG_INSTALL AC_CHECK_PROGS(MINGW_CXX, ${target_cpu}-w64-mingw32-g++) test -n "$MINGW_CXX" || AC_MSG_ERROR([no acceptable mingw g++ found in \$PATH]) +AC_CHECK_PROGS(MINGW32_CC, i686-w64-mingw32-gcc) +test -n "$MINGW32_CC" || AC_MSG_ERROR([no acceptable mingw32 gcc found in \$PATH]) +AC_CHECK_PROGS(MINGW64_CC, x86_64-w64-mingw32-gcc) +test -n "$MINGW64_CC" || AC_MSG_ERROR([no acceptable mingw64 gcc found in \$PATH]) + AC_EXEEXT AC_CONFIGURE_ARGS AC_CONFIG_FILES([Makefile]) diff --git a/winsup/utils/getprocaddr.c b/winsup/utils/getprocaddr.c new file mode 100644 index 0000000000..54f8e27954 --- /dev/null +++ b/winsup/utils/getprocaddr.c @@ -0,0 +1,146 @@ +#include +#include + +/** + * To determine the address of kernel32!CtrlRoutine, we need to use + * dbghelp.dll. But we want to avoid linking statically to that library because + * the normal operation of cygwin-console-helper.exe (i.e. to allocate a hidden + * Console) does not need it. + * + * Therefore, we declare the SYMBOL_INFOW structure here, load the dbghelp + * library via LoadLibraryExA() and obtain the SymInitialize(), SymFromAddrW() + * and SymCleanup() functions via GetProcAddr(). + */ + +#include + +/* Avoid fprintf(), as it would try to reference '__getreent' */ +static void +output (BOOL error, const char *fmt, ...) +{ + va_list ap; + char buffer[1024]; + + va_start (ap, fmt); + vsnprintf (buffer, sizeof(buffer) - 1, fmt, ap); + buffer[sizeof(buffer) - 1] = '\0'; + va_end (ap); + WriteFile (GetStdHandle(error ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE), + buffer, strlen (buffer), NULL, NULL); +} + +static WINAPI BOOL +ctrl_handler(DWORD ctrl_type) +{ + unsigned short count; + void *address; + HANDLE process; + PSYMBOL_INFOW info; + DWORD64 displacement; + + count = CaptureStackBackTrace (1l /* skip this function */, + 1l /* return only one trace item */, + &address, NULL); + if (count != 1) + { + output (1, "Could not capture backtrace\n"); + return FALSE; + } + + process = GetCurrentProcess (); + if (!SymInitialize (process, NULL, TRUE)) + { + output (1, "Could not initialize symbols\n"); + return FALSE; + } + + info = (PSYMBOL_INFOW) + malloc (sizeof(*info) + MAX_SYM_NAME * sizeof(wchar_t)); + if (!info) + { + output (1, "Could not allocate symbol info structure\n"); + return FALSE; + } + info->SizeOfStruct = sizeof(*info); + info->MaxNameLen = MAX_SYM_NAME; + + if (!SymFromAddrW (process, (DWORD64)(intptr_t)address, &displacement, info)) + { + output (1, "Could not get symbol info\n"); + SymCleanup(process); + return FALSE; + } + output (0, "%p\n", (void *)(intptr_t)info->Address); + CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + SymCleanup(process); + + exit(0); +} + +int +main (int argc, char **argv) +{ + char *end; + + if (argc < 2) + { + output (1, "Need a function name\n"); + return 1; + } + + if (strcmp(argv[1], "CtrlRoutine")) + { + if (argc > 2) + { + output (1, "Unhandled option: %s\n", argv[2]); + return 1; + } + + HINSTANCE kernel32 = GetModuleHandle ("kernel32"); + if (!kernel32) + return 1; + void *address = (void *) GetProcAddress (kernel32, argv[1]); + if (!address) + return 1; + output (0, "%p\n", address); + return 0; + } + + /* Special-case kernel32!CtrlRoutine */ + if (argc == 3 && !strcmp ("--alloc-console", argv[2])) + { + if (!FreeConsole () && GetLastError () != ERROR_INVALID_PARAMETER) + { + output (1, "Could not detach from current Console: %d\n", + (int)GetLastError()); + return 1; + } + if (!AllocConsole ()) + { + output (1, "Could not allocate a new Console\n"); + return 1; + } + } + else if (argc > 2) + { + output (1, "Unhandled option: %s\n", argv[2]); + return 1; + } + + if (!SetConsoleCtrlHandler (ctrl_handler, TRUE)) + { + output (1, "Could not register Ctrl handler\n"); + return 1; + } + + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0)) + { + output (1, "Could not simulate Ctrl+Break\n"); + return 1; + } + + /* Give the event 1sec time to print out the address */ + Sleep(1000); + return 1; +} + diff --git a/winsup/utils/kill.cc b/winsup/utils/kill.cc index a22d70253f..45690516e5 100644 --- a/winsup/utils/kill.cc +++ b/winsup/utils/kill.cc @@ -17,6 +17,7 @@ details. */ #include #include #include +#include static char *prog_name; @@ -186,10 +187,20 @@ forcekill (pid_t pid, DWORD winpid, 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", - prog_name, (unsigned int) dwpid, (unsigned int) GetLastError ()); + { + HANDLE cur = GetCurrentProcess (), h2; + /* duplicate handle with access rights required for exit_process_tree() */ + if (DuplicateHandle (cur, h, cur, &h2, PROCESS_CREATE_THREAD | + PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | + PROCESS_VM_WRITE | PROCESS_VM_READ | + PROCESS_TERMINATE, FALSE, 0)) + { + CloseHandle(h); + h = h2; + } + exit_process_tree (h2, 128 + sig); + } CloseHandle (h); }