summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_pyio.py2
-rw-r--r--Lib/multiprocessing/connection.py6
-rw-r--r--Lib/multiprocessing/forkserver.py4
-rw-r--r--Lib/multiprocessing/popen_fork.py2
-rw-r--r--Lib/multiprocessing/popen_spawn_posix.py4
-rw-r--r--Lib/multiprocessing/semaphore_tracker.py2
-rw-r--r--Lib/multiprocessing/util.py8
-rw-r--r--Lib/socket.py17
-rw-r--r--Lib/subprocess.py9
-rw-r--r--Lib/tempfile.py26
-rw-r--r--Lib/test/subprocessdata/inherited.py22
-rw-r--r--Lib/test/test_asyncore.py7
-rw-r--r--Lib/test/test_builtin.py5
-rw-r--r--Lib/test/test_devpoll.py11
-rw-r--r--Lib/test/test_epoll.py10
-rw-r--r--Lib/test/test_kqueue.py5
-rw-r--r--Lib/test/test_os.py67
-rw-r--r--Lib/test/test_socket.py54
-rw-r--r--Lib/test/test_subprocess.py73
-rw-r--r--Lib/test/test_tempfile.py1
-rw-r--r--Lib/xmlrpc/server.py7
21 files changed, 260 insertions, 82 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 137ebcbd82..7ce86a46e3 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 443fa7e138..9fbe46deff 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 208bd4e31d..11df38285c 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 fd25ddc56d..c9f3aaecb6 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 e67915d9c9..751bf22038 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 99a0dd4f8f..6c9e4a57e2 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 d9e4799d0a..ac8e9136c1 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 96f8ed0c7f..81864f5113 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 6ebcc25abe..4ce3c929d6 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 df7328984a..1eff59cca4 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 0000000000..1aac041b34
--- /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 87655f4495..084d247295 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 8d3271f51e..2411c9b311 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 167e0ee4bf..40ebeee597 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 93a9e1d320..6459fbafc8 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 930f088d16..bafdeba6ff 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 1d41b774d6..d0dd364f89 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 c7a52d87a9..c7b9ba8dd7 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 8a3ac4bae7..8cd2d1e07c 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 6b146d23c9..493c640801 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 78ca4e0a4a..d914282917 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