1 # Copyright 1999-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
5 from _emerge.AbstractPollTask import AbstractPollTask
9 class SubProcess(AbstractPollTask):
11 __slots__ = ("pid",) + \
14 # A file descriptor is required for the scheduler to monitor changes from
15 # inside a poll() loop. When logging is not enabled, create a pipe just to
16 # serve this purpose alone.
19 # This is how much time we allow for waitpid to succeed after
20 # we've sent a kill signal to our subprocess.
21 _cancel_timeout = 1000 # 1 second
24 if self.returncode is not None:
25 return self.returncode
27 return self.returncode
29 return self.returncode
32 # With waitpid and WNOHANG, only check the
33 # first element of the tuple since the second
34 # element may vary (bug #337465).
35 retval = os.waitpid(self.pid, os.WNOHANG)
37 if e.errno != errno.ECHILD:
40 retval = (self.pid, 1)
44 self._set_returncode(retval)
46 return self.returncode
51 os.kill(self.pid, signal.SIGTERM)
53 if e.errno != errno.ESRCH:
57 return self.pid is not None and \
58 self.returncode is None
62 if self.returncode is not None:
63 return self.returncode
67 timeout = self._cancel_timeout
68 self.scheduler.schedule(self._reg_id, timeout=timeout)
71 os.kill(self.pid, signal.SIGKILL)
73 if e.errno != errno.ESRCH:
76 self.scheduler.schedule(self._reg_id, timeout=timeout)
78 self._orphan_process_warn()
80 while self._registered:
81 self.scheduler.iteration()
83 if self.returncode is not None:
84 return self.returncode
87 # With waitpid and WNOHANG, only check the
88 # first element of the tuple since the second
89 # element may vary (bug #337465).
90 wait_retval = os.waitpid(self.pid, os.WNOHANG)
92 if e.errno != errno.ECHILD:
95 self._set_returncode((self.pid, 1 << 8))
97 if wait_retval[0] != 0:
98 self._set_returncode(wait_retval)
101 wait_retval = os.waitpid(self.pid, 0)
103 if e.errno != errno.ECHILD:
106 self._set_returncode((self.pid, 1 << 8))
108 self._set_returncode(wait_retval)
110 return self.returncode
112 def _orphan_process_warn(self):
115 def _unregister(self):
117 Unregister from the scheduler and close open files.
120 self._registered = False
122 if self._reg_id is not None:
123 self.scheduler.unregister(self._reg_id)
126 if self._files is not None:
127 for f in self._files.values():
128 if isinstance(f, int):
134 def _set_returncode(self, wait_retval):
136 Set the returncode in a manner compatible with
137 subprocess.Popen.returncode: A negative value -N indicates
138 that the child was terminated by signal N (Unix only).
141 pid, status = wait_retval
143 if os.WIFSIGNALED(status):
144 retval = - os.WTERMSIG(status)
146 retval = os.WEXITSTATUS(status)
148 self.returncode = retval