diff options
-rw-r--r-- | coreutils/timeout.c | 18 | ||||
-rw-r--r-- | include/mingw.h | 1 | ||||
-rw-r--r-- | win32/process.c | 196 |
3 files changed, 101 insertions, 114 deletions
diff --git a/coreutils/timeout.c b/coreutils/timeout.c index ff58a753a..802ddfc07 100644 --- a/coreutils/timeout.c +++ b/coreutils/timeout.c | |||
@@ -54,7 +54,9 @@ static HANDLE child = INVALID_HANDLE_VALUE; | |||
54 | static void kill_child(void) | 54 | static void kill_child(void) |
55 | { | 55 | { |
56 | if (child != INVALID_HANDLE_VALUE) { | 56 | if (child != INVALID_HANDLE_VALUE) { |
57 | kill_signal_by_handle(child, SIGTERM); | 57 | pid_t pid = (pid_t)GetProcessId(child); |
58 | if (pid) | ||
59 | kill(pid, SIGTERM); | ||
58 | } | 60 | } |
59 | } | 61 | } |
60 | 62 | ||
@@ -206,13 +208,15 @@ int timeout_main(int argc UNUSED_PARAM, char **argv) | |||
206 | status = signo == SIGKILL ? 137 : 124; | 208 | status = signo == SIGKILL ? 137 : 124; |
207 | 209 | ||
208 | pid = (pid_t)GetProcessId(child); | 210 | pid = (pid_t)GetProcessId(child); |
209 | kill(pid, signo); | 211 | if (pid) { |
212 | kill(pid, signo); | ||
210 | 213 | ||
211 | if (kill_timeout > 0) { | 214 | if (kill_timeout > 0) { |
212 | if (timeout_wait(kill_timeout, child, &status)) | 215 | if (timeout_wait(kill_timeout, child, &status)) |
213 | goto finish; | 216 | goto finish; |
214 | kill(pid, SIGKILL); | 217 | kill(pid, SIGKILL); |
215 | status = 137; | 218 | status = 137; |
219 | } | ||
216 | } | 220 | } |
217 | finish: | 221 | finish: |
218 | CloseHandle(child); | 222 | CloseHandle(child); |
diff --git a/include/mingw.h b/include/mingw.h index 41d4cd940..56a7285e5 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
@@ -569,7 +569,6 @@ int mingw_execve(const char *cmd, char *const *argv, char *const *envp); | |||
569 | #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') | 569 | #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') |
570 | 570 | ||
571 | BOOL WINAPI kill_child_ctrl_handler(DWORD dwCtrlType); | 571 | BOOL WINAPI kill_child_ctrl_handler(DWORD dwCtrlType); |
572 | int kill_signal_by_handle(HANDLE process, int sig); | ||
573 | int FAST_FUNC is_valid_signal(int number); | 572 | int FAST_FUNC is_valid_signal(int number); |
574 | int exit_code_to_wait_status(DWORD win_exit_code); | 573 | int exit_code_to_wait_status(DWORD win_exit_code); |
575 | int exit_code_to_posix(DWORD win_exit_code); | 574 | int exit_code_to_posix(DWORD win_exit_code); |
diff --git a/win32/process.c b/win32/process.c index 5be07e54a..b993bb2be 100644 --- a/win32/process.c +++ b/win32/process.c | |||
@@ -685,14 +685,98 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
685 | } | 685 | } |
686 | 686 | ||
687 | /** | 687 | /** |
688 | * Determine whether a process runs in the same architecture as the current | ||
689 | * one. That test is required before we assume that GetProcAddress() returns | ||
690 | * a valid address *for the target process*. | ||
691 | */ | ||
692 | static inline int process_architecture_matches_current(HANDLE process) | ||
693 | { | ||
694 | static BOOL current_is_wow = -1; | ||
695 | BOOL is_wow; | ||
696 | |||
697 | if (current_is_wow == -1 && | ||
698 | !IsWow64Process (GetCurrentProcess(), ¤t_is_wow)) | ||
699 | current_is_wow = -2; | ||
700 | if (current_is_wow == -2) | ||
701 | return 0; /* could not determine current process' WoW-ness */ | ||
702 | if (!IsWow64Process (process, &is_wow)) | ||
703 | return 0; /* cannot determine */ | ||
704 | return is_wow == current_is_wow; | ||
705 | } | ||
706 | |||
707 | /** | ||
708 | * This function tries to terminate a Win32 process, as gently as possible, | ||
709 | * by injecting a thread that calls ExitProcess(). | ||
710 | * | ||
711 | * Note: as kernel32.dll is loaded before any process, the other process and | ||
712 | * this process will have ExitProcess() at the same address. | ||
713 | * | ||
714 | * The idea comes from the Dr Dobb's article "A Safer Alternative to | ||
715 | * TerminateProcess()" by Andrew Tucker (July 1, 1999), | ||
716 | * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 | ||
717 | * | ||
718 | */ | ||
719 | static int kill_signal_by_handle(HANDLE process, int sig) | ||
720 | { | ||
721 | DECLARE_PROC_ADDR(DWORD, ExitProcess, LPVOID); | ||
722 | PVOID arg = (PVOID)(intptr_t)(sig << 24); | ||
723 | DWORD thread_id; | ||
724 | HANDLE thread; | ||
725 | |||
726 | if (!INIT_PROC_ADDR(kernel32, ExitProcess) || | ||
727 | !process_architecture_matches_current(process)) { | ||
728 | SetLastError(ERROR_ACCESS_DENIED); | ||
729 | return -1; | ||
730 | } | ||
731 | |||
732 | if (sig != 0 && (thread = CreateRemoteThread(process, NULL, 0, | ||
733 | ExitProcess, arg, 0, &thread_id))) { | ||
734 | CloseHandle(thread); | ||
735 | } | ||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | static int kill_signal(pid_t pid, int sig) | ||
740 | { | ||
741 | HANDLE process; | ||
742 | int ret = 0; | ||
743 | DWORD code; | ||
744 | |||
745 | if (sig == SIGKILL) | ||
746 | process = OpenProcess(PROCESS_TERMINATE, FALSE, pid); | ||
747 | else | ||
748 | process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD | | ||
749 | PROCESS_QUERY_INFORMATION | | ||
750 | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | | ||
751 | PROCESS_VM_READ, FALSE, pid); | ||
752 | |||
753 | if (!process) | ||
754 | return -1; | ||
755 | |||
756 | if (!GetExitCodeProcess(process, &code) || code != STILL_ACTIVE) { | ||
757 | SetLastError(ERROR_INVALID_PARAMETER); | ||
758 | ret = -1; | ||
759 | } else if (sig == SIGKILL) { | ||
760 | /* This way of terminating processes is not gentle: they get no | ||
761 | * chance to clean up after themselves (closing file handles, | ||
762 | * removing .lock files, terminating spawned processes (if any), | ||
763 | * etc). */ | ||
764 | ret = !TerminateProcess(process, SIGKILL << 24); | ||
765 | } else { | ||
766 | ret = kill_signal_by_handle(process, sig); | ||
767 | } | ||
768 | CloseHandle(process); | ||
769 | |||
770 | return ret; | ||
771 | } | ||
772 | |||
773 | /** | ||
688 | * If the process ID is positive invoke the callback for that process | 774 | * If the process ID is positive invoke the callback for that process |
689 | * only. If negative or zero invoke the callback for all descendants | 775 | * only. If negative or zero invoke the callback for all descendants |
690 | * of the indicated process. Zero indicates the current process; negative | 776 | * of the indicated process. Zero indicates the current process; negative |
691 | * indicates the process with process ID -pid. | 777 | * indicates the process with process ID -pid. |
692 | */ | 778 | */ |
693 | typedef int (*kill_callback)(pid_t pid, int sig); | 779 | static int kill_pids(pid_t pid, int sig) |
694 | |||
695 | static int kill_pids(pid_t pid, int sig, kill_callback killer) | ||
696 | { | 780 | { |
697 | DWORD pids[16384]; | 781 | DWORD pids[16384]; |
698 | int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; | 782 | int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; |
@@ -750,7 +834,7 @@ static int kill_pids(pid_t pid, int sig, kill_callback killer) | |||
750 | 834 | ||
751 | for (i = len - 1; i >= 0; i--) { | 835 | for (i = len - 1; i >= 0; i--) { |
752 | SetLastError(0); | 836 | SetLastError(0); |
753 | if (killer(pids[i], sig)) { | 837 | if (kill_signal(pids[i], sig)) { |
754 | errno = err_win_to_posix(); | 838 | errno = err_win_to_posix(); |
755 | ret = -1; | 839 | ret = -1; |
756 | } | 840 | } |
@@ -759,104 +843,6 @@ static int kill_pids(pid_t pid, int sig, kill_callback killer) | |||
759 | return ret; | 843 | return ret; |
760 | } | 844 | } |
761 | 845 | ||
762 | /** | ||
763 | * Determine whether a process runs in the same architecture as the current | ||
764 | * one. That test is required before we assume that GetProcAddress() returns | ||
765 | * a valid address *for the target process*. | ||
766 | */ | ||
767 | static inline int process_architecture_matches_current(HANDLE process) | ||
768 | { | ||
769 | static BOOL current_is_wow = -1; | ||
770 | BOOL is_wow; | ||
771 | |||
772 | if (current_is_wow == -1 && | ||
773 | !IsWow64Process (GetCurrentProcess(), ¤t_is_wow)) | ||
774 | current_is_wow = -2; | ||
775 | if (current_is_wow == -2) | ||
776 | return 0; /* could not determine current process' WoW-ness */ | ||
777 | if (!IsWow64Process (process, &is_wow)) | ||
778 | return 0; /* cannot determine */ | ||
779 | return is_wow == current_is_wow; | ||
780 | } | ||
781 | |||
782 | /** | ||
783 | * This function tries to terminate a Win32 process, as gently as possible, | ||
784 | * by injecting a thread that calls ExitProcess(). | ||
785 | * | ||
786 | * Note: as kernel32.dll is loaded before any process, the other process and | ||
787 | * this process will have ExitProcess() at the same address. | ||
788 | * | ||
789 | * The idea comes from the Dr Dobb's article "A Safer Alternative to | ||
790 | * TerminateProcess()" by Andrew Tucker (July 1, 1999), | ||
791 | * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 | ||
792 | * | ||
793 | */ | ||
794 | int kill_signal_by_handle(HANDLE process, int sig) | ||
795 | { | ||
796 | DWORD code; | ||
797 | int ret = 0; | ||
798 | |||
799 | if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) { | ||
800 | DECLARE_PROC_ADDR(DWORD, ExitProcess, LPVOID); | ||
801 | PVOID arg = (PVOID)(intptr_t)(sig << 24); | ||
802 | DWORD thread_id; | ||
803 | HANDLE thread; | ||
804 | |||
805 | if (!INIT_PROC_ADDR(kernel32, ExitProcess) || | ||
806 | !process_architecture_matches_current(process)) { | ||
807 | SetLastError(ERROR_ACCESS_DENIED); | ||
808 | ret = -1; | ||
809 | goto finish; | ||
810 | } | ||
811 | |||
812 | if ((thread = CreateRemoteThread(process, NULL, 0, | ||
813 | ExitProcess, arg, 0, &thread_id))) { | ||
814 | CloseHandle(thread); | ||
815 | } | ||
816 | } | ||
817 | |||
818 | finish: | ||
819 | CloseHandle(process); | ||
820 | return ret; | ||
821 | } | ||
822 | |||
823 | static int kill_signal(pid_t pid, int sig) | ||
824 | { | ||
825 | HANDLE process; | ||
826 | |||
827 | if (!(process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD | | ||
828 | PROCESS_QUERY_INFORMATION | | ||
829 | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | | ||
830 | PROCESS_VM_READ, FALSE, pid))) { | ||
831 | return -1; | ||
832 | } | ||
833 | |||
834 | return kill_signal_by_handle(process, sig); | ||
835 | } | ||
836 | |||
837 | /* | ||
838 | * This way of terminating processes is not gentle: they get no chance to | ||
839 | * clean up after themselves (closing file handles, removing .lock files, | ||
840 | * terminating spawned processes (if any), etc). | ||
841 | * | ||
842 | * If the signal isn't SIGKILL just check if the target process exists. | ||
843 | */ | ||
844 | static int kill_SIGKILL(pid_t pid, int sig) | ||
845 | { | ||
846 | HANDLE process; | ||
847 | int ret = 0; | ||
848 | |||
849 | if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) { | ||
850 | return -1; | ||
851 | } | ||
852 | |||
853 | if (sig == SIGKILL) | ||
854 | ret = !TerminateProcess(process, SIGKILL << 24); | ||
855 | CloseHandle(process); | ||
856 | |||
857 | return ret; | ||
858 | } | ||
859 | |||
860 | int FAST_FUNC is_valid_signal(int number) | 846 | int FAST_FUNC is_valid_signal(int number) |
861 | { | 847 | { |
862 | return isalpha(*get_signame(number)); | 848 | return isalpha(*get_signame(number)); |
@@ -897,10 +883,8 @@ int exit_code_to_posix(DWORD exit_code) | |||
897 | 883 | ||
898 | int kill(pid_t pid, int sig) | 884 | int kill(pid_t pid, int sig) |
899 | { | 885 | { |
900 | if (sig == SIGKILL || sig == 0) | 886 | if (is_valid_signal(sig)) |
901 | return kill_pids(pid, sig, kill_SIGKILL); | 887 | return kill_pids(pid, sig); |
902 | else if (is_valid_signal(sig)) | ||
903 | return kill_pids(pid, sig, kill_signal); | ||
904 | 888 | ||
905 | errno = EINVAL; | 889 | errno = EINVAL; |
906 | return -1; | 890 | return -1; |