11a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 283d290c5STom Rini# Copyright (c) 2011 The Chromium OS Authors. 30d24de9dSSimon Glass# 40d24de9dSSimon Glass 50d24de9dSSimon Glassimport os 6a10fd93cSSimon Glassimport cros_subprocess 70d24de9dSSimon Glass 80d24de9dSSimon Glass"""Shell command ease-ups for Python.""" 90d24de9dSSimon Glass 10a10fd93cSSimon Glassclass CommandResult: 11a10fd93cSSimon Glass """A class which captures the result of executing a command. 12a10fd93cSSimon Glass 13a10fd93cSSimon Glass Members: 14a10fd93cSSimon Glass stdout: stdout obtained from command, as a string 15a10fd93cSSimon Glass stderr: stderr obtained from command, as a string 16a10fd93cSSimon Glass return_code: Return code from command 17a10fd93cSSimon Glass exception: Exception received, or None if all ok 18a10fd93cSSimon Glass """ 19a10fd93cSSimon Glass def __init__(self): 20a10fd93cSSimon Glass self.stdout = None 21a10fd93cSSimon Glass self.stderr = None 2282012dd2SSimon Glass self.combined = None 23a10fd93cSSimon Glass self.return_code = None 24a10fd93cSSimon Glass self.exception = None 25a10fd93cSSimon Glass 2682012dd2SSimon Glass def __init__(self, stdout='', stderr='', combined='', return_code=0, 2782012dd2SSimon Glass exception=None): 2882012dd2SSimon Glass self.stdout = stdout 2982012dd2SSimon Glass self.stderr = stderr 3082012dd2SSimon Glass self.combined = combined 3182012dd2SSimon Glass self.return_code = return_code 3282012dd2SSimon Glass self.exception = exception 3382012dd2SSimon Glass 3482012dd2SSimon Glass 3582012dd2SSimon Glass# This permits interception of RunPipe for test purposes. If it is set to 3682012dd2SSimon Glass# a function, then that function is called with the pipe list being 3782012dd2SSimon Glass# executed. Otherwise, it is assumed to be a CommandResult object, and is 3882012dd2SSimon Glass# returned as the result for every RunPipe() call. 3982012dd2SSimon Glass# When this value is None, commands are executed as normal. 4082012dd2SSimon Glasstest_result = None 41a10fd93cSSimon Glass 42a10fd93cSSimon Glassdef RunPipe(pipe_list, infile=None, outfile=None, 43a10fd93cSSimon Glass capture=False, capture_stderr=False, oneline=False, 44dc191505SSimon Glass raise_on_error=True, cwd=None, **kwargs): 450d24de9dSSimon Glass """ 460d24de9dSSimon Glass Perform a command pipeline, with optional input/output filenames. 470d24de9dSSimon Glass 48a10fd93cSSimon Glass Args: 49a10fd93cSSimon Glass pipe_list: List of command lines to execute. Each command line is 50a10fd93cSSimon Glass piped into the next, and is itself a list of strings. For 51a10fd93cSSimon Glass example [ ['ls', '.git'] ['wc'] ] will pipe the output of 52a10fd93cSSimon Glass 'ls .git' into 'wc'. 53a10fd93cSSimon Glass infile: File to provide stdin to the pipeline 54a10fd93cSSimon Glass outfile: File to store stdout 55a10fd93cSSimon Glass capture: True to capture output 56a10fd93cSSimon Glass capture_stderr: True to capture stderr 57a10fd93cSSimon Glass oneline: True to strip newline chars from output 58a10fd93cSSimon Glass kwargs: Additional keyword arguments to cros_subprocess.Popen() 59a10fd93cSSimon Glass Returns: 60a10fd93cSSimon Glass CommandResult object 610d24de9dSSimon Glass """ 6282012dd2SSimon Glass if test_result: 6382012dd2SSimon Glass if hasattr(test_result, '__call__'): 64*2b19321eSSimon Glass result = test_result(pipe_list=pipe_list) 65*2b19321eSSimon Glass if result: 66*2b19321eSSimon Glass return result 67*2b19321eSSimon Glass else: 6882012dd2SSimon Glass return test_result 69*2b19321eSSimon Glass # No result: fall through to normal processing 70a10fd93cSSimon Glass result = CommandResult() 710d24de9dSSimon Glass last_pipe = None 72a10fd93cSSimon Glass pipeline = list(pipe_list) 73dc191505SSimon Glass user_pipestr = '|'.join([' '.join(pipe) for pipe in pipe_list]) 74ddaf5c8fSSimon Glass kwargs['stdout'] = None 75ddaf5c8fSSimon Glass kwargs['stderr'] = None 760d24de9dSSimon Glass while pipeline: 770d24de9dSSimon Glass cmd = pipeline.pop(0) 780d24de9dSSimon Glass if last_pipe is not None: 790d24de9dSSimon Glass kwargs['stdin'] = last_pipe.stdout 800d24de9dSSimon Glass elif infile: 810d24de9dSSimon Glass kwargs['stdin'] = open(infile, 'rb') 820d24de9dSSimon Glass if pipeline or capture: 83a10fd93cSSimon Glass kwargs['stdout'] = cros_subprocess.PIPE 840d24de9dSSimon Glass elif outfile: 850d24de9dSSimon Glass kwargs['stdout'] = open(outfile, 'wb') 86a10fd93cSSimon Glass if capture_stderr: 87a10fd93cSSimon Glass kwargs['stderr'] = cros_subprocess.PIPE 880d24de9dSSimon Glass 89a10fd93cSSimon Glass try: 90a10fd93cSSimon Glass last_pipe = cros_subprocess.Popen(cmd, cwd=cwd, **kwargs) 91ac3fde93SPaul Burton except Exception as err: 92a10fd93cSSimon Glass result.exception = err 93dc191505SSimon Glass if raise_on_error: 94dc191505SSimon Glass raise Exception("Error running '%s': %s" % (user_pipestr, str)) 95dc191505SSimon Glass result.return_code = 255 96dc191505SSimon Glass return result 970d24de9dSSimon Glass 980d24de9dSSimon Glass if capture: 99a10fd93cSSimon Glass result.stdout, result.stderr, result.combined = ( 100a10fd93cSSimon Glass last_pipe.CommunicateFilter(None)) 101a10fd93cSSimon Glass if result.stdout and oneline: 102a10fd93cSSimon Glass result.output = result.stdout.rstrip('\r\n') 103a10fd93cSSimon Glass result.return_code = last_pipe.wait() 1040d24de9dSSimon Glass else: 105a10fd93cSSimon Glass result.return_code = os.waitpid(last_pipe.pid, 0)[1] 106dc191505SSimon Glass if raise_on_error and result.return_code: 107dc191505SSimon Glass raise Exception("Error running '%s'" % user_pipestr) 108a10fd93cSSimon Glass return result 1090d24de9dSSimon Glass 110785f1548SSimon Glassdef Output(*cmd, **kwargs): 111785f1548SSimon Glass raise_on_error = kwargs.get('raise_on_error', True) 112785f1548SSimon Glass return RunPipe([cmd], capture=True, raise_on_error=raise_on_error).stdout 1130d24de9dSSimon Glass 114a10fd93cSSimon Glassdef OutputOneLine(*cmd, **kwargs): 115dc191505SSimon Glass raise_on_error = kwargs.pop('raise_on_error', True) 116a10fd93cSSimon Glass return (RunPipe([cmd], capture=True, oneline=True, 117dc191505SSimon Glass raise_on_error=raise_on_error, 118a10fd93cSSimon Glass **kwargs).stdout.strip()) 1190d24de9dSSimon Glass 1200d24de9dSSimon Glassdef Run(*cmd, **kwargs): 121a10fd93cSSimon Glass return RunPipe([cmd], **kwargs).stdout 1220d24de9dSSimon Glass 1230d24de9dSSimon Glassdef RunList(cmd): 124a10fd93cSSimon Glass return RunPipe([cmd], capture=True).stdout 125a10fd93cSSimon Glass 126a10fd93cSSimon Glassdef StopAll(): 127a10fd93cSSimon Glass cros_subprocess.stay_alive = False 128