def __init__(self, args, stdin=None, stdout=None): self.returncode = None self.stdout = None self.stdin = None # TODO: stderr file_actions = posix_spawn.FileActions() if stdout is not None: # child's stdout, child 2 parent pipe c2pread, c2pwrite = os.pipe() # attach child's stdout to writing en of c2p pipe file_actions.add_dup2(c2pwrite, 1) # close other end file_actions.add_close(c2pread) if stdin is not None: # child's stdin, parent to child pipe p2cread, p2cwrite = os.pipe() # attach child's stdin to reading en of p2c pipe file_actions.add_dup2(p2cread, 0) # close other end file_actions.add_close(p2cwrite) self.pid = posix_spawn.posix_spawnp(args[0], args, file_actions=file_actions) if stdout is not None: self.stdout = os.fdopen(c2pread) os.close(c2pwrite) if stdin is not None: self.stdin = os.fdopen(p2cwrite, 'w') os.close(p2cread)
def call(*args): cmd = [] if isinstance(args[0], str): if len(args) == 1: # oversimplified splitting of arguments, # TODO: care about use of simple and double quotes cmd = args[0].split() else: cmd = args elif isinstance(args[0], list) and len(args) == 1: cmd = args[0] else: raise Exception("Wrong arguments passed to subprocess.call") pid = posix_spawn.posix_spawnp(cmd[0], cmd) return os.waitpid(pid, 0)
def __execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): """ Executes the program using posix_spawn(). This is based on the method from the superclass but the posix_spawn API forces a number of changes. In particular: * When using fork() FDs are manipulated in the child process after the fork, but before the program is exec()ed. With posix_spawn() this is done by passing a data-structure to the posix_spawn() call, which describes the FD manipulations to perform. * The fork() version waits until after the fork before unsetting the non-blocking flag on the FDs that the child has inherited. In the posix_spawn() version, we cannot do that after the fork so we dup the FDs in advance and unset the flag on the duped FD, which we then pass to the child. """ if preexec_fn is not None: raise NotImplementedError("preexec_fn not supported") if close_fds: raise NotImplementedError("close_fds not implemented") if cwd: raise NotImplementedError( "cwd not implemented") # pragma: no cover if universal_newlines: raise NotImplementedError() # pragma: no cover assert startupinfo is None and creationflags == 0 _log.debug("Pipes: p2c %s, %s; c2p %s, %s; err %s, %s", p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) if isinstance(args, types.StringTypes): args = [args] else: args = [a.encode("ascii") for a in args] if shell: args = ["/bin/sh", "-c"] + args if executable: args[0] = executable if executable is None: executable = args[0] self._loop.install_sigchld() # The FileActions object is an ordered list of FD operations for # posix_spawn to do in the child process before it execs the new # program. file_actions = FileActions() # In the child, close parent's pipe ends. if p2cwrite is not None: file_actions.add_close(p2cwrite) if c2pread is not None: file_actions.add_close(c2pread) if errread is not None: file_actions.add_close(errread) # When duping fds, if there arises a situation where one of the fds # is either 0, 1 or 2, it is possible that it is overwritten (#12607). fds_to_close_in_parent = [] if c2pwrite == 0: c2pwrite = os.dup(c2pwrite) fds_to_close_in_parent.append(c2pwrite) if errwrite == 0 or errwrite == 1: errwrite = os.dup(errwrite) fds_to_close_in_parent.append(errwrite) # Dup stdin/out/err FDs in child. def _dup2(dup_from, dup_to): if dup_from is None: # Pass through the existing FD. dup_from = dup_to # Need to take a dup so we can remove the non-blocking flag a_dup = os.dup(dup_from) _log.debug("Duped %s as %s", dup_from, a_dup) fds_to_close_in_parent.append(a_dup) self._remove_nonblock_flag(a_dup) file_actions.add_dup2(a_dup, dup_to) _dup2(p2cread, 0) _dup2(c2pwrite, 1) _dup2(errwrite, 2) # Close pipe fds in the child. Make sure we don't close the same fd # more than once, or standard fds. for fd in set([p2cread, c2pwrite, errwrite]): if fd > 2: file_actions.add_close(fd) gc_was_enabled = gc.isenabled() # FIXME Does this bug apply to posix_spawn version? try: # Disable gc to avoid bug where gc -> file_dealloc -> # write to stderr -> hang. http://bugs.python.org/issue1336 gc.disable() self.pid = posix_spawnp( executable, args, file_actions=file_actions, env=env, ) except: if gc_was_enabled: gc.enable() raise finally: for fd in fds_to_close_in_parent: os.close(fd) # Capture the SIGCHILD. self._watcher = self._loop.child(self.pid) self._watcher.start(self._on_child, self._watcher) if gc_was_enabled: gc.enable() # Close the Child's pipe ends in the parent. if p2cread is not None and p2cwrite is not None: os.close(p2cread) if c2pwrite is not None and c2pread is not None: os.close(c2pwrite) if errwrite is not None and errread is not None: os.close(errwrite)
def __execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): """ Executes the program using posix_spawn(). This is based on the method from the superclass but the posix_spawn API forces a number of changes. In particular: * When using fork() FDs are manipulated in the child process after the fork, but before the program is exec()ed. With posix_spawn() this is done by passing a data-structure to the posix_spawn() call, which describes the FD manipulations to perform. * The fork() version waits until after the fork before unsetting the non-blocking flag on the FDs that the child has inherited. In the posix_spawn() version, we cannot do that after the fork so we dup the FDs in advance and unset the flag on the duped FD, which we then pass to the child. """ if preexec_fn is not None: raise NotImplementedError("preexec_fn not supported") if close_fds: raise NotImplementedError("close_fds not implemented") if cwd: raise NotImplementedError("cwd not implemented") # pragma: no cover if universal_newlines: raise NotImplementedError() # pragma: no cover assert startupinfo is None and creationflags == 0 _log.debug("Pipes: p2c %s, %s; c2p %s, %s; err %s, %s", p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) if isinstance(args, types.StringTypes): args = [args] else: args = [a.encode("ascii") for a in args] if shell: args = ["/bin/sh", "-c"] + args if executable: args[0] = executable if executable is None: executable = args[0] self._loop.install_sigchld() # The FileActions object is an ordered list of FD operations for # posix_spawn to do in the child process before it execs the new # program. file_actions = FileActions() # In the child, close parent's pipe ends. if p2cwrite is not None: file_actions.add_close(p2cwrite) if c2pread is not None: file_actions.add_close(c2pread) if errread is not None: file_actions.add_close(errread) # When duping fds, if there arises a situation where one of the fds # is either 0, 1 or 2, it is possible that it is overwritten (#12607). fds_to_close_in_parent = [] if c2pwrite == 0: c2pwrite = os.dup(c2pwrite) fds_to_close_in_parent.append(c2pwrite) if errwrite == 0 or errwrite == 1: errwrite = os.dup(errwrite) fds_to_close_in_parent.append(errwrite) # Dup stdin/out/err FDs in child. def _dup2(dup_from, dup_to): if dup_from is None: # Pass through the existing FD. dup_from = dup_to # Need to take a dup so we can remove the non-blocking flag a_dup = os.dup(dup_from) _log.debug("Duped %s as %s", dup_from, a_dup) fds_to_close_in_parent.append(a_dup) self._remove_nonblock_flag(a_dup) file_actions.add_dup2(a_dup, dup_to) _dup2(p2cread, 0) _dup2(c2pwrite, 1) _dup2(errwrite, 2) # Close pipe fds in the child. Make sure we don't close the same fd # more than once, or standard fds. for fd in set([p2cread, c2pwrite, errwrite]): if fd > 2: file_actions.add_close(fd) gc_was_enabled = gc.isenabled() # FIXME Does this bug apply to posix_spawn version? try: # Disable gc to avoid bug where gc -> file_dealloc -> # write to stderr -> hang. http://bugs.python.org/issue1336 gc.disable() self.pid = posix_spawnp( executable, args, file_actions=file_actions, env=env, ) except: if gc_was_enabled: gc.enable() raise finally: for fd in fds_to_close_in_parent: os.close(fd) # Capture the SIGCHILD. self._watcher = self._loop.child(self.pid) self._watcher.start(self._on_child, self._watcher) if gc_was_enabled: gc.enable() # Close the Child's pipe ends in the parent. if p2cread is not None and p2cwrite is not None: os.close(p2cread) if c2pwrite is not None and c2pread is not None: os.close(c2pwrite) if errwrite is not None and errread is not None: os.close(errwrite)
def test_echo_is_on_path(self): pid = posix_spawnp(b"echo", [b"echo", b"hello world"]) assert exits(pid) == 0