diff --git a/honcho/environ.py b/honcho/environ.py index e9826b4..7b1c97b 100644 --- a/honcho/environ.py +++ b/honcho/environ.py @@ -5,6 +5,7 @@ import shlex import os import re +import signal from . import compat @@ -21,17 +22,29 @@ def now(self): return datetime.datetime.now() if compat.ON_WINDOWS: - # Shamelessly cribbed from - # https://docs.python.org/2/faq/windows.html#how-do-i-emulate-os-kill-in-windows - def killpg(self, pid, signum=None): - """kill function for Win32""" - kernel32 = ctypes.windll.kernel32 - handle = kernel32.OpenProcess(1, 0, pid) - return (0 != kernel32.TerminateProcess(handle, 0)) + def terminate(self, pid): + PROCESS_TERMINATE = 1 + handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, + False, + pid) + ctypes.windll.kernel32.TerminateProcess(handle, -1) + ctypes.windll.kernel32.CloseHandle(handle) else: - def killpg(self, pid, signum=None): + def terminate(self, pid): try: - os.killpg(pid, signum) + os.killpg(pid, signal.SIGTERM) + except OSError as e: + if e.errno not in [errno.EPERM, errno.ESRCH]: + raise + + if compat.ON_WINDOWS: + def kill(self, pid): + # There's no SIGKILL on Win32... + self.terminate(pid) + else: + def kill(self, pid): + try: + os.killpg(pid, signal.SIGKILL) except OSError as e: if e.errno not in [errno.EPERM, errno.ESRCH]: raise diff --git a/honcho/manager.py b/honcho/manager.py index f42836a..218f2b4 100644 --- a/honcho/manager.py +++ b/honcho/manager.py @@ -20,9 +20,6 @@ 'name': 'SIGTERM', 'rc': 143, }, - signal.SIGKILL: { - 'name': 'SIGKILL', - } } SYSTEM_PRINTER_NAME = 'system' @@ -127,7 +124,7 @@ def _terminate(signum, frame): if exit_start is not None: # If we've been in this loop for more than KILL_WAIT seconds, - # it's time to SIGKILL all remaining children. + # it's time to kill all remaining children. waiting = self._env.now() - exit_start if waiting > datetime.timedelta(seconds=KILL_WAIT): self.kill() @@ -139,20 +136,16 @@ def terminate(self): if self._terminating: return self._terminating = True - self._killall(signal.SIGTERM) + self._killall() def kill(self): """ Kill all processes managed by this ProcessManager. """ - self._killall(signal.SIGKILL) + self._killall(force=True) - def _kill(self, pid, signum=None): - """Kill a single process ID""" - self._env.killpg(pid, signum) - - def _killall(self, signum): - """Kill all remaining processes with the specified signal""" + def _killall(self, force=False): + """Kill all remaining processes, forcefully if requested.""" for_termination = [] for n, p in iteritems(self._processes): @@ -161,9 +154,13 @@ def _killall(self, signum): for n in for_termination: p = self._processes[n] + signame = 'SIGKILL' if force else 'SIGTERM' self._system_print("sending %s to %s (pid %s)\n" % - (SIGNALS[signum]['name'], n, p['pid'])) - self._kill(p['pid'], signum) + (signame, n, p['pid'])) + if force: + self._env.kill(p['pid']) + else: + self._env.terminate(p['pid']) def _start(self): for name, p in self._processes.items(): diff --git a/honcho/test/unit/test_manager.py b/honcho/test/unit/test_manager.py index d5cd428..0ffc133 100644 --- a/honcho/test/unit/test_manager.py +++ b/honcho/test/unit/test_manager.py @@ -49,7 +49,10 @@ class FakeEnv(object): def now(self): return datetime.datetime(2012, 8, 11, 12, 42) - def killpg(self, pid, sig=None): + def terminate(self, pid): + pass + + def kill(self, pid): pass