def test_pipe2(self): self.assertRaises(TypeError, os.pipe2, 'DEADBEEF') self.assertRaises(TypeError, os.pipe2, 0, 0) # try calling with flags = 0, like os.pipe() r, w = os.pipe2(0) os.close(r) os.close(w) # test flags r, w = os.pipe2(os.O_CLOEXEC|os.O_NONBLOCK) self.addCleanup(os.close, r) self.addCleanup(os.close, w) self.assertFalse(os.get_inheritable(r)) self.assertFalse(os.get_inheritable(w)) self.assertTrue(fcntl.fcntl(r, fcntl.F_GETFL) & os.O_NONBLOCK) self.assertTrue(fcntl.fcntl(w, fcntl.F_GETFL) & os.O_NONBLOCK) # try reading from an empty pipe: this should fail, not block self.assertRaises(OSError, os.read, r, 1) # try a write big enough to fill-up the pipe: this should either # fail or perform a partial write, not block try: os.write(w, b'x' * support.PIPE_MAX_SIZE) except OSError: pass
def test_noinherit(self): # _mkstemp_inner file handles are not inherited by child processes if support.verbose: v = "v" else: v = "q" file = self.do_create() self.assertEqual(os.get_inheritable(file.fd), False) fd = "%d" % file.fd try: me = __file__ except NameError: me = sys.argv[0] # We have to exec something, so that FD_CLOEXEC will take # effect. The core of this test is therefore in # tf_inherit_check.py, which see. tester = os.path.join(os.path.dirname(os.path.abspath(me)), "tf_inherit_check.py") # On Windows a spawn* /path/ with embedded spaces shouldn't be quoted, # but an arg with embedded spaces should be decorated with double # quotes on each end if sys.platform == "win32": decorated = '"%s"' % sys.executable tester = '"%s"' % tester else: decorated = sys.executable retval = os.spawnl(os.P_WAIT, sys.executable, decorated, tester, v, fd) self.assertFalse(retval < 0, "child process caught fatal signal %d" % -retval) self.assertFalse(retval > 0, "child process reports failure %d" % retval)
def set_inheritable(fd, inheritable): # On py34+ we can use os.set_inheritable but < py34 we must polyfill # with fcntl and SetHandleInformation if hasattr(os, 'get_inheritable'): if os.get_inheritable(fd) != inheritable: os.set_inheritable(fd, inheritable) elif WIN: h = get_handle(fd) flags = winapi.HANDLE_FLAG_INHERIT if inheritable else 0 winapi.SetHandleInformation(h, winapi.HANDLE_FLAG_INHERIT, flags) else: flags = fcntl.fcntl(fd, fcntl.F_GETFD) if inheritable: new_flags = flags & ~fcntl.FD_CLOEXEC else: new_flags = flags | fcntl.FD_CLOEXEC if new_flags != flags: fcntl.fcntl(fd, fcntl.F_SETFD, new_flags)
def get_inheritable(self): return os.get_inheritable(self.fileno())
def test_fd_non_inheritable(self): epoll = select.epoll() self.addCleanup(epoll.close) self.assertEqual(os.get_inheritable(epoll.fileno()), False)
def test_fd_non_inheritable(self): kqueue = select.kqueue() self.addCleanup(kqueue.close) self.assertEqual(os.get_inheritable(kqueue.fileno()), False)
def test_oscloexec(self): fd = os.open(support.TESTFN, os.O_RDONLY | os.O_CLOEXEC) self.addCleanup(os.close, fd) self.assertFalse(os.get_inheritable(fd))
def _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session): """Execute program (POSIX version)""" if PY3 and isinstance(args, (str, bytes)): args = [args] elif not PY3 and isinstance(args, string_types): args = [args] else: args = list(args) if shell: args = ["/bin/sh", "-c"] + args if executable: args[0] = executable if executable is None: executable = args[0] self._loop.install_sigchld() # For transferring possible exec failure from child to parent # The first char specifies the exception type: 0 means # OSError, 1 means some other error. errpipe_read, errpipe_write = self.pipe_cloexec() # errpipe_write must not be in the standard io 0, 1, or 2 fd range. low_fds_to_close = [] while errpipe_write < 3: low_fds_to_close.append(errpipe_write) errpipe_write = os.dup(errpipe_write) for low_fd in low_fds_to_close: os.close(low_fd) try: try: gc_was_enabled = gc.isenabled() # Disable gc to avoid bug where gc -> file_dealloc -> # write to stderr -> hang. http://bugs.python.org/issue1336 gc.disable() try: self.pid = fork_and_watch(self._on_child, self._loop, True, fork) except: if gc_was_enabled: gc.enable() raise if self.pid == 0: # Child try: # Close parent's pipe ends if p2cwrite is not None: os.close(p2cwrite) if c2pread is not None: os.close(c2pread) if errread is not None: os.close(errread) os.close(errpipe_read) # 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). if c2pwrite == 0: c2pwrite = os.dup(c2pwrite) if errwrite == 0 or errwrite == 1: errwrite = os.dup(errwrite) # Dup fds for child def _dup2(a, b): # dup2() removes the CLOEXEC flag but # we must do it ourselves if dup2() # would be a no-op (issue #10806). if a == b: self._set_cloexec_flag(a, False) elif a is not None: os.dup2(a, b) self._remove_nonblock_flag(b) _dup2(p2cread, 0) _dup2(c2pwrite, 1) _dup2(errwrite, 2) # Close pipe fds. Make sure we don't close the # same fd more than once, or standard fds. closed = set([None]) for fd in [p2cread, c2pwrite, errwrite]: if fd not in closed and fd > 2: os.close(fd) closed.add(fd) if cwd is not None: os.chdir(cwd) if preexec_fn: preexec_fn() # Close all other fds, if asked for. This must be done # after preexec_fn runs. if close_fds: fds_to_keep = set(pass_fds) fds_to_keep.add(errpipe_write) self._close_fds(fds_to_keep) elif hasattr(os, 'get_inheritable'): # close_fds was false, and we're on # Python 3.4 or newer, so "all file # descriptors except standard streams # are closed, and inheritable handles # are only inherited if the close_fds # parameter is False." for i in xrange(3, MAXFD): try: if i == errpipe_write or os.get_inheritable(i): continue os.close(i) except: pass if restore_signals: # restore the documented signals back to sig_dfl; # not all will be defined on every platform for sig in 'SIGPIPE', 'SIGXFZ', 'SIGXFSZ': sig = getattr(signal, sig, None) if sig is not None: signal.signal(sig, signal.SIG_DFL) if start_new_session: os.setsid() if env is None: os.execvp(executable, args) else: os.execvpe(executable, args, env) except: exc_type, exc_value, tb = sys.exc_info() # Save the traceback and attach it to the exception object exc_lines = traceback.format_exception(exc_type, exc_value, tb) exc_value.child_traceback = ''.join(exc_lines) os.write(errpipe_write, pickle.dumps(exc_value)) finally: # Make sure that the process exits no matter what. # The return code does not matter much as it won't be # reported to the application os._exit(1) # Parent self._child_created = True if gc_was_enabled: gc.enable() finally: # be sure the FD is closed no matter what os.close(errpipe_write) # self._devnull is not always defined. devnull_fd = getattr(self, '_devnull', None) if p2cread is not None and p2cwrite is not None and p2cread != devnull_fd: os.close(p2cread) if c2pwrite is not None and c2pread is not None and c2pwrite != devnull_fd: os.close(c2pwrite) if errwrite is not None and errread is not None and errwrite != devnull_fd: os.close(errwrite) if devnull_fd is not None: os.close(devnull_fd) # Prevent a double close of these fds from __init__ on error. self._closed_child_pipe_fds = True # Wait for exec to fail or succeed; possibly raising exception errpipe_read = FileObject(errpipe_read, 'rb') data = errpipe_read.read() finally: if hasattr(errpipe_read, 'close'): errpipe_read.close() else: os.close(errpipe_read) if data != b"": self.wait() child_exception = pickle.loads(data) for fd in (p2cwrite, c2pread, errread): if fd is not None: os.close(fd) raise child_exception
import os read_fd, write_fd = os.pipe() print('read_fd:', read_fd, os.get_inheritable(read_fd)) print('write_fd:', write_fd, os.get_inheritable(write_fd)) child_pid = os.fork() if child_pid == 0: os.close(read_fd) with open(write_fd, "w") as write_end: write_end.write("hello from your child!") else: os.close(write_fd) os.waitpid(child_pid, 0) with open(read_fd) as read_end: message = read_end.read() print("received from child:", message)
def test_fd_non_inheritable(self): devpoll = select.devpoll() self.addCleanup(devpoll.close) self.assertEqual(os.get_inheritable(devpoll.fileno()), False)
def _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session): """Execute program (POSIX version)""" if PY3 and isinstance(args, (str, bytes)): args = [args] elif not PY3 and isinstance(args, string_types): args = [args] else: args = list(args) if shell: args = ["/bin/sh", "-c"] + args if executable: args[0] = executable if executable is None: executable = args[0] self._loop.install_sigchld() # For transferring possible exec failure from child to parent # The first char specifies the exception type: 0 means # OSError, 1 means some other error. errpipe_read, errpipe_write = self.pipe_cloexec() # errpipe_write must not be in the standard io 0, 1, or 2 fd range. low_fds_to_close = [] while errpipe_write < 3: low_fds_to_close.append(errpipe_write) errpipe_write = os.dup(errpipe_write) for low_fd in low_fds_to_close: os.close(low_fd) try: try: gc_was_enabled = gc.isenabled() # Disable gc to avoid bug where gc -> file_dealloc -> # write to stderr -> hang. http://bugs.python.org/issue1336 gc.disable() try: self.pid = fork() except: if gc_was_enabled: gc.enable() raise if self.pid == 0: # Child try: # Close parent's pipe ends if p2cwrite is not None: os.close(p2cwrite) if c2pread is not None: os.close(c2pread) if errread is not None: os.close(errread) os.close(errpipe_read) # 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). if c2pwrite == 0: c2pwrite = os.dup(c2pwrite) if errwrite == 0 or errwrite == 1: errwrite = os.dup(errwrite) # Dup fds for child def _dup2(a, b): # dup2() removes the CLOEXEC flag but # we must do it ourselves if dup2() # would be a no-op (issue #10806). if a == b: self._set_cloexec_flag(a, False) elif a is not None: os.dup2(a, b) self._remove_nonblock_flag(b) _dup2(p2cread, 0) _dup2(c2pwrite, 1) _dup2(errwrite, 2) # Close pipe fds. Make sure we don't close the # same fd more than once, or standard fds. closed = set([None]) for fd in [p2cread, c2pwrite, errwrite]: if fd not in closed and fd > 2: os.close(fd) closed.add(fd) if cwd is not None: os.chdir(cwd) if preexec_fn: preexec_fn() # Close all other fds, if asked for. This must be done # after preexec_fn runs. if close_fds: fds_to_keep = set(pass_fds) fds_to_keep.add(errpipe_write) self._close_fds(fds_to_keep) elif hasattr(os, 'get_inheritable'): # close_fds was false, and we're on # Python 3.4 or newer, so "all file # descriptors except standard streams # are closed, and inheritable handles # are only inherited if the close_fds # parameter is False." for i in xrange(3, MAXFD): try: if i == errpipe_write or os.get_inheritable( i): continue os.close(i) except: pass if restore_signals: # restore the documented signals back to sig_dfl; # not all will be defined on every platform for sig in 'SIGPIPE', 'SIGXFZ', 'SIGXFSZ': sig = getattr(signal, sig, None) if sig is not None: signal.signal(sig, signal.SIG_DFL) if start_new_session: os.setsid() if env is None: os.execvp(executable, args) else: os.execvpe(executable, args, env) except: exc_type, exc_value, tb = sys.exc_info() # Save the traceback and attach it to the exception object exc_lines = traceback.format_exception( exc_type, exc_value, tb) exc_value.child_traceback = ''.join(exc_lines) os.write(errpipe_write, pickle.dumps(exc_value)) finally: # Make sure that the process exits no matter what. # The return code does not matter much as it won't be # reported to the application os._exit(1) # Parent self._watcher = self._loop.child(self.pid) self._watcher.start(self._on_child, self._watcher) if gc_was_enabled: gc.enable() finally: # be sure the FD is closed no matter what os.close(errpipe_write) # self._devnull is not always defined. devnull_fd = getattr(self, '_devnull', None) if p2cread is not None and p2cwrite is not None and p2cread != devnull_fd: os.close(p2cread) if c2pwrite is not None and c2pread is not None and c2pwrite != devnull_fd: os.close(c2pwrite) if errwrite is not None and errread is not None and errwrite != devnull_fd: os.close(errwrite) if devnull_fd is not None: os.close(devnull_fd) # Prevent a double close of these fds from __init__ on error. self._closed_child_pipe_fds = True # Wait for exec to fail or succeed; possibly raising exception errpipe_read = FileObject(errpipe_read, 'rb') data = errpipe_read.read() finally: if hasattr(errpipe_read, 'close'): errpipe_read.close() else: os.close(errpipe_read) if data != b"": self.wait() child_exception = pickle.loads(data) for fd in (p2cwrite, c2pread, errread): if fd is not None: os.close(fd) raise child_exception
assert isinstance(a, int) assert isinstance(b, int) assert isinstance(os.ttyname(b), str) assert_raises(OSError, lambda: os.ttyname(9999)) os.close(b) os.close(a) # os.get_blocking, os.set_blocking # TODO: windows support should be added for below functions # os.pipe, # os.set_inheritable, os.get_inheritable, rfd, wfd = os.pipe() try: os.write(wfd, CONTENT2) assert os.read(rfd, len(CONTENT2)) == CONTENT2 assert not os.get_inheritable(rfd) assert not os.get_inheritable(wfd) os.set_inheritable(rfd, True) os.set_inheritable(wfd, True) assert os.get_inheritable(rfd) assert os.get_inheritable(wfd) os.set_inheritable(rfd, True) os.set_inheritable(wfd, True) os.set_inheritable(rfd, True) os.set_inheritable(wfd, True) assert os.get_inheritable(rfd) assert os.get_inheritable(wfd) assert os.get_blocking(rfd) assert os.get_blocking(wfd) os.set_blocking(rfd, False)