1*d68f33bcSAndroid Build Coastguard Worker# Copyright 2016 The Android Open Source Project 2*d68f33bcSAndroid Build Coastguard Worker# 3*d68f33bcSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*d68f33bcSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*d68f33bcSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*d68f33bcSAndroid Build Coastguard Worker# 7*d68f33bcSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*d68f33bcSAndroid Build Coastguard Worker# 9*d68f33bcSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*d68f33bcSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*d68f33bcSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*d68f33bcSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*d68f33bcSAndroid Build Coastguard Worker# limitations under the License. 14*d68f33bcSAndroid Build Coastguard Worker 15*d68f33bcSAndroid Build Coastguard Worker"""Various utility functions.""" 16*d68f33bcSAndroid Build Coastguard Worker 17*d68f33bcSAndroid Build Coastguard Workerimport errno 18*d68f33bcSAndroid Build Coastguard Workerimport functools 19*d68f33bcSAndroid Build Coastguard Workerimport os 20*d68f33bcSAndroid Build Coastguard Workerimport signal 21*d68f33bcSAndroid Build Coastguard Workerimport subprocess 22*d68f33bcSAndroid Build Coastguard Workerimport sys 23*d68f33bcSAndroid Build Coastguard Workerimport tempfile 24*d68f33bcSAndroid Build Coastguard Workerimport time 25*d68f33bcSAndroid Build Coastguard Worker 26*d68f33bcSAndroid Build Coastguard Worker_path = os.path.realpath(__file__ + '/../..') 27*d68f33bcSAndroid Build Coastguard Workerif sys.path[0] != _path: 28*d68f33bcSAndroid Build Coastguard Worker sys.path.insert(0, _path) 29*d68f33bcSAndroid Build Coastguard Workerdel _path 30*d68f33bcSAndroid Build Coastguard Worker 31*d68f33bcSAndroid Build Coastguard Worker# pylint: disable=wrong-import-position 32*d68f33bcSAndroid Build Coastguard Workerimport rh.shell 33*d68f33bcSAndroid Build Coastguard Workerimport rh.signals 34*d68f33bcSAndroid Build Coastguard Worker 35*d68f33bcSAndroid Build Coastguard Worker 36*d68f33bcSAndroid Build Coastguard Workerdef timedelta_str(delta): 37*d68f33bcSAndroid Build Coastguard Worker """A less noisy timedelta.__str__. 38*d68f33bcSAndroid Build Coastguard Worker 39*d68f33bcSAndroid Build Coastguard Worker The default timedelta stringification contains a lot of leading zeros and 40*d68f33bcSAndroid Build Coastguard Worker uses microsecond resolution. This makes for noisy output. 41*d68f33bcSAndroid Build Coastguard Worker """ 42*d68f33bcSAndroid Build Coastguard Worker total = delta.total_seconds() 43*d68f33bcSAndroid Build Coastguard Worker hours, rem = divmod(total, 3600) 44*d68f33bcSAndroid Build Coastguard Worker mins, secs = divmod(rem, 60) 45*d68f33bcSAndroid Build Coastguard Worker ret = f'{int(secs)}.{delta.microseconds // 1000:03}s' 46*d68f33bcSAndroid Build Coastguard Worker if mins: 47*d68f33bcSAndroid Build Coastguard Worker ret = f'{int(mins)}m{ret}' 48*d68f33bcSAndroid Build Coastguard Worker if hours: 49*d68f33bcSAndroid Build Coastguard Worker ret = f'{int(hours)}h{ret}' 50*d68f33bcSAndroid Build Coastguard Worker return ret 51*d68f33bcSAndroid Build Coastguard Worker 52*d68f33bcSAndroid Build Coastguard Worker 53*d68f33bcSAndroid Build Coastguard Workerclass CompletedProcess(subprocess.CompletedProcess): 54*d68f33bcSAndroid Build Coastguard Worker """An object to store various attributes of a child process. 55*d68f33bcSAndroid Build Coastguard Worker 56*d68f33bcSAndroid Build Coastguard Worker This is akin to subprocess.CompletedProcess. 57*d68f33bcSAndroid Build Coastguard Worker """ 58*d68f33bcSAndroid Build Coastguard Worker 59*d68f33bcSAndroid Build Coastguard Worker def __init__(self, args=None, returncode=None, stdout=None, stderr=None): 60*d68f33bcSAndroid Build Coastguard Worker super().__init__( 61*d68f33bcSAndroid Build Coastguard Worker args=args, returncode=returncode, stdout=stdout, stderr=stderr) 62*d68f33bcSAndroid Build Coastguard Worker 63*d68f33bcSAndroid Build Coastguard Worker @property 64*d68f33bcSAndroid Build Coastguard Worker def cmd(self): 65*d68f33bcSAndroid Build Coastguard Worker """Alias to self.args to better match other subprocess APIs.""" 66*d68f33bcSAndroid Build Coastguard Worker return self.args 67*d68f33bcSAndroid Build Coastguard Worker 68*d68f33bcSAndroid Build Coastguard Worker @property 69*d68f33bcSAndroid Build Coastguard Worker def cmdstr(self): 70*d68f33bcSAndroid Build Coastguard Worker """Return self.cmd as a nicely formatted string (useful for logs).""" 71*d68f33bcSAndroid Build Coastguard Worker return rh.shell.cmd_to_str(self.cmd) 72*d68f33bcSAndroid Build Coastguard Worker 73*d68f33bcSAndroid Build Coastguard Worker 74*d68f33bcSAndroid Build Coastguard Workerclass CalledProcessError(subprocess.CalledProcessError): 75*d68f33bcSAndroid Build Coastguard Worker """Error caught in run() function. 76*d68f33bcSAndroid Build Coastguard Worker 77*d68f33bcSAndroid Build Coastguard Worker This is akin to subprocess.CalledProcessError. We do not support |output|, 78*d68f33bcSAndroid Build Coastguard Worker only |stdout|. 79*d68f33bcSAndroid Build Coastguard Worker 80*d68f33bcSAndroid Build Coastguard Worker Attributes: 81*d68f33bcSAndroid Build Coastguard Worker returncode: The exit code of the process. 82*d68f33bcSAndroid Build Coastguard Worker cmd: The command that triggered this exception. 83*d68f33bcSAndroid Build Coastguard Worker msg: Short explanation of the error. 84*d68f33bcSAndroid Build Coastguard Worker """ 85*d68f33bcSAndroid Build Coastguard Worker 86*d68f33bcSAndroid Build Coastguard Worker def __init__(self, returncode, cmd, stdout=None, stderr=None, msg=None): 87*d68f33bcSAndroid Build Coastguard Worker super().__init__(returncode, cmd, stdout, stderr=stderr) 88*d68f33bcSAndroid Build Coastguard Worker 89*d68f33bcSAndroid Build Coastguard Worker # The parent class will set |output|, so delete it. If Python ever drops 90*d68f33bcSAndroid Build Coastguard Worker # this output/stdout compat logic, we can drop this to match. 91*d68f33bcSAndroid Build Coastguard Worker del self.output 92*d68f33bcSAndroid Build Coastguard Worker self._stdout = stdout 93*d68f33bcSAndroid Build Coastguard Worker 94*d68f33bcSAndroid Build Coastguard Worker self.msg = msg 95*d68f33bcSAndroid Build Coastguard Worker 96*d68f33bcSAndroid Build Coastguard Worker @property 97*d68f33bcSAndroid Build Coastguard Worker def stdout(self): 98*d68f33bcSAndroid Build Coastguard Worker """Override parent's usage of .output""" 99*d68f33bcSAndroid Build Coastguard Worker return self._stdout 100*d68f33bcSAndroid Build Coastguard Worker 101*d68f33bcSAndroid Build Coastguard Worker @stdout.setter 102*d68f33bcSAndroid Build Coastguard Worker def stdout(self, value): 103*d68f33bcSAndroid Build Coastguard Worker """Override parent's usage of .output""" 104*d68f33bcSAndroid Build Coastguard Worker self._stdout = value 105*d68f33bcSAndroid Build Coastguard Worker 106*d68f33bcSAndroid Build Coastguard Worker @property 107*d68f33bcSAndroid Build Coastguard Worker def cmdstr(self): 108*d68f33bcSAndroid Build Coastguard Worker """Return self.cmd as a well shell-quoted string for debugging.""" 109*d68f33bcSAndroid Build Coastguard Worker return '' if self.cmd is None else rh.shell.cmd_to_str(self.cmd) 110*d68f33bcSAndroid Build Coastguard Worker 111*d68f33bcSAndroid Build Coastguard Worker def stringify(self, stdout=True, stderr=True): 112*d68f33bcSAndroid Build Coastguard Worker """Custom method for controlling what is included in stringifying this. 113*d68f33bcSAndroid Build Coastguard Worker 114*d68f33bcSAndroid Build Coastguard Worker Args: 115*d68f33bcSAndroid Build Coastguard Worker stdout: Whether to include captured stdout in the return value. 116*d68f33bcSAndroid Build Coastguard Worker stderr: Whether to include captured stderr in the return value. 117*d68f33bcSAndroid Build Coastguard Worker 118*d68f33bcSAndroid Build Coastguard Worker Returns: 119*d68f33bcSAndroid Build Coastguard Worker A summary string for this result. 120*d68f33bcSAndroid Build Coastguard Worker """ 121*d68f33bcSAndroid Build Coastguard Worker items = [ 122*d68f33bcSAndroid Build Coastguard Worker f'return code: {self.returncode}; command: {self.cmdstr}', 123*d68f33bcSAndroid Build Coastguard Worker ] 124*d68f33bcSAndroid Build Coastguard Worker if stderr and self.stderr: 125*d68f33bcSAndroid Build Coastguard Worker items.append(self.stderr) 126*d68f33bcSAndroid Build Coastguard Worker if stdout and self.stdout: 127*d68f33bcSAndroid Build Coastguard Worker items.append(self.stdout) 128*d68f33bcSAndroid Build Coastguard Worker if self.msg: 129*d68f33bcSAndroid Build Coastguard Worker items.append(self.msg) 130*d68f33bcSAndroid Build Coastguard Worker return '\n'.join(items) 131*d68f33bcSAndroid Build Coastguard Worker 132*d68f33bcSAndroid Build Coastguard Worker def __str__(self): 133*d68f33bcSAndroid Build Coastguard Worker return self.stringify() 134*d68f33bcSAndroid Build Coastguard Worker 135*d68f33bcSAndroid Build Coastguard Worker 136*d68f33bcSAndroid Build Coastguard Workerclass TerminateCalledProcessError(CalledProcessError): 137*d68f33bcSAndroid Build Coastguard Worker """We were signaled to shutdown while running a command. 138*d68f33bcSAndroid Build Coastguard Worker 139*d68f33bcSAndroid Build Coastguard Worker Client code shouldn't generally know, nor care about this class. It's 140*d68f33bcSAndroid Build Coastguard Worker used internally to suppress retry attempts when we're signaled to die. 141*d68f33bcSAndroid Build Coastguard Worker """ 142*d68f33bcSAndroid Build Coastguard Worker 143*d68f33bcSAndroid Build Coastguard Worker 144*d68f33bcSAndroid Build Coastguard Workerdef _kill_child_process(proc, int_timeout, kill_timeout, cmd, original_handler, 145*d68f33bcSAndroid Build Coastguard Worker signum, frame): 146*d68f33bcSAndroid Build Coastguard Worker """Used as a signal handler by RunCommand. 147*d68f33bcSAndroid Build Coastguard Worker 148*d68f33bcSAndroid Build Coastguard Worker This is internal to Runcommand. No other code should use this. 149*d68f33bcSAndroid Build Coastguard Worker """ 150*d68f33bcSAndroid Build Coastguard Worker if signum: 151*d68f33bcSAndroid Build Coastguard Worker # If we've been invoked because of a signal, ignore delivery of that 152*d68f33bcSAndroid Build Coastguard Worker # signal from this point forward. The invoking context of this func 153*d68f33bcSAndroid Build Coastguard Worker # restores signal delivery to what it was prior; we suppress future 154*d68f33bcSAndroid Build Coastguard Worker # delivery till then since this code handles SIGINT/SIGTERM fully 155*d68f33bcSAndroid Build Coastguard Worker # including delivering the signal to the original handler on the way 156*d68f33bcSAndroid Build Coastguard Worker # out. 157*d68f33bcSAndroid Build Coastguard Worker signal.signal(signum, signal.SIG_IGN) 158*d68f33bcSAndroid Build Coastguard Worker 159*d68f33bcSAndroid Build Coastguard Worker # Do not trust Popen's returncode alone; we can be invoked from contexts 160*d68f33bcSAndroid Build Coastguard Worker # where the Popen instance was created, but no process was generated. 161*d68f33bcSAndroid Build Coastguard Worker if proc.returncode is None and proc.pid is not None: 162*d68f33bcSAndroid Build Coastguard Worker try: 163*d68f33bcSAndroid Build Coastguard Worker while proc.poll_lock_breaker() is None and int_timeout >= 0: 164*d68f33bcSAndroid Build Coastguard Worker time.sleep(0.1) 165*d68f33bcSAndroid Build Coastguard Worker int_timeout -= 0.1 166*d68f33bcSAndroid Build Coastguard Worker 167*d68f33bcSAndroid Build Coastguard Worker proc.terminate() 168*d68f33bcSAndroid Build Coastguard Worker while proc.poll_lock_breaker() is None and kill_timeout >= 0: 169*d68f33bcSAndroid Build Coastguard Worker time.sleep(0.1) 170*d68f33bcSAndroid Build Coastguard Worker kill_timeout -= 0.1 171*d68f33bcSAndroid Build Coastguard Worker 172*d68f33bcSAndroid Build Coastguard Worker if proc.poll_lock_breaker() is None: 173*d68f33bcSAndroid Build Coastguard Worker # Still doesn't want to die. Too bad, so sad, time to die. 174*d68f33bcSAndroid Build Coastguard Worker proc.kill() 175*d68f33bcSAndroid Build Coastguard Worker except EnvironmentError as e: 176*d68f33bcSAndroid Build Coastguard Worker print(f'Ignoring unhandled exception in _kill_child_process: {e}', 177*d68f33bcSAndroid Build Coastguard Worker file=sys.stderr) 178*d68f33bcSAndroid Build Coastguard Worker 179*d68f33bcSAndroid Build Coastguard Worker # Ensure our child process has been reaped, but don't wait forever. 180*d68f33bcSAndroid Build Coastguard Worker proc.wait_lock_breaker(timeout=60) 181*d68f33bcSAndroid Build Coastguard Worker 182*d68f33bcSAndroid Build Coastguard Worker if not rh.signals.relay_signal(original_handler, signum, frame): 183*d68f33bcSAndroid Build Coastguard Worker # Mock up our own, matching exit code for signaling. 184*d68f33bcSAndroid Build Coastguard Worker raise TerminateCalledProcessError( 185*d68f33bcSAndroid Build Coastguard Worker signum << 8, cmd, msg=f'Received signal {signum}') 186*d68f33bcSAndroid Build Coastguard Worker 187*d68f33bcSAndroid Build Coastguard Worker 188*d68f33bcSAndroid Build Coastguard Workerclass _Popen(subprocess.Popen): 189*d68f33bcSAndroid Build Coastguard Worker """subprocess.Popen derivative customized for our usage. 190*d68f33bcSAndroid Build Coastguard Worker 191*d68f33bcSAndroid Build Coastguard Worker Specifically, we fix terminate/send_signal/kill to work if the child process 192*d68f33bcSAndroid Build Coastguard Worker was a setuid binary; on vanilla kernels, the parent can wax the child 193*d68f33bcSAndroid Build Coastguard Worker regardless, on goobuntu this apparently isn't allowed, thus we fall back 194*d68f33bcSAndroid Build Coastguard Worker to the sudo machinery we have. 195*d68f33bcSAndroid Build Coastguard Worker 196*d68f33bcSAndroid Build Coastguard Worker While we're overriding send_signal, we also suppress ESRCH being raised 197*d68f33bcSAndroid Build Coastguard Worker if the process has exited, and suppress signaling all together if the 198*d68f33bcSAndroid Build Coastguard Worker process has knowingly been waitpid'd already. 199*d68f33bcSAndroid Build Coastguard Worker """ 200*d68f33bcSAndroid Build Coastguard Worker 201*d68f33bcSAndroid Build Coastguard Worker # pylint: disable=arguments-differ,arguments-renamed 202*d68f33bcSAndroid Build Coastguard Worker def send_signal(self, signum): 203*d68f33bcSAndroid Build Coastguard Worker if self.returncode is not None: 204*d68f33bcSAndroid Build Coastguard Worker # The original implementation in Popen allows signaling whatever 205*d68f33bcSAndroid Build Coastguard Worker # process now occupies this pid, even if the Popen object had 206*d68f33bcSAndroid Build Coastguard Worker # waitpid'd. Since we can escalate to sudo kill, we do not want 207*d68f33bcSAndroid Build Coastguard Worker # to allow that. Fixing this addresses that angle, and makes the 208*d68f33bcSAndroid Build Coastguard Worker # API less sucky in the process. 209*d68f33bcSAndroid Build Coastguard Worker return 210*d68f33bcSAndroid Build Coastguard Worker 211*d68f33bcSAndroid Build Coastguard Worker try: 212*d68f33bcSAndroid Build Coastguard Worker os.kill(self.pid, signum) 213*d68f33bcSAndroid Build Coastguard Worker except EnvironmentError as e: 214*d68f33bcSAndroid Build Coastguard Worker if e.errno == errno.ESRCH: 215*d68f33bcSAndroid Build Coastguard Worker # Since we know the process is dead, reap it now. 216*d68f33bcSAndroid Build Coastguard Worker # Normally Popen would throw this error- we suppress it since 217*d68f33bcSAndroid Build Coastguard Worker # frankly that's a misfeature and we're already overriding 218*d68f33bcSAndroid Build Coastguard Worker # this method. 219*d68f33bcSAndroid Build Coastguard Worker self.poll() 220*d68f33bcSAndroid Build Coastguard Worker else: 221*d68f33bcSAndroid Build Coastguard Worker raise 222*d68f33bcSAndroid Build Coastguard Worker 223*d68f33bcSAndroid Build Coastguard Worker def _lock_breaker(self, func, *args, **kwargs): 224*d68f33bcSAndroid Build Coastguard Worker """Helper to manage the waitpid lock. 225*d68f33bcSAndroid Build Coastguard Worker 226*d68f33bcSAndroid Build Coastguard Worker Workaround https://bugs.python.org/issue25960. 227*d68f33bcSAndroid Build Coastguard Worker """ 228*d68f33bcSAndroid Build Coastguard Worker # If the lock doesn't exist, or is not locked, call the func directly. 229*d68f33bcSAndroid Build Coastguard Worker lock = getattr(self, '_waitpid_lock', None) 230*d68f33bcSAndroid Build Coastguard Worker if lock is not None and lock.locked(): 231*d68f33bcSAndroid Build Coastguard Worker try: 232*d68f33bcSAndroid Build Coastguard Worker lock.release() 233*d68f33bcSAndroid Build Coastguard Worker return func(*args, **kwargs) 234*d68f33bcSAndroid Build Coastguard Worker finally: 235*d68f33bcSAndroid Build Coastguard Worker if not lock.locked(): 236*d68f33bcSAndroid Build Coastguard Worker lock.acquire() 237*d68f33bcSAndroid Build Coastguard Worker else: 238*d68f33bcSAndroid Build Coastguard Worker return func(*args, **kwargs) 239*d68f33bcSAndroid Build Coastguard Worker 240*d68f33bcSAndroid Build Coastguard Worker def poll_lock_breaker(self, *args, **kwargs): 241*d68f33bcSAndroid Build Coastguard Worker """Wrapper around poll() to break locks if needed.""" 242*d68f33bcSAndroid Build Coastguard Worker return self._lock_breaker(self.poll, *args, **kwargs) 243*d68f33bcSAndroid Build Coastguard Worker 244*d68f33bcSAndroid Build Coastguard Worker def wait_lock_breaker(self, *args, **kwargs): 245*d68f33bcSAndroid Build Coastguard Worker """Wrapper around wait() to break locks if needed.""" 246*d68f33bcSAndroid Build Coastguard Worker return self._lock_breaker(self.wait, *args, **kwargs) 247*d68f33bcSAndroid Build Coastguard Worker 248*d68f33bcSAndroid Build Coastguard Worker 249*d68f33bcSAndroid Build Coastguard Worker# We use the keyword arg |input| which trips up pylint checks. 250*d68f33bcSAndroid Build Coastguard Worker# pylint: disable=redefined-builtin 251*d68f33bcSAndroid Build Coastguard Workerdef run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None, 252*d68f33bcSAndroid Build Coastguard Worker shell=False, env=None, extra_env=None, combine_stdout_stderr=False, 253*d68f33bcSAndroid Build Coastguard Worker check=True, int_timeout=1, kill_timeout=1, capture_output=False): 254*d68f33bcSAndroid Build Coastguard Worker """Runs a command. 255*d68f33bcSAndroid Build Coastguard Worker 256*d68f33bcSAndroid Build Coastguard Worker Args: 257*d68f33bcSAndroid Build Coastguard Worker cmd: cmd to run. Should be input to subprocess.Popen. If a string, shell 258*d68f33bcSAndroid Build Coastguard Worker must be true. Otherwise the command must be an array of arguments, 259*d68f33bcSAndroid Build Coastguard Worker and shell must be false. 260*d68f33bcSAndroid Build Coastguard Worker redirect_stdout: Returns the stdout. 261*d68f33bcSAndroid Build Coastguard Worker redirect_stderr: Holds stderr output until input is communicated. 262*d68f33bcSAndroid Build Coastguard Worker cwd: The working directory to run this cmd. 263*d68f33bcSAndroid Build Coastguard Worker input: The data to pipe into this command through stdin. If a file object 264*d68f33bcSAndroid Build Coastguard Worker or file descriptor, stdin will be connected directly to that. 265*d68f33bcSAndroid Build Coastguard Worker shell: Controls whether we add a shell as a command interpreter. See cmd 266*d68f33bcSAndroid Build Coastguard Worker since it has to agree as to the type. 267*d68f33bcSAndroid Build Coastguard Worker env: If non-None, this is the environment for the new process. 268*d68f33bcSAndroid Build Coastguard Worker extra_env: If set, this is added to the environment for the new process. 269*d68f33bcSAndroid Build Coastguard Worker This dictionary is not used to clear any entries though. 270*d68f33bcSAndroid Build Coastguard Worker combine_stdout_stderr: Combines stdout and stderr streams into stdout. 271*d68f33bcSAndroid Build Coastguard Worker check: Whether to raise an exception when command returns a non-zero exit 272*d68f33bcSAndroid Build Coastguard Worker code, or return the CompletedProcess object containing the exit code. 273*d68f33bcSAndroid Build Coastguard Worker Note: will still raise an exception if the cmd file does not exist. 274*d68f33bcSAndroid Build Coastguard Worker int_timeout: If we're interrupted, how long (in seconds) should we give 275*d68f33bcSAndroid Build Coastguard Worker the invoked process to clean up before we send a SIGTERM. 276*d68f33bcSAndroid Build Coastguard Worker kill_timeout: If we're interrupted, how long (in seconds) should we give 277*d68f33bcSAndroid Build Coastguard Worker the invoked process to shutdown from a SIGTERM before we SIGKILL it. 278*d68f33bcSAndroid Build Coastguard Worker capture_output: Set |redirect_stdout| and |redirect_stderr| to True. 279*d68f33bcSAndroid Build Coastguard Worker 280*d68f33bcSAndroid Build Coastguard Worker Returns: 281*d68f33bcSAndroid Build Coastguard Worker A CompletedProcess object. 282*d68f33bcSAndroid Build Coastguard Worker 283*d68f33bcSAndroid Build Coastguard Worker Raises: 284*d68f33bcSAndroid Build Coastguard Worker CalledProcessError: Raises exception on error. 285*d68f33bcSAndroid Build Coastguard Worker """ 286*d68f33bcSAndroid Build Coastguard Worker if capture_output: 287*d68f33bcSAndroid Build Coastguard Worker redirect_stdout, redirect_stderr = True, True 288*d68f33bcSAndroid Build Coastguard Worker 289*d68f33bcSAndroid Build Coastguard Worker # Set default for variables. 290*d68f33bcSAndroid Build Coastguard Worker popen_stdout = None 291*d68f33bcSAndroid Build Coastguard Worker popen_stderr = None 292*d68f33bcSAndroid Build Coastguard Worker stdin = None 293*d68f33bcSAndroid Build Coastguard Worker result = CompletedProcess() 294*d68f33bcSAndroid Build Coastguard Worker 295*d68f33bcSAndroid Build Coastguard Worker # Force the timeout to float; in the process, if it's not convertible, 296*d68f33bcSAndroid Build Coastguard Worker # a self-explanatory exception will be thrown. 297*d68f33bcSAndroid Build Coastguard Worker kill_timeout = float(kill_timeout) 298*d68f33bcSAndroid Build Coastguard Worker 299*d68f33bcSAndroid Build Coastguard Worker def _get_tempfile(): 300*d68f33bcSAndroid Build Coastguard Worker try: 301*d68f33bcSAndroid Build Coastguard Worker return tempfile.TemporaryFile(buffering=0) 302*d68f33bcSAndroid Build Coastguard Worker except EnvironmentError as e: 303*d68f33bcSAndroid Build Coastguard Worker if e.errno != errno.ENOENT: 304*d68f33bcSAndroid Build Coastguard Worker raise 305*d68f33bcSAndroid Build Coastguard Worker # This can occur if we were pointed at a specific location for our 306*d68f33bcSAndroid Build Coastguard Worker # TMP, but that location has since been deleted. Suppress that 307*d68f33bcSAndroid Build Coastguard Worker # issue in this particular case since our usage gurantees deletion, 308*d68f33bcSAndroid Build Coastguard Worker # and since this is primarily triggered during hard cgroups 309*d68f33bcSAndroid Build Coastguard Worker # shutdown. 310*d68f33bcSAndroid Build Coastguard Worker return tempfile.TemporaryFile(dir='/tmp', buffering=0) 311*d68f33bcSAndroid Build Coastguard Worker 312*d68f33bcSAndroid Build Coastguard Worker # Modify defaults based on parameters. 313*d68f33bcSAndroid Build Coastguard Worker # Note that tempfiles must be unbuffered else attempts to read 314*d68f33bcSAndroid Build Coastguard Worker # what a separate process did to that file can result in a bad 315*d68f33bcSAndroid Build Coastguard Worker # view of the file. 316*d68f33bcSAndroid Build Coastguard Worker # The Popen API accepts either an int or a file handle for stdout/stderr. 317*d68f33bcSAndroid Build Coastguard Worker # pylint: disable=redefined-variable-type 318*d68f33bcSAndroid Build Coastguard Worker if redirect_stdout: 319*d68f33bcSAndroid Build Coastguard Worker popen_stdout = _get_tempfile() 320*d68f33bcSAndroid Build Coastguard Worker 321*d68f33bcSAndroid Build Coastguard Worker if combine_stdout_stderr: 322*d68f33bcSAndroid Build Coastguard Worker popen_stderr = subprocess.STDOUT 323*d68f33bcSAndroid Build Coastguard Worker elif redirect_stderr: 324*d68f33bcSAndroid Build Coastguard Worker popen_stderr = _get_tempfile() 325*d68f33bcSAndroid Build Coastguard Worker # pylint: enable=redefined-variable-type 326*d68f33bcSAndroid Build Coastguard Worker 327*d68f33bcSAndroid Build Coastguard Worker # If subprocesses have direct access to stdout or stderr, they can bypass 328*d68f33bcSAndroid Build Coastguard Worker # our buffers, so we need to flush to ensure that output is not interleaved. 329*d68f33bcSAndroid Build Coastguard Worker if popen_stdout is None or popen_stderr is None: 330*d68f33bcSAndroid Build Coastguard Worker sys.stdout.flush() 331*d68f33bcSAndroid Build Coastguard Worker sys.stderr.flush() 332*d68f33bcSAndroid Build Coastguard Worker 333*d68f33bcSAndroid Build Coastguard Worker # If input is a string, we'll create a pipe and send it through that. 334*d68f33bcSAndroid Build Coastguard Worker # Otherwise we assume it's a file object that can be read from directly. 335*d68f33bcSAndroid Build Coastguard Worker if isinstance(input, str): 336*d68f33bcSAndroid Build Coastguard Worker stdin = subprocess.PIPE 337*d68f33bcSAndroid Build Coastguard Worker input = input.encode('utf-8') 338*d68f33bcSAndroid Build Coastguard Worker elif input is not None: 339*d68f33bcSAndroid Build Coastguard Worker stdin = input 340*d68f33bcSAndroid Build Coastguard Worker input = None 341*d68f33bcSAndroid Build Coastguard Worker 342*d68f33bcSAndroid Build Coastguard Worker if isinstance(cmd, str): 343*d68f33bcSAndroid Build Coastguard Worker if not shell: 344*d68f33bcSAndroid Build Coastguard Worker raise Exception('Cannot run a string command without a shell') 345*d68f33bcSAndroid Build Coastguard Worker cmd = ['/bin/bash', '-c', cmd] 346*d68f33bcSAndroid Build Coastguard Worker shell = False 347*d68f33bcSAndroid Build Coastguard Worker elif shell: 348*d68f33bcSAndroid Build Coastguard Worker raise Exception('Cannot run an array command with a shell') 349*d68f33bcSAndroid Build Coastguard Worker 350*d68f33bcSAndroid Build Coastguard Worker # If we are using enter_chroot we need to use enterchroot pass env through 351*d68f33bcSAndroid Build Coastguard Worker # to the final command. 352*d68f33bcSAndroid Build Coastguard Worker env = env.copy() if env is not None else os.environ.copy() 353*d68f33bcSAndroid Build Coastguard Worker env.update(extra_env if extra_env else {}) 354*d68f33bcSAndroid Build Coastguard Worker 355*d68f33bcSAndroid Build Coastguard Worker def ensure_text(s): 356*d68f33bcSAndroid Build Coastguard Worker """Make sure |s| is a string if it's bytes.""" 357*d68f33bcSAndroid Build Coastguard Worker if isinstance(s, bytes): 358*d68f33bcSAndroid Build Coastguard Worker s = s.decode('utf-8', 'replace') 359*d68f33bcSAndroid Build Coastguard Worker return s 360*d68f33bcSAndroid Build Coastguard Worker 361*d68f33bcSAndroid Build Coastguard Worker result.args = cmd 362*d68f33bcSAndroid Build Coastguard Worker 363*d68f33bcSAndroid Build Coastguard Worker proc = None 364*d68f33bcSAndroid Build Coastguard Worker try: 365*d68f33bcSAndroid Build Coastguard Worker proc = _Popen(cmd, cwd=cwd, stdin=stdin, stdout=popen_stdout, 366*d68f33bcSAndroid Build Coastguard Worker stderr=popen_stderr, shell=False, env=env, 367*d68f33bcSAndroid Build Coastguard Worker close_fds=True) 368*d68f33bcSAndroid Build Coastguard Worker 369*d68f33bcSAndroid Build Coastguard Worker old_sigint = signal.getsignal(signal.SIGINT) 370*d68f33bcSAndroid Build Coastguard Worker handler = functools.partial(_kill_child_process, proc, int_timeout, 371*d68f33bcSAndroid Build Coastguard Worker kill_timeout, cmd, old_sigint) 372*d68f33bcSAndroid Build Coastguard Worker # We have to ignore ValueError in case we're run from a thread. 373*d68f33bcSAndroid Build Coastguard Worker try: 374*d68f33bcSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, handler) 375*d68f33bcSAndroid Build Coastguard Worker except ValueError: 376*d68f33bcSAndroid Build Coastguard Worker old_sigint = None 377*d68f33bcSAndroid Build Coastguard Worker 378*d68f33bcSAndroid Build Coastguard Worker old_sigterm = signal.getsignal(signal.SIGTERM) 379*d68f33bcSAndroid Build Coastguard Worker handler = functools.partial(_kill_child_process, proc, int_timeout, 380*d68f33bcSAndroid Build Coastguard Worker kill_timeout, cmd, old_sigterm) 381*d68f33bcSAndroid Build Coastguard Worker try: 382*d68f33bcSAndroid Build Coastguard Worker signal.signal(signal.SIGTERM, handler) 383*d68f33bcSAndroid Build Coastguard Worker except ValueError: 384*d68f33bcSAndroid Build Coastguard Worker old_sigterm = None 385*d68f33bcSAndroid Build Coastguard Worker 386*d68f33bcSAndroid Build Coastguard Worker try: 387*d68f33bcSAndroid Build Coastguard Worker (result.stdout, result.stderr) = proc.communicate(input) 388*d68f33bcSAndroid Build Coastguard Worker finally: 389*d68f33bcSAndroid Build Coastguard Worker if old_sigint is not None: 390*d68f33bcSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, old_sigint) 391*d68f33bcSAndroid Build Coastguard Worker if old_sigterm is not None: 392*d68f33bcSAndroid Build Coastguard Worker signal.signal(signal.SIGTERM, old_sigterm) 393*d68f33bcSAndroid Build Coastguard Worker 394*d68f33bcSAndroid Build Coastguard Worker if popen_stdout: 395*d68f33bcSAndroid Build Coastguard Worker # The linter is confused by how stdout is a file & an int. 396*d68f33bcSAndroid Build Coastguard Worker # pylint: disable=maybe-no-member,no-member 397*d68f33bcSAndroid Build Coastguard Worker popen_stdout.seek(0) 398*d68f33bcSAndroid Build Coastguard Worker result.stdout = popen_stdout.read() 399*d68f33bcSAndroid Build Coastguard Worker popen_stdout.close() 400*d68f33bcSAndroid Build Coastguard Worker 401*d68f33bcSAndroid Build Coastguard Worker if popen_stderr and popen_stderr != subprocess.STDOUT: 402*d68f33bcSAndroid Build Coastguard Worker # The linter is confused by how stderr is a file & an int. 403*d68f33bcSAndroid Build Coastguard Worker # pylint: disable=maybe-no-member,no-member 404*d68f33bcSAndroid Build Coastguard Worker popen_stderr.seek(0) 405*d68f33bcSAndroid Build Coastguard Worker result.stderr = popen_stderr.read() 406*d68f33bcSAndroid Build Coastguard Worker popen_stderr.close() 407*d68f33bcSAndroid Build Coastguard Worker 408*d68f33bcSAndroid Build Coastguard Worker result.returncode = proc.returncode 409*d68f33bcSAndroid Build Coastguard Worker 410*d68f33bcSAndroid Build Coastguard Worker if check and proc.returncode: 411*d68f33bcSAndroid Build Coastguard Worker msg = f'cwd={cwd}' 412*d68f33bcSAndroid Build Coastguard Worker if extra_env: 413*d68f33bcSAndroid Build Coastguard Worker msg += f', extra env={extra_env}' 414*d68f33bcSAndroid Build Coastguard Worker raise CalledProcessError( 415*d68f33bcSAndroid Build Coastguard Worker result.returncode, result.cmd, msg=msg, 416*d68f33bcSAndroid Build Coastguard Worker stdout=ensure_text(result.stdout), 417*d68f33bcSAndroid Build Coastguard Worker stderr=ensure_text(result.stderr)) 418*d68f33bcSAndroid Build Coastguard Worker except OSError as e: 419*d68f33bcSAndroid Build Coastguard Worker # Avoid leaking tempfiles. 420*d68f33bcSAndroid Build Coastguard Worker if popen_stdout is not None and not isinstance(popen_stdout, int): 421*d68f33bcSAndroid Build Coastguard Worker popen_stdout.close() 422*d68f33bcSAndroid Build Coastguard Worker if popen_stderr is not None and not isinstance(popen_stderr, int): 423*d68f33bcSAndroid Build Coastguard Worker popen_stderr.close() 424*d68f33bcSAndroid Build Coastguard Worker 425*d68f33bcSAndroid Build Coastguard Worker estr = str(e) 426*d68f33bcSAndroid Build Coastguard Worker if e.errno == errno.EACCES: 427*d68f33bcSAndroid Build Coastguard Worker estr += '; does the program need `chmod a+x`?' 428*d68f33bcSAndroid Build Coastguard Worker if not check: 429*d68f33bcSAndroid Build Coastguard Worker result = CompletedProcess(args=cmd, returncode=255) 430*d68f33bcSAndroid Build Coastguard Worker if combine_stdout_stderr: 431*d68f33bcSAndroid Build Coastguard Worker result.stdout = estr 432*d68f33bcSAndroid Build Coastguard Worker else: 433*d68f33bcSAndroid Build Coastguard Worker result.stderr = estr 434*d68f33bcSAndroid Build Coastguard Worker else: 435*d68f33bcSAndroid Build Coastguard Worker raise CalledProcessError( 436*d68f33bcSAndroid Build Coastguard Worker result.returncode, result.cmd, msg=estr, 437*d68f33bcSAndroid Build Coastguard Worker stdout=ensure_text(result.stdout), 438*d68f33bcSAndroid Build Coastguard Worker stderr=ensure_text(result.stderr)) from e 439*d68f33bcSAndroid Build Coastguard Worker finally: 440*d68f33bcSAndroid Build Coastguard Worker if proc is not None: 441*d68f33bcSAndroid Build Coastguard Worker # Ensure the process is dead. 442*d68f33bcSAndroid Build Coastguard Worker # Some pylint3 versions are confused here. 443*d68f33bcSAndroid Build Coastguard Worker # pylint: disable=too-many-function-args 444*d68f33bcSAndroid Build Coastguard Worker _kill_child_process(proc, int_timeout, kill_timeout, cmd, None, 445*d68f33bcSAndroid Build Coastguard Worker None, None) 446*d68f33bcSAndroid Build Coastguard Worker 447*d68f33bcSAndroid Build Coastguard Worker # Make sure output is returned as a string rather than bytes. 448*d68f33bcSAndroid Build Coastguard Worker result.stdout = ensure_text(result.stdout) 449*d68f33bcSAndroid Build Coastguard Worker result.stderr = ensure_text(result.stderr) 450*d68f33bcSAndroid Build Coastguard Worker 451*d68f33bcSAndroid Build Coastguard Worker return result 452*d68f33bcSAndroid Build Coastguard Worker# pylint: enable=redefined-builtin 453