diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/_pyio.py | 2 | ||||
-rw-r--r-- | Lib/multiprocessing/connection.py | 6 | ||||
-rw-r--r-- | Lib/multiprocessing/forkserver.py | 4 | ||||
-rw-r--r-- | Lib/multiprocessing/popen_fork.py | 2 | ||||
-rw-r--r-- | Lib/multiprocessing/popen_spawn_posix.py | 4 | ||||
-rw-r--r-- | Lib/multiprocessing/semaphore_tracker.py | 2 | ||||
-rw-r--r-- | Lib/multiprocessing/util.py | 8 | ||||
-rw-r--r-- | Lib/socket.py | 17 | ||||
-rw-r--r-- | Lib/subprocess.py | 9 | ||||
-rw-r--r-- | Lib/tempfile.py | 26 | ||||
-rw-r--r-- | Lib/test/subprocessdata/inherited.py | 22 | ||||
-rw-r--r-- | Lib/test/test_asyncore.py | 7 | ||||
-rw-r--r-- | Lib/test/test_builtin.py | 5 | ||||
-rw-r--r-- | Lib/test/test_devpoll.py | 11 | ||||
-rw-r--r-- | Lib/test/test_epoll.py | 10 | ||||
-rw-r--r-- | Lib/test/test_kqueue.py | 5 | ||||
-rw-r--r-- | Lib/test/test_os.py | 67 | ||||
-rw-r--r-- | Lib/test/test_socket.py | 54 | ||||
-rw-r--r-- | Lib/test/test_subprocess.py | 73 | ||||
-rw-r--r-- | Lib/test/test_tempfile.py | 1 | ||||
-rw-r--r-- | Lib/xmlrpc/server.py | 7 |
21 files changed, 260 insertions, 82 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 137ebcb..7ce86a4 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -129,6 +129,8 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, be kept open when the file is closed. This does not work when a file name is given and must be True in that case. + The newly created file is non-inheritable. + A custom opener can be used by passing a callable as *opener*. The underlying file descriptor for the file object is then obtained by calling *opener* with (*file*, *flags*). *opener* must return an open file diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index 443fa7e..9fbe46d 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -509,7 +509,7 @@ if sys.platform != 'win32': c1 = Connection(s1.detach()) c2 = Connection(s2.detach()) else: - fd1, fd2 = util.pipe() + fd1, fd2 = os.pipe() c1 = Connection(fd1, writable=False) c2 = Connection(fd2, readable=False) @@ -536,7 +536,9 @@ else: _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE, _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE | _winapi.PIPE_WAIT, - 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL + 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, + # default security descriptor: the handle cannot be inherited + _winapi.NULL ) h2 = _winapi.CreateFile( address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING, diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index 208bd4e..11df382 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -60,8 +60,8 @@ def connect_to_new_process(fds): raise ValueError('too many fds') with socket.socket(socket.AF_UNIX) as client: client.connect(_forkserver_address) - parent_r, child_w = util.pipe() - child_r, parent_w = util.pipe() + parent_r, child_w = os.pipe() + child_r, parent_w = os.pipe() allfds = [child_r, child_w, _forkserver_alive_fd, semaphore_tracker._semaphore_tracker_fd] allfds += fds diff --git a/Lib/multiprocessing/popen_fork.py b/Lib/multiprocessing/popen_fork.py index fd25ddc..c9f3aae 100644 --- a/Lib/multiprocessing/popen_fork.py +++ b/Lib/multiprocessing/popen_fork.py @@ -66,7 +66,7 @@ class Popen(object): def _launch(self, process_obj): code = 1 - parent_r, child_w = util.pipe() + parent_r, child_w = os.pipe() self.pid = os.fork() if self.pid == 0: try: diff --git a/Lib/multiprocessing/popen_spawn_posix.py b/Lib/multiprocessing/popen_spawn_posix.py index e67915d..751bf22 100644 --- a/Lib/multiprocessing/popen_spawn_posix.py +++ b/Lib/multiprocessing/popen_spawn_posix.py @@ -54,8 +54,8 @@ class Popen(popen_fork.Popen): parent_r = child_w = child_r = parent_w = None try: - parent_r, child_w = util.pipe() - child_r, parent_w = util.pipe() + parent_r, child_w = os.pipe() + child_r, parent_w = os.pipe() cmd = spawn.get_command_line(tracker_fd=tracker_fd, pipe_handle=child_r) self._fds.extend([child_r, child_w]) diff --git a/Lib/multiprocessing/semaphore_tracker.py b/Lib/multiprocessing/semaphore_tracker.py index 99a0dd4..6c9e4a5 100644 --- a/Lib/multiprocessing/semaphore_tracker.py +++ b/Lib/multiprocessing/semaphore_tracker.py @@ -45,7 +45,7 @@ def ensure_running(): except Exception: pass cmd = 'from multiprocessing.semaphore_tracker import main; main(%d)' - r, w = util.pipe() + r, w = os.pipe() try: fds_to_pass.append(r) # process will out live us, so no need to wait on pid diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py index d9e4799..ac8e913 100644 --- a/Lib/multiprocessing/util.py +++ b/Lib/multiprocessing/util.py @@ -365,7 +365,7 @@ def spawnv_passfds(path, args, passfds): if flag & fcntl.FD_CLOEXEC: fcntl.fcntl(fd, fcntl.F_SETFD, flag & ~fcntl.FD_CLOEXEC) tmp.append((fd, flag)) - errpipe_read, errpipe_write = _posixsubprocess.cloexec_pipe() + errpipe_read, errpipe_write = os.pipe() try: return _posixsubprocess.fork_exec( args, [os.fsencode(path)], True, passfds, None, None, @@ -381,7 +381,9 @@ def spawnv_passfds(path, args, passfds): # # Return pipe with CLOEXEC set on fds # +# Deprecated: os.pipe() creates non-inheritable file descriptors +# since Python 3.4 +# def pipe(): - import _posixsubprocess - return _posixsubprocess.cloexec_pipe() + return os.pipe() diff --git a/Lib/socket.py b/Lib/socket.py index 96f8ed0..81864f5 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -137,7 +137,8 @@ class socket(_socket.socket): def dup(self): """dup() -> socket object - Return a new socket object connected to the same system resource. + Duplicate the socket. Return a new socket object connected to the same + system resource. The new socket is non-inheritable. """ fd = dup(self.fileno()) sock = self.__class__(self.family, self.type, self.proto, fileno=fd) @@ -229,6 +230,20 @@ class socket(_socket.socket): self._closed = True return super().detach() + if os.name == 'nt': + def get_inheritable(self): + return os.get_handle_inheritable(self.fileno()) + def set_inheritable(self, inheritable): + os.set_handle_inheritable(self.fileno(), inheritable) + else: + def get_inheritable(self): + return os.get_inheritable(self.fileno()) + def set_inheritable(self, inheritable): + os.set_inheritable(self.fileno(), inheritable) + get_inheritable.__doc__ = "Get the inheritable flag of the socket" + set_inheritable.__doc__ = "Set the inheritable flag of the socket" + + def fromfd(fd, family, type, proto=0): """ fromfd(fd, family, type[, proto]) -> socket object diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 6ebcc25..4ce3c92 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -405,7 +405,6 @@ else: import select _has_poll = hasattr(select, 'poll') import _posixsubprocess - _create_pipe = _posixsubprocess.cloexec_pipe # When select or poll has indicated that the file is writable, # we can write up to _PIPE_BUF bytes without risk of blocking. @@ -1258,7 +1257,7 @@ class Popen(object): if stdin is None: pass elif stdin == PIPE: - p2cread, p2cwrite = _create_pipe() + p2cread, p2cwrite = os.pipe() elif stdin == DEVNULL: p2cread = self._get_devnull() elif isinstance(stdin, int): @@ -1270,7 +1269,7 @@ class Popen(object): if stdout is None: pass elif stdout == PIPE: - c2pread, c2pwrite = _create_pipe() + c2pread, c2pwrite = os.pipe() elif stdout == DEVNULL: c2pwrite = self._get_devnull() elif isinstance(stdout, int): @@ -1282,7 +1281,7 @@ class Popen(object): if stderr is None: pass elif stderr == PIPE: - errread, errwrite = _create_pipe() + errread, errwrite = os.pipe() elif stderr == STDOUT: errwrite = c2pwrite elif stderr == DEVNULL: @@ -1334,7 +1333,7 @@ class Popen(object): # For transferring possible exec failure from child to parent. # Data format: "exception name:hex errno:description" # Pickle is not used; it is complex and involves memory allocation. - errpipe_read, errpipe_write = _create_pipe() + errpipe_read, errpipe_write = os.pipe() try: try: # We must avoid complex work that could involve diff --git a/Lib/tempfile.py b/Lib/tempfile.py index df73289..1eff59c 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -35,33 +35,12 @@ import errno as _errno from random import Random as _Random try: - import fcntl as _fcntl -except ImportError: - def _set_cloexec(fd): - pass -else: - def _set_cloexec(fd): - try: - flags = _fcntl.fcntl(fd, _fcntl.F_GETFD, 0) - except OSError: - pass - else: - # flags read successfully, modify - flags |= _fcntl.FD_CLOEXEC - _fcntl.fcntl(fd, _fcntl.F_SETFD, flags) - - -try: import _thread except ImportError: import _dummy_thread as _thread _allocate_lock = _thread.allocate_lock _text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL -if hasattr(_os, 'O_CLOEXEC'): - _text_openflags |= _os.O_CLOEXEC -if hasattr(_os, 'O_NOINHERIT'): - _text_openflags |= _os.O_NOINHERIT if hasattr(_os, 'O_NOFOLLOW'): _text_openflags |= _os.O_NOFOLLOW @@ -90,8 +69,8 @@ else: # Fallback. All we need is something that raises OSError if the # file doesn't exist. def _stat(fn): - f = open(fn) - f.close() + fd = _os.open(fn, _os.O_RDONLY) + os.close(fd) def _exists(fn): try: @@ -217,7 +196,6 @@ def _mkstemp_inner(dir, pre, suf, flags): file = _os.path.join(dir, pre + name + suf) try: fd = _os.open(file, flags, 0o600) - _set_cloexec(fd) return (fd, _os.path.abspath(file)) except FileExistsError: continue # try again diff --git a/Lib/test/subprocessdata/inherited.py b/Lib/test/subprocessdata/inherited.py new file mode 100644 index 0000000..1aac041 --- /dev/null +++ b/Lib/test/subprocessdata/inherited.py @@ -0,0 +1,22 @@ +"""Similar to fd_status.py, but only checks file descriptors passed on the +command line.""" + +import errno +import os +import sys +import stat + +if __name__ == "__main__": + fds = map(int, sys.argv[1:]) + inherited = [] + for fd in fds: + try: + st = os.fstat(fd) + except OSError as e: + if e.errno == errno.EBADF: + continue + raise + # Ignore Solaris door files + if not stat.S_ISDOOR(st.st_mode): + inherited.append(fd) + print(','.join(map(str, inherited))) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 87655f4..084d247 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -744,7 +744,12 @@ class BaseTestAPI: s.create_socket(self.family) self.assertEqual(s.socket.family, self.family) SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0) - self.assertEqual(s.socket.type, socket.SOCK_STREAM | SOCK_NONBLOCK) + sock_type = socket.SOCK_STREAM | SOCK_NONBLOCK + if hasattr(socket, 'SOCK_CLOEXEC'): + self.assertIn(s.socket.type, + (sock_type | socket.SOCK_CLOEXEC, sock_type)) + else: + self.assertEqual(s.socket.type, sock_type) def test_bind(self): if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX: diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 8d3271f..2411c9b 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1015,6 +1015,11 @@ class BuiltinTest(unittest.TestCase): os.environ.clear() os.environ.update(old_environ) + def test_open_non_inheritable(self): + fileobj = open(__file__) + with fileobj: + self.assertFalse(os.get_inheritable(fileobj.fileno())) + def test_ord(self): self.assertEqual(ord(' '), 32) self.assertEqual(ord('A'), 65) diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py index 167e0ee..40ebeee 100644 --- a/Lib/test/test_devpoll.py +++ b/Lib/test/test_devpoll.py @@ -2,7 +2,11 @@ # Initial tests are copied as is from "test_poll.py" -import os, select, random, unittest, sys +import os +import random +import select +import sys +import unittest from test.support import TESTFN, run_unittest try: @@ -111,6 +115,11 @@ class DevPollTests(unittest.TestCase): self.assertRaises(ValueError, devpoll.register, fd, fd, select.POLLIN) self.assertRaises(ValueError, devpoll.unregister, fd) + def test_fd_non_inheritable(self): + devpoll = select.devpoll() + self.addCleanup(devpoll.close) + self.assertEqual(os.get_inheritable(devpoll.fileno()), False) + def test_main(): run_unittest(DevPollTests) diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index 93a9e1d..6459fba 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -21,10 +21,11 @@ """ Tests for epoll wrapper. """ -import socket import errno -import time +import os import select +import socket +import time import unittest from test import support @@ -249,6 +250,11 @@ class TestEPoll(unittest.TestCase): self.assertRaises(ValueError, epoll.register, fd, select.EPOLLIN) self.assertRaises(ValueError, epoll.unregister, fd) + def test_fd_non_inheritable(self): + epoll = select.epoll() + self.addCleanup(epoll.close) + self.assertEqual(os.get_inheritable(epoll.fileno()), False) + def test_main(): support.run_unittest(TestEPoll) diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index 930f088..bafdeba 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -206,6 +206,11 @@ class TestKQueue(unittest.TestCase): # operations must fail with ValueError("I/O operation on closed ...") self.assertRaises(ValueError, kqueue.control, None, 4) + def test_fd_non_inheritable(self): + kqueue = select.kqueue() + self.addCleanup(kqueue.close) + self.assertEqual(os.get_inheritable(kqueue.fileno()), False) + def test_main(): support.run_unittest(TestKQueue) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 1d41b77..d0dd364 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2298,6 +2298,72 @@ class CPUCountTests(unittest.TestCase): else: self.skipTest("Could not determine the number of CPUs") + +class FDInheritanceTests(unittest.TestCase): + def test_get_inheritable(self): + fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd) + for inheritable in (False, True): + os.set_inheritable(fd, inheritable) + self.assertEqual(os.get_inheritable(fd), inheritable) + + def test_set_inheritable(self): + fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd) + os.set_inheritable(fd, True) + self.assertEqual(os.get_inheritable(fd), True) + + def test_open(self): + fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd) + self.assertEqual(os.get_inheritable(fd), False) + + @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()") + def test_pipe(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.addCleanup(os.close, wfd) + self.assertEqual(os.get_inheritable(rfd), False) + self.assertEqual(os.get_inheritable(wfd), False) + + def test_dup(self): + fd1 = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd1) + + fd2 = os.dup(fd1) + self.addCleanup(os.close, fd2) + self.assertEqual(os.get_inheritable(fd2), False) + + @unittest.skipUnless(hasattr(os, 'dup2'), "need os.dup2()") + def test_dup2(self): + fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd) + + # inheritable by default + fd2 = os.open(__file__, os.O_RDONLY) + try: + os.dup2(fd, fd2) + self.assertEqual(os.get_inheritable(fd2), True) + finally: + os.close(fd2) + + # force non-inheritable + fd3 = os.open(__file__, os.O_RDONLY) + try: + os.dup2(fd, fd3, inheritable=False) + self.assertEqual(os.get_inheritable(fd3), False) + finally: + os.close(fd3) + + @unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") + def test_openpty(self): + master_fd, slave_fd = os.openpty() + self.addCleanup(os.close, master_fd) + self.addCleanup(os.close, slave_fd) + self.assertEqual(os.get_inheritable(master_fd), False) + self.assertEqual(os.get_inheritable(slave_fd), False) + + @support.reap_threads def test_main(): support.run_unittest( @@ -2330,6 +2396,7 @@ def test_main(): OSErrorTests, RemoveDirsTests, CPUCountTests, + FDInheritanceTests, ) if __name__ == "__main__": diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index c7a52d8..c7b9ba8 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -23,10 +23,6 @@ import math import pickle import struct try: - import fcntl -except ImportError: - fcntl = False -try: import multiprocessing except ImportError: multiprocessing = False @@ -1108,9 +1104,15 @@ class GeneralModuleTests(unittest.TestCase): def testNewAttributes(self): # testing .family, .type and .protocol + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.assertEqual(sock.family, socket.AF_INET) - self.assertEqual(sock.type, socket.SOCK_STREAM) + if hasattr(socket, 'SOCK_CLOEXEC'): + self.assertIn(sock.type, + (socket.SOCK_STREAM | socket.SOCK_CLOEXEC, + socket.SOCK_STREAM)) + else: + self.assertEqual(sock.type, socket.SOCK_STREAM) self.assertEqual(sock.proto, 0) sock.close() @@ -4749,16 +4751,46 @@ class ContextManagersTest(ThreadedTCPSocketTest): self.assertRaises(OSError, sock.sendall, b'foo') -@unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"), - "SOCK_CLOEXEC not defined") -@unittest.skipUnless(fcntl, "module fcntl not available") -class CloexecConstantTest(unittest.TestCase): +class InheritanceTest(unittest.TestCase): + @unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"), + "SOCK_CLOEXEC not defined") @support.requires_linux_version(2, 6, 28) def test_SOCK_CLOEXEC(self): with socket.socket(socket.AF_INET, socket.SOCK_STREAM | socket.SOCK_CLOEXEC) as s: self.assertTrue(s.type & socket.SOCK_CLOEXEC) - self.assertTrue(fcntl.fcntl(s, fcntl.F_GETFD) & fcntl.FD_CLOEXEC) + self.assertTrue(sock.get_inheritable()) + + def test_default_inheritable(self): + sock = socket.socket() + with sock: + self.assertEqual(sock.get_inheritable(), False) + + def test_dup(self): + sock = socket.socket() + with sock: + newsock = sock.dup() + sock.close() + with newsock: + self.assertEqual(newsock.get_inheritable(), False) + + def test_set_inheritable(self): + sock = socket.socket() + with sock: + sock.set_inheritable(True) + self.assertEqual(sock.get_inheritable(), True) + + sock.set_inheritable(False) + self.assertEqual(sock.get_inheritable(), False) + + @unittest.skipUnless(hasattr(socket, "socketpair"), + "need socket.socketpair()") + def test_socketpair(self): + s1, s2 = socket.socketpair() + self.addCleanup(s1.close) + self.addCleanup(s2.close) + self.assertEqual(s1.get_inheritable(), False) + self.assertEqual(s2.get_inheritable(), False) @unittest.skipUnless(hasattr(socket, "SOCK_NONBLOCK"), @@ -4927,7 +4959,7 @@ def test_main(): NetworkConnectionAttributesTest, NetworkConnectionBehaviourTest, ContextManagersTest, - CloexecConstantTest, + InheritanceTest, NonblockConstantTest ]) if hasattr(socket, "socketpair"): diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 8a3ac4b..8cd2d1e 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1501,16 +1501,28 @@ class POSIXProcessTestCase(BaseTestCase): # Terminating a dead process self._kill_dead_process('terminate') + def _save_fds(self, save_fds): + fds = [] + for fd in save_fds: + inheritable = os.get_inheritable(fd) + saved = os.dup(fd) + fds.append((fd, saved, inheritable)) + return fds + + def _restore_fds(self, fds): + for fd, saved, inheritable in fds: + os.dup2(saved, fd, inheritable=inheritable) + os.close(saved) + def check_close_std_fds(self, fds): # Issue #9905: test that subprocess pipes still work properly with # some standard fds closed stdin = 0 - newfds = [] - for a in fds: - b = os.dup(a) - newfds.append(b) - if a == 0: - stdin = b + saved_fds = self._save_fds(fds) + for fd, saved, inheritable in saved_fds: + if fd == 0: + stdin = saved + break try: for fd in fds: os.close(fd) @@ -1525,10 +1537,7 @@ class POSIXProcessTestCase(BaseTestCase): err = support.strip_python_stderr(err) self.assertEqual((out, err), (b'apple', b'orange')) finally: - for b, a in zip(newfds, fds): - os.dup2(b, a) - for b in newfds: - os.close(b) + self._restore_fds(saved_fds) def test_close_fd_0(self): self.check_close_std_fds([0]) @@ -1568,7 +1577,7 @@ class POSIXProcessTestCase(BaseTestCase): os.lseek(temp_fds[1], 0, 0) # move the standard file descriptors out of the way - saved_fds = [os.dup(fd) for fd in range(3)] + saved_fds = self._save_fds(range(3)) try: # duplicate the file objects over the standard fd's for fd, temp_fd in enumerate(temp_fds): @@ -1584,10 +1593,7 @@ class POSIXProcessTestCase(BaseTestCase): stderr=temp_fds[0]) p.wait() finally: - # restore the original fd's underneath sys.stdin, etc. - for std, saved in enumerate(saved_fds): - os.dup2(saved, std) - os.close(saved) + self._restore_fds(saved_fds) for fd in temp_fds: os.lseek(fd, 0, 0) @@ -1611,7 +1617,7 @@ class POSIXProcessTestCase(BaseTestCase): os.unlink(fname) # save a copy of the standard file descriptors - saved_fds = [os.dup(fd) for fd in range(3)] + saved_fds = self._save_fds(range(3)) try: # duplicate the temp files over the standard fd's 0, 1, 2 for fd, temp_fd in enumerate(temp_fds): @@ -1637,9 +1643,7 @@ class POSIXProcessTestCase(BaseTestCase): out = os.read(stdout_no, 1024) err = support.strip_python_stderr(os.read(stderr_no, 1024)) finally: - for std, saved in enumerate(saved_fds): - os.dup2(saved, std) - os.close(saved) + self._restore_fds(saved_fds) self.assertEqual(out, b"got STDIN") self.assertEqual(err, b"err") @@ -1810,6 +1814,9 @@ class POSIXProcessTestCase(BaseTestCase): self.addCleanup(os.close, fd) open_fds.add(fd) + for fd in open_fds: + os.set_inheritable(fd, True) + p = subprocess.Popen([sys.executable, fd_status], stdout=subprocess.PIPE, close_fds=False) output, ignored = p.communicate() @@ -1854,6 +1861,8 @@ class POSIXProcessTestCase(BaseTestCase): fds = os.pipe() self.addCleanup(os.close, fds[0]) self.addCleanup(os.close, fds[1]) + os.set_inheritable(fds[0], True) + os.set_inheritable(fds[1], True) open_fds.update(fds) for fd in open_fds: @@ -1876,6 +1885,32 @@ class POSIXProcessTestCase(BaseTestCase): close_fds=False, pass_fds=(fd, ))) self.assertIn('overriding close_fds', str(context.warning)) + def test_pass_fds_inheritable(self): + script = support.findfile("inherited.py", subdir="subprocessdata") + + inheritable, non_inheritable = os.pipe() + self.addCleanup(os.close, inheritable) + self.addCleanup(os.close, non_inheritable) + os.set_inheritable(inheritable, True) + os.set_inheritable(non_inheritable, False) + pass_fds = (inheritable, non_inheritable) + args = [sys.executable, script] + args += list(map(str, pass_fds)) + + p = subprocess.Popen(args, + stdout=subprocess.PIPE, close_fds=True, + pass_fds=pass_fds) + output, ignored = p.communicate() + fds = set(map(int, output.split(b','))) + + # the inheritable file descriptor must be inherited, so its inheritable + # flag must be set in the child process after fork() and before exec() + self.assertEqual(fds, set(pass_fds)) + + # inheritable flag must not be changed in the parent process + self.assertEqual(os.get_inheritable(inheritable), True) + self.assertEqual(os.get_inheritable(non_inheritable), False) + def test_stdout_stdin_are_single_inout_fd(self): with io.open(os.devnull, "r+") as inout: p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"], diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 6b146d2..493c640 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -333,6 +333,7 @@ class TestMkstempInner(BaseTestCase): v="q" file = self.do_create() + self.assertEqual(os.get_inheritable(file.fd), False) fd = "%d" % file.fd try: diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index 78ca4e0..d914282 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -584,13 +584,6 @@ class SimpleXMLRPCServer(socketserver.TCPServer, SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) - # [Bug #1222790] If possible, set close-on-exec flag; if a - # method spawns a subprocess, the subprocess shouldn't have - # the listening socket open. - if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): - flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) - flags |= fcntl.FD_CLOEXEC - fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) class MultiPathXMLRPCServer(SimpleXMLRPCServer): """Multipath XML-RPC Server |