Package _emerge :: Module SubProcess
[hide private]

Source Code for Module _emerge.SubProcess

  1  # Copyright 1999-2013 Gentoo Foundation 
  2  # Distributed under the terms of the GNU General Public License v2 
  3   
  4  import logging 
  5   
  6  from portage import os 
  7  from portage.util import writemsg_level 
  8  from _emerge.AbstractPollTask import AbstractPollTask 
  9  import signal 
 10  import errno 
 11   
12 -class SubProcess(AbstractPollTask):
13 14 __slots__ = ("pid",) + \ 15 ("_dummy_pipe_fd", "_files", "_reg_id") 16 17 # This is how much time we allow for waitpid to succeed after 18 # we've sent a kill signal to our subprocess. 19 _cancel_timeout = 1000 # 1 second 20
21 - def _poll(self):
22 if self.returncode is not None: 23 return self.returncode 24 if self.pid is None: 25 return self.returncode 26 if self._registered: 27 return self.returncode 28 29 try: 30 # With waitpid and WNOHANG, only check the 31 # first element of the tuple since the second 32 # element may vary (bug #337465). 33 retval = os.waitpid(self.pid, os.WNOHANG) 34 except OSError as e: 35 if e.errno != errno.ECHILD: 36 raise 37 del e 38 retval = (self.pid, 1) 39 40 if retval[0] == 0: 41 return None 42 self._set_returncode(retval) 43 self.wait() 44 return self.returncode
45
46 - def _cancel(self):
47 if self.isAlive(): 48 try: 49 os.kill(self.pid, signal.SIGTERM) 50 except OSError as e: 51 if e.errno == errno.EPERM: 52 # Reported with hardened kernel (bug #358211). 53 writemsg_level( 54 "!!! kill: (%i) - Operation not permitted\n" % 55 (self.pid,), level=logging.ERROR, 56 noiselevel=-1) 57 elif e.errno != errno.ESRCH: 58 raise
59
60 - def isAlive(self):
61 return self.pid is not None and \ 62 self.returncode is None
63
64 - def _wait(self):
65 66 if self.returncode is not None: 67 return self.returncode 68 69 if self._registered: 70 if self.cancelled: 71 self._wait_loop(timeout=self._cancel_timeout) 72 if self._registered: 73 try: 74 os.kill(self.pid, signal.SIGKILL) 75 except OSError as e: 76 if e.errno == errno.EPERM: 77 # Reported with hardened kernel (bug #358211). 78 writemsg_level( 79 "!!! kill: (%i) - Operation not permitted\n" % 80 (self.pid,), level=logging.ERROR, 81 noiselevel=-1) 82 elif e.errno != errno.ESRCH: 83 raise 84 del e 85 self._wait_loop(timeout=self._cancel_timeout) 86 if self._registered: 87 self._orphan_process_warn() 88 else: 89 self._wait_loop() 90 91 if self.returncode is not None: 92 return self.returncode 93 94 if not isinstance(self.pid, int): 95 # Get debug info for bug #403697. 96 raise AssertionError( 97 "%s: pid is non-integer: %s" % 98 (self.__class__.__name__, repr(self.pid))) 99 100 self._waitpid_loop() 101 102 return self.returncode
103
104 - def _waitpid_loop(self):
105 source_id = self.scheduler.child_watch_add( 106 self.pid, self._waitpid_cb) 107 try: 108 while self.returncode is None: 109 self.scheduler.iteration() 110 finally: 111 self.scheduler.source_remove(source_id)
112
113 - def _waitpid_cb(self, pid, condition, user_data=None):
114 if pid != self.pid: 115 raise AssertionError("expected pid %s, got %s" % (self.pid, pid)) 116 self._set_returncode((pid, condition))
117
118 - def _orphan_process_warn(self):
119 pass
120
121 - def _unregister(self):
122 """ 123 Unregister from the scheduler and close open files. 124 """ 125 126 self._registered = False 127 128 if self._reg_id is not None: 129 self.scheduler.source_remove(self._reg_id) 130 self._reg_id = None 131 132 if self._files is not None: 133 for f in self._files.values(): 134 if isinstance(f, int): 135 os.close(f) 136 else: 137 f.close() 138 self._files = None
139
140 - def _set_returncode(self, wait_retval):
141 """ 142 Set the returncode in a manner compatible with 143 subprocess.Popen.returncode: A negative value -N indicates 144 that the child was terminated by signal N (Unix only). 145 """ 146 self._unregister() 147 148 pid, status = wait_retval 149 150 if os.WIFSIGNALED(status): 151 retval = - os.WTERMSIG(status) 152 else: 153 retval = os.WEXITSTATUS(status) 154 155 self.returncode = retval
156