|
| 1 | +import builtins |
| 2 | +import errno |
| 3 | +import io |
| 4 | +import os |
| 5 | +import subprocess |
| 6 | +import warnings |
| 7 | + |
| 8 | +import _posixsubprocess |
| 9 | +from subprocess import (PIPE, _PLATFORM_DEFAULT_CLOSE_FDS, _create_pipe, |
| 10 | + _cleanup, _eintr_retry_call, ) |
| 11 | + |
| 12 | +mswindows = msvcrt = False |
| 13 | + |
| 14 | + |
| 15 | +# Popen for python 3.3 |
| 16 | +class _Popen(subprocess.Popen): |
| 17 | + def __init__(self, args, bufsize=-1, executable=None, |
| 18 | + stdin=None, stdout=None, stderr=None, |
| 19 | + preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS, |
| 20 | + shell=False, cwd=None, env=None, universal_newlines=False, |
| 21 | + startupinfo=None, creationflags=0, |
| 22 | + restore_signals=True, start_new_session=False, |
| 23 | + pass_fds=()): |
| 24 | + """Create new Popen instance.""" |
| 25 | + _cleanup() |
| 26 | + |
| 27 | + self._input = None |
| 28 | + self._communication_started = False |
| 29 | + if bufsize is None: |
| 30 | + bufsize = -1 # Restore default |
| 31 | + if not isinstance(bufsize, int): |
| 32 | + raise TypeError("bufsize must be an integer") |
| 33 | + |
| 34 | + if mswindows: |
| 35 | + if preexec_fn is not None: |
| 36 | + raise ValueError("preexec_fn is not supported on Windows " |
| 37 | + "platforms") |
| 38 | + any_stdio_set = (stdin is not None or stdout is not None or |
| 39 | + stderr is not None) |
| 40 | + if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS: |
| 41 | + if any_stdio_set: |
| 42 | + close_fds = False |
| 43 | + else: |
| 44 | + close_fds = True |
| 45 | + elif close_fds and any_stdio_set: |
| 46 | + raise ValueError( |
| 47 | + "close_fds is not supported on Windows platforms" |
| 48 | + " if you redirect stdin/stdout/stderr") |
| 49 | + else: |
| 50 | + # POSIX |
| 51 | + if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS: |
| 52 | + close_fds = True |
| 53 | + if pass_fds and not close_fds: |
| 54 | + warnings.warn("pass_fds overriding close_fds.", RuntimeWarning) |
| 55 | + close_fds = True |
| 56 | + if startupinfo is not None: |
| 57 | + raise ValueError("startupinfo is only supported on Windows " |
| 58 | + "platforms") |
| 59 | + if creationflags != 0: |
| 60 | + raise ValueError("creationflags is only supported on Windows " |
| 61 | + "platforms") |
| 62 | + |
| 63 | + self.args = args |
| 64 | + self.stdin = None |
| 65 | + self.stdout = None |
| 66 | + self.stderr = None |
| 67 | + self.pid = None |
| 68 | + self.returncode = None |
| 69 | + self.universal_newlines = universal_newlines |
| 70 | + |
| 71 | + # Input and output objects. The general principle is like |
| 72 | + # this: |
| 73 | + # |
| 74 | + # Parent Child |
| 75 | + # ------ ----- |
| 76 | + # p2cwrite ---stdin---> p2cread |
| 77 | + # c2pread <--stdout--- c2pwrite |
| 78 | + # errread <--stderr--- errwrite |
| 79 | + # |
| 80 | + # On POSIX, the child objects are file descriptors. On |
| 81 | + # Windows, these are Windows file handles. The parent objects |
| 82 | + # are file descriptors on both platforms. The parent objects |
| 83 | + # are -1 when not using PIPEs. The child objects are -1 |
| 84 | + # when not redirecting. |
| 85 | + |
| 86 | + (p2cread, p2cwrite, |
| 87 | + c2pread, c2pwrite, |
| 88 | + errread, errwrite) = self._get_handles(stdin, stdout, stderr) |
| 89 | + |
| 90 | + # We wrap OS handles *before* launching the child, otherwise a |
| 91 | + # quickly terminating child could make our fds unwrappable |
| 92 | + # (see #8458). |
| 93 | + |
| 94 | + if mswindows: |
| 95 | + if p2cwrite != -1: |
| 96 | + p2cwrite = msvcrt.open_osfhandle(p2cwrite.Detach(), 0) |
| 97 | + if c2pread != -1: |
| 98 | + c2pread = msvcrt.open_osfhandle(c2pread.Detach(), 0) |
| 99 | + if errread != -1: |
| 100 | + errread = msvcrt.open_osfhandle(errread.Detach(), 0) |
| 101 | + |
| 102 | + if p2cwrite != -1: |
| 103 | + self.stdin = io.open(p2cwrite, 'wb', bufsize) |
| 104 | + if universal_newlines: |
| 105 | + self.stdin = io.TextIOWrapper(self.stdin, write_through=True) |
| 106 | + if c2pread != -1: |
| 107 | + self.stdout = io.open(c2pread, 'rb', bufsize) |
| 108 | + if universal_newlines: |
| 109 | + self.stdout = io.TextIOWrapper(self.stdout) |
| 110 | + if errread != -1: |
| 111 | + self.stderr = io.open(errread, 'rb', bufsize) |
| 112 | + if universal_newlines: |
| 113 | + self.stderr = io.TextIOWrapper(self.stderr) |
| 114 | + |
| 115 | + self._child_pipes_to_close = set() |
| 116 | + if stdin == PIPE: |
| 117 | + self._child_pipes_to_close.add(p2cread) |
| 118 | + if stdout == PIPE: |
| 119 | + self._child_pipes_to_close.add(c2pwrite) |
| 120 | + if stderr == PIPE: |
| 121 | + self._child_pipes_to_close.add(errwrite) |
| 122 | + if hasattr(self, '_devnull'): |
| 123 | + self._child_pipes_to_close.add(self._devnull) |
| 124 | + |
| 125 | + try: |
| 126 | + self._execute_child(args, executable, preexec_fn, close_fds, |
| 127 | + pass_fds, cwd, env, |
| 128 | + startupinfo, creationflags, shell, |
| 129 | + p2cread, p2cwrite, |
| 130 | + c2pread, c2pwrite, |
| 131 | + errread, errwrite, |
| 132 | + restore_signals, start_new_session) |
| 133 | + except: |
| 134 | + # Cleanup if the child failed starting. |
| 135 | + self._cleanup_on_exec_failure() |
| 136 | + raise |
| 137 | + |
| 138 | + def _cleanup_on_exec_failure(self): |
| 139 | + for f in filter(None, (self.stdin, self.stdout, self.stderr)): |
| 140 | + try: |
| 141 | + f.close() |
| 142 | + except OSError: |
| 143 | + pass # Ignore EBADF or other errors. |
| 144 | + |
| 145 | + for fd in self._child_pipes_to_close: |
| 146 | + try: |
| 147 | + os.close(fd) |
| 148 | + except OSError: |
| 149 | + pass |
| 150 | + |
| 151 | + self._child_pipes_to_close.clear() |
| 152 | + |
| 153 | + if True: # XXX if unix |
| 154 | + def _execute_child(self, args, executable, preexec_fn, close_fds, |
| 155 | + pass_fds, cwd, env, |
| 156 | + startupinfo, creationflags, shell, |
| 157 | + p2cread, p2cwrite, |
| 158 | + c2pread, c2pwrite, |
| 159 | + errread, errwrite, |
| 160 | + restore_signals, start_new_session): |
| 161 | + """Execute program (POSIX version)""" |
| 162 | + |
| 163 | + if isinstance(args, (str, bytes)): |
| 164 | + args = [args] |
| 165 | + else: |
| 166 | + args = list(args) |
| 167 | + |
| 168 | + if shell: |
| 169 | + args = ["/bin/sh", "-c"] + args |
| 170 | + if executable: |
| 171 | + args[0] = executable |
| 172 | + |
| 173 | + if executable is None: |
| 174 | + executable = args[0] |
| 175 | + orig_executable = executable |
| 176 | + |
| 177 | + # For transferring possible exec failure from child to parent. |
| 178 | + # Data format: "exception name:hex errno:description" |
| 179 | + # Pickle is not used; it is complex and involves memory allocation. |
| 180 | + errpipe_read, errpipe_write = self._get_exec_err_pipe() |
| 181 | + try: |
| 182 | + try: |
| 183 | + # We must avoid complex work that could involve |
| 184 | + # malloc or free in the child process to avoid |
| 185 | + # potential deadlocks, thus we do all this here. |
| 186 | + # and pass it to fork_exec() |
| 187 | + |
| 188 | + if env is not None: |
| 189 | + env_list = [os.fsencode(k) + b'=' + os.fsencode(v) |
| 190 | + for k, v in env.items()] |
| 191 | + else: |
| 192 | + env_list = None # Use execv instead of execve. |
| 193 | + executable = os.fsencode(executable) |
| 194 | + if os.path.dirname(executable): |
| 195 | + executable_list = (executable,) |
| 196 | + else: |
| 197 | + # This matches the behavior of os._execvpe(). |
| 198 | + executable_list = tuple( |
| 199 | + os.path.join(os.fsencode(dir), executable) |
| 200 | + for dir in os.get_exec_path(env)) |
| 201 | + fds_to_keep = set(pass_fds) |
| 202 | + fds_to_keep.add(errpipe_write) |
| 203 | + self.pid = _posixsubprocess.fork_exec( |
| 204 | + args, executable_list, |
| 205 | + close_fds, sorted(fds_to_keep), cwd, env_list, |
| 206 | + p2cread, p2cwrite, c2pread, c2pwrite, |
| 207 | + errread, errwrite, |
| 208 | + errpipe_read, errpipe_write, |
| 209 | + restore_signals, start_new_session, preexec_fn) |
| 210 | + self._child_created = True |
| 211 | + finally: |
| 212 | + # be sure the FD is closed no matter what |
| 213 | + os.close(errpipe_write) |
| 214 | + |
| 215 | + # self._devnull is not always defined. |
| 216 | + devnull_fd = getattr(self, '_devnull', None) |
| 217 | + to_close = set() |
| 218 | + if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd: |
| 219 | + to_close.add(p2cread) |
| 220 | + if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd: |
| 221 | + to_close.add(c2pwrite) |
| 222 | + if errwrite != -1 and errread != -1 and errwrite != devnull_fd: |
| 223 | + to_close.add(errwrite) |
| 224 | + if devnull_fd is not None: |
| 225 | + to_close.add(devnull_fd) |
| 226 | + for fd in to_close: |
| 227 | + os.close(fd) |
| 228 | + # Prevent a double close of these fds from __init__ on error. |
| 229 | + self._child_pipes_to_close.remove(fd) |
| 230 | + except: |
| 231 | + os.close(errpipe_read) |
| 232 | + raise |
| 233 | + |
| 234 | + self._wait_exec_done(orig_executable, cwd, errpipe_read) |
| 235 | + |
| 236 | + def _get_exec_err_pipe(self): |
| 237 | + return _create_pipe() |
| 238 | + |
| 239 | + def _wait_exec_done(self, orig_executable, cwd, errpipe_read): |
| 240 | + assert errpipe_read is not None |
| 241 | + try: |
| 242 | + # Wait for exec to fail or succeed; possibly raising an |
| 243 | + # exception (limited in size) |
| 244 | + errpipe_data = bytearray() |
| 245 | + while True: |
| 246 | + part = _eintr_retry_call(os.read, errpipe_read, 50000) |
| 247 | + errpipe_data += part |
| 248 | + if not part or len(errpipe_data) > 50000: |
| 249 | + break |
| 250 | + finally: |
| 251 | + # be sure the FD is closed no matter what |
| 252 | + os.close(errpipe_read) |
| 253 | + |
| 254 | + if errpipe_data: |
| 255 | + self._check_exec_result(orig_executable, cwd, errpipe_data) |
| 256 | + |
| 257 | + def _check_exec_result(self, orig_executable, cwd, errpipe_data): |
| 258 | + try: |
| 259 | + _eintr_retry_call(os.waitpid, self.pid, 0) |
| 260 | + except OSError as e: |
| 261 | + if e.errno != errno.ECHILD: |
| 262 | + raise |
| 263 | + try: |
| 264 | + exception_name, hex_errno, err_msg = ( |
| 265 | + errpipe_data.split(b':', 2)) |
| 266 | + except ValueError: |
| 267 | + exception_name = b'RuntimeError' |
| 268 | + hex_errno = b'0' |
| 269 | + err_msg = (b'Bad exception data from child: ' + |
| 270 | + repr(errpipe_data)) |
| 271 | + child_exception_type = getattr( |
| 272 | + builtins, exception_name.decode('ascii'), |
| 273 | + RuntimeError) |
| 274 | + err_msg = err_msg.decode(errors="surrogatepass") |
| 275 | + if issubclass(child_exception_type, OSError) and hex_errno: |
| 276 | + errno_num = int(hex_errno, 16) |
| 277 | + child_exec_never_called = (err_msg == "noexec") |
| 278 | + if child_exec_never_called: |
| 279 | + err_msg = "" |
| 280 | + if errno_num != 0: |
| 281 | + err_msg = os.strerror(errno_num) |
| 282 | + if errno_num == errno.ENOENT: |
| 283 | + if child_exec_never_called: |
| 284 | + # The error must be from chdir(cwd). |
| 285 | + err_msg += ': ' + repr(cwd) |
| 286 | + else: |
| 287 | + err_msg += ': ' + repr(orig_executable) |
| 288 | + raise child_exception_type(errno_num, err_msg) |
| 289 | + raise child_exception_type(err_msg) |
0 commit comments