| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """A wrapper for subprocess to make calling shell commands easier.""" | 5 """A wrapper for subprocess to make calling shell commands easier.""" |
| 6 | 6 |
| 7 import logging | 7 import logging |
| 8 import os | 8 import os |
| 9 import pipes | 9 import pipes |
| 10 import select | 10 import select |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 shell: Whether to execute args as a shell command. | 108 shell: Whether to execute args as a shell command. |
| 109 | 109 |
| 110 Returns: | 110 Returns: |
| 111 Captures and returns the command's stdout. | 111 Captures and returns the command's stdout. |
| 112 Prints the command's stderr to logger (which defaults to stdout). | 112 Prints the command's stderr to logger (which defaults to stdout). |
| 113 """ | 113 """ |
| 114 (_, output) = GetCmdStatusAndOutput(args, cwd, shell) | 114 (_, output) = GetCmdStatusAndOutput(args, cwd, shell) |
| 115 return output | 115 return output |
| 116 | 116 |
| 117 | 117 |
| 118 def _LogCommand(args, cwd=None): |
| 119 if not isinstance(args, basestring): |
| 120 args = ' '.join(SingleQuote(c) for c in args) |
| 121 if cwd is None: |
| 122 cwd = '' |
| 123 else: |
| 124 cwd = ':' + cwd |
| 125 logging.info('[host]%s> %s', cwd, args) |
| 126 |
| 127 |
| 118 def GetCmdStatusAndOutput(args, cwd=None, shell=False): | 128 def GetCmdStatusAndOutput(args, cwd=None, shell=False): |
| 119 """Executes a subprocess and returns its exit code and output. | 129 """Executes a subprocess and returns its exit code and output. |
| 120 | 130 |
| 121 Args: | 131 Args: |
| 122 args: A string or a sequence of program arguments. The program to execute is | 132 args: A string or a sequence of program arguments. The program to execute is |
| 123 the string or the first item in the args sequence. | 133 the string or the first item in the args sequence. |
| 124 cwd: If not None, the subprocess's current directory will be changed to | 134 cwd: If not None, the subprocess's current directory will be changed to |
| 125 |cwd| before it's executed. | 135 |cwd| before it's executed. |
| 126 shell: Whether to execute args as a shell command. | 136 shell: Whether to execute args as a shell command. |
| 127 | 137 |
| 128 Returns: | 138 Returns: |
| 129 The 2-tuple (exit code, output). | 139 The 2-tuple (exit code, output). |
| 130 """ | 140 """ |
| 131 if isinstance(args, basestring): | 141 if isinstance(args, basestring): |
| 132 args_repr = args | |
| 133 if not shell: | 142 if not shell: |
| 134 raise Exception('string args must be run with shell=True') | 143 raise Exception('string args must be run with shell=True') |
| 135 elif shell: | 144 elif shell: |
| 136 raise Exception('array args must be run with shell=False') | 145 raise Exception('array args must be run with shell=False') |
| 137 else: | |
| 138 args_repr = ' '.join(map(SingleQuote, args)) | |
| 139 | 146 |
| 140 s = '[host]' | 147 _LogCommand(args, cwd) |
| 141 if cwd: | |
| 142 s += ':' + cwd | |
| 143 s += '> ' + args_repr | |
| 144 logging.info(s) | |
| 145 pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 148 pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 146 shell=shell, cwd=cwd) | 149 shell=shell, cwd=cwd) |
| 147 stdout, stderr = pipe.communicate() | 150 stdout, stderr = pipe.communicate() |
| 148 | 151 |
| 149 if stderr: | 152 if stderr: |
| 150 logging.critical(stderr) | 153 logging.critical(stderr) |
| 151 if len(stdout) > 4096: | 154 if len(stdout) > 4096: |
| 152 logging.debug('Truncated output:') | 155 logging.debug('Truncated output:') |
| 153 logging.debug(stdout[:4096]) | 156 logging.debug(stdout[:4096]) |
| 154 return (pipe.returncode, stdout) | 157 return (pipe.returncode, stdout) |
| (...skipping 15 matching lines...) Expand all Loading... |
| 170 cwd: If not None, the subprocess's current directory will be changed to | 173 cwd: If not None, the subprocess's current directory will be changed to |
| 171 |cwd| before it's executed. | 174 |cwd| before it's executed. |
| 172 shell: Whether to execute args as a shell command. | 175 shell: Whether to execute args as a shell command. |
| 173 logfile: Optional file-like object that will receive output from the | 176 logfile: Optional file-like object that will receive output from the |
| 174 command as it is running. | 177 command as it is running. |
| 175 | 178 |
| 176 Returns: | 179 Returns: |
| 177 The 2-tuple (exit code, output). | 180 The 2-tuple (exit code, output). |
| 178 """ | 181 """ |
| 179 assert fcntl, 'fcntl module is required' | 182 assert fcntl, 'fcntl module is required' |
| 183 if isinstance(args, basestring): |
| 184 if not shell: |
| 185 raise Exception('string args must be run with shell=True') |
| 186 elif shell: |
| 187 raise Exception('array args must be run with shell=False') |
| 188 |
| 189 _LogCommand(args, cwd) |
| 180 process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE, | 190 process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE, |
| 181 stderr=subprocess.STDOUT) | 191 stderr=subprocess.STDOUT) |
| 182 try: | 192 try: |
| 183 end_time = (time.time() + timeout) if timeout else None | 193 end_time = (time.time() + timeout) if timeout else None |
| 184 poll_interval = 1 | 194 poll_interval = 1 |
| 185 buffer_size = 4096 | 195 buffer_size = 4096 |
| 186 child_fd = process.stdout.fileno() | 196 child_fd = process.stdout.fileno() |
| 187 output = StringIO.StringIO() | 197 output = StringIO.StringIO() |
| 188 | 198 |
| 189 # Enable non-blocking reads from the child's stdout. | 199 # Enable non-blocking reads from the child's stdout. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 205 break | 215 break |
| 206 finally: | 216 finally: |
| 207 try: | 217 try: |
| 208 # Make sure the process doesn't stick around if we fail with an | 218 # Make sure the process doesn't stick around if we fail with an |
| 209 # exception. | 219 # exception. |
| 210 process.kill() | 220 process.kill() |
| 211 except OSError: | 221 except OSError: |
| 212 pass | 222 pass |
| 213 process.wait() | 223 process.wait() |
| 214 return process.returncode, output.getvalue() | 224 return process.returncode, output.getvalue() |
| OLD | NEW |