# Copyright 1999-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id$ from portage import os from _emerge.AbstractPollTask import AbstractPollTask import signal import errno class SubProcess(AbstractPollTask): __slots__ = ("pid",) + \ ("_files", "_reg_id") # A file descriptor is required for the scheduler to monitor changes from # inside a poll() loop. When logging is not enabled, create a pipe just to # serve this purpose alone. _dummy_pipe_fd = 9 def _poll(self): if self.returncode is not None: return self.returncode if self.pid is None: return self.returncode if self._registered: return self.returncode try: retval = os.waitpid(self.pid, os.WNOHANG) except OSError as e: if e.errno != errno.ECHILD: raise del e retval = (self.pid, 1) if retval == (0, 0): return None self._set_returncode(retval) return self.returncode def cancel(self): if self.isAlive(): try: os.kill(self.pid, signal.SIGTERM) except OSError as e: if e.errno != errno.ESRCH: raise del e self.cancelled = True if self.pid is not None: self.wait() return self.returncode def isAlive(self): return self.pid is not None and \ self.returncode is None def _wait(self): if self.returncode is not None: return self.returncode if self._registered: self.scheduler.schedule(self._reg_id) self._unregister() if self.returncode is not None: return self.returncode try: wait_retval = os.waitpid(self.pid, 0) except OSError as e: if e.errno != errno.ECHILD: raise del e self._set_returncode((self.pid, 1)) else: self._set_returncode(wait_retval) return self.returncode def _unregister(self): """ Unregister from the scheduler and close open files. """ self._registered = False if self._reg_id is not None: self.scheduler.unregister(self._reg_id) self._reg_id = None if self._files is not None: for f in self._files.values(): f.close() self._files = None def _set_returncode(self, wait_retval): retval = wait_retval[1] if retval != os.EX_OK: if retval & 0xff: retval = (retval & 0xff) << 8 else: retval = retval >> 8 self.returncode = retval