From: LRN Date: Thu, 12 Jul 2012 21:06:27 +0000 (+0000) Subject: W32: safer process termination X-Git-Tag: initial-import-from-subversion-38251~12527 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=1ddd74011af93e1e166d9de48542b0a23f1e1405;p=oweals%2Fgnunet.git W32: safer process termination --- diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 0d6539330..aaab287a8 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -13,6 +13,7 @@ timeout_watchdog_SOURCES = \ endif noinst_SCRIPTS = \ + terminate.py \ gnunet_pyexpect.py \ gnunet_janitor.py @@ -28,6 +29,7 @@ EXTRA_DIST = \ hostlist.cgi \ hostlist.php \ report.sh \ + terminate.py.in \ gnunet_pyexpect.py.in \ gnunet_janitor.py.in \ gnunet-gns-import.sh diff --git a/contrib/gnunet_janitor.py.in b/contrib/gnunet_janitor.py.in index 056ab9bc4..69186fde6 100644 --- a/contrib/gnunet_janitor.py.in +++ b/contrib/gnunet_janitor.py.in @@ -30,13 +30,11 @@ import sys import shutil import time import signal +import terminate if os.name == 'nt': from win32com.client import GetObject WMI = GetObject('winmgmts:') - killsignal = signal.SIGTERM # any valid value will result in TerminateProcess() -else: - killsignal = signal.SIGKILL def get_process_list (): result = [] @@ -63,7 +61,7 @@ def main (): if re.match (r'gnunet-service-arm', p[1]): print ("killing arm process {0:5} {1}".format (p[0], p[1])) try: - os.kill (int (p[0]), killsignal) + terminate.safe_terminate_process_by_pid (int (p[0]), 1) except OSError as e: print ("failed: {0}".format (e)) pass @@ -71,7 +69,7 @@ def main (): if not re.match (r'gnunet-service-arm', p[1]): print ("killing non-arm process {0:5} {1}".format (p[0], p[1])) try: - os.kill (int (p[0]), killsignal) + terminate.safe_terminate_process_by_pid (int (p[0]), 1) except OSError as e: print ("failed: {0}".format (e)) pass diff --git a/src/include/winproc.h b/src/include/winproc.h index 3670a74b0..6cbe56248 100644 --- a/src/include/winproc.h +++ b/src/include/winproc.h @@ -227,6 +227,7 @@ extern "C" int GNInitWinEnv (); void GNShutdownWinEnv (); + BOOL SafeTerminateProcess (HANDLE hProcess, UINT uExitCode, DWORD dwTimeout); #ifdef __cplusplus } #endif diff --git a/src/util/os_priority.c b/src/util/os_priority.c index 173d8814a..b0668e20d 100644 --- a/src/util/os_priority.c +++ b/src/util/os_priority.c @@ -461,10 +461,16 @@ GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig) if (0 != GetExitCodeProcess (proc->handle, &exitcode)) must_kill = (exitcode == STILL_ACTIVE) ? GNUNET_YES : GNUNET_NO; if (GNUNET_YES == must_kill) - if (0 == TerminateProcess (proc->handle, 0)) + if (0 == SafeTerminateProcess (proc->handle, 0, 0)) { - SetErrnoFromWinError (GetLastError ()); - return -1; + DWORD error_code = GetLastError (); + if (error_code != WAIT_TIMEOUT) /* OK, since timeout is 0 */ + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "SafeTermiateProcess failed with code %lu\n", error_code); + SetErrnoFromWinError (error_code); + return -1; + } } } return 0; @@ -1386,7 +1392,7 @@ start_process (int pipe_control, /* If we can't pass on the socket(s), the child will block forever, * better put it out of its misery. */ - TerminateProcess (gnunet_proc->handle, 0); + SafeTerminateProcess (gnunet_proc->handle, 0, 0); CloseHandle (gnunet_proc->handle); if (NULL != gnunet_proc->control_pipe) GNUNET_DISK_file_close (gnunet_proc->control_pipe); diff --git a/src/util/win.cc b/src/util/win.cc index 1f660720e..7b67c9e83 100644 --- a/src/util/win.cc +++ b/src/util/win.cc @@ -1261,6 +1261,75 @@ char *winErrorStr(const char *prefix, int dwErr) return ret; } +/** + * Terminate a process by creating a remote thread within it, + * which proceeds to call ExitProcess() inside that process. + * Safer than TerminateProcess (). + * + * Code is from From http://private-storm.de/2009/08/11/case-terminateprocess/ + * + * @param hProcess handle of a process to terminate + * @param uExitCode exit code to use for ExitProcess() + * @param dwTimeout number of ms to wait for the process to terminate + * @return TRUE on success, FALSE on failure (check last error for the code) + */ +BOOL +SafeTerminateProcess (HANDLE hProcess, UINT uExitCode, DWORD dwTimeout) +{ + DWORD dwTID, dwCode, dwErr = 0; + HANDLE hProcessDup = INVALID_HANDLE_VALUE; + HANDLE hRT = NULL; + HINSTANCE hKernel = GetModuleHandle ("Kernel32"); + BOOL bSuccess = FALSE; + + BOOL bDup = DuplicateHandle (GetCurrentProcess (), hProcess, + GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS, + FALSE, 0); + + /* Detect the special case where the process is + * already dead... + */ + if (GetExitCodeProcess (bDup ? hProcessDup : hProcess, &dwCode) && + (STILL_ACTIVE == dwCode)) + { + FARPROC pfnExitProc; + + pfnExitProc = GetProcAddress (hKernel, "ExitProcess"); + + hRT = CreateRemoteThread ((bDup) ? hProcessDup : hProcess, NULL, 0, + (LPTHREAD_START_ROUTINE) pfnExitProc, (PVOID) uExitCode, 0, &dwTID); + + dwErr = GetLastError (); + } + else + { + dwErr = ERROR_PROCESS_ABORTED; + } + + if (hRT) + { + /* Must wait process to terminate to + * guarantee that it has exited... + */ + DWORD dwWaitResult = WaitForSingleObject ((bDup) ? hProcessDup : hProcess, + dwTimeout); + if (dwWaitResult == WAIT_TIMEOUT) + dwErr = WAIT_TIMEOUT; + else + dwErr = GetLastError (); + + CloseHandle (hRT); + bSuccess = dwErr == NO_ERROR; + } + + if (bDup) + CloseHandle (hProcessDup); + + SetLastError (dwErr); + + return bSuccess; +} + } /* extern "C" */ #endif