diff -r ac6dab1819c4 -r 17211acb65b1 Doc/library/functions.rst --- a/Doc/library/functions.rst Thu Aug 22 08:20:31 2013 -0700 +++ b/Doc/library/functions.rst Fri Aug 23 00:22:53 2013 +0200 @@ -979,6 +979,8 @@ :mod:`os.open` as *opener* results in functionality similar to passing ``None``). + The newly created file is :ref:`non-inheritable `. + The following example uses the :ref:`dir_fd ` parameter of the :func:`os.open` function to open a file relative to a given directory:: @@ -992,10 +994,6 @@ ... >>> os.close(dir_fd) # don't leak a file descriptor - .. versionchanged:: 3.3 - The *opener* parameter was added. - The ``'x'`` mode was added. - The type of :term:`file object` returned by the :func:`open` function depends on the mode. When :func:`open` is used to open a file in a text mode (``'w'``, ``'r'``, ``'wt'``, ``'rt'``, etc.), it returns a subclass of @@ -1022,10 +1020,15 @@ and :mod:`shutil`. .. versionchanged:: 3.3 + The *opener* parameter was added. + The ``'x'`` mode was added. :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`. :exc:`FileExistsError` is now raised if the file opened in exclusive creation mode (``'x'``) already exists. + .. versionchanged:: 3.4 + The file is now non-inheritable. + .. XXX works for bytes too, but should it? .. function:: ord(c) diff -r ac6dab1819c4 -r 17211acb65b1 Doc/library/io.rst --- a/Doc/library/io.rst Thu Aug 22 08:20:31 2013 -0700 +++ b/Doc/library/io.rst Fri Aug 23 00:22:53 2013 +0200 @@ -522,6 +522,8 @@ :mod:`os.open` as *opener* results in functionality similar to passing ``None``). + The newly created file is :ref:`non-inheritable `. + See the :func:`open` built-in function for examples on using the *opener* parameter. @@ -529,6 +531,9 @@ The *opener* parameter was added. The ``'x'`` mode was added. + .. versionchanged:: 3.4 + The file is now non-inheritable. + In addition to the attributes and methods from :class:`IOBase` and :class:`RawIOBase`, :class:`FileIO` provides the following data attributes: diff -r ac6dab1819c4 -r 17211acb65b1 Doc/library/os.rst --- a/Doc/library/os.rst Thu Aug 22 08:20:31 2013 -0700 +++ b/Doc/library/os.rst Fri Aug 23 00:22:53 2013 +0200 @@ -685,17 +685,30 @@ .. function:: dup(fd) - Return a duplicate of file descriptor *fd*. + Return a duplicate of file descriptor *fd*. The new file descriptor is + :ref:`non-inheritable `. + + On Windows, when duplicating a standard stream (0: stdin, 1: stdout, + 2: stderr), the new file descriptor is :ref:`inheritable + `. Availability: Unix, Windows. - -.. function:: dup2(fd, fd2) + .. versionchanged:: 3.4 + The new file descriptor is now non-inheritable. + + +.. function:: dup2(fd, fd2, inheritable=True) Duplicate file descriptor *fd* to *fd2*, closing the latter first if necessary. + The file descriptor *fd2* is :ref:`inheritable ` by default, + or non-inheritable if *inheritable* is ``False``. Availability: Unix, Windows. + .. versionchanged:: 3.4 + Add the optional *inheritable* parameter. + .. function:: fchmod(fd, mode) @@ -848,6 +861,7 @@ Open the file *file* and set various flags according to *flags* and possibly its mode according to *mode*. When computing *mode*, the current umask value is first masked out. Return the file descriptor for the newly opened file. + The new file descriptor is :ref:`non-inheritable `. For a description of the flag and mode values, see the C run-time documentation; flag constants (like :const:`O_RDONLY` and :const:`O_WRONLY`) are defined in @@ -859,6 +873,9 @@ Availability: Unix, Windows. + .. versionchanged:: 3.4 + The new file descriptor is now non-inheritable. + .. note:: This function is intended for low-level I/O. For normal usage, use the @@ -933,20 +950,28 @@ .. index:: module: pty - Open a new pseudo-terminal pair. Return a pair of file descriptors ``(master, - slave)`` for the pty and the tty, respectively. For a (slightly) more portable - approach, use the :mod:`pty` module. + Open a new pseudo-terminal pair. Return a pair of file descriptors + ``(master, slave)`` for the pty and the tty, respectively. The new file + descriptors are :ref:`non-inheritable `. For a (slightly) more + portable approach, use the :mod:`pty` module. Availability: some flavors of Unix. + .. versionchanged:: 3.4 + The new file descriptors are now non-inheritable. + .. function:: pipe() - Create a pipe. Return a pair of file descriptors ``(r, w)`` usable for reading - and writing, respectively. + Create a pipe. Return a pair of file descriptors ``(r, w)`` usable for + reading and writing, respectively. The new file descriptor are + :ref:`non-inheritable `. Availability: Unix, Windows. + .. versionchanged:: 3.4 + The new file descriptors are now non-inheritable. + .. function:: pipe2(flags) @@ -1178,6 +1203,50 @@ Height of the terminal window in characters. +.. _fd_inheritance: + +Inheritance of File Descriptors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A file descriptor has a inheritable flag which indicates if the file descriptor +can be inherited or not in child processes. Since Python 3.4, file descriptors +created by Python are non-inheritable by default. + +On UNIX, non-inheritable file descriptors are closed in child processes at the +execution of a new program, other file descriptors are inherited. + +On Windows, non-inheritable handles and file descriptors are closed in child +processes, except standard streams (file descriptors 0, 1 and 2: stdin, stdout +and stderr) which are always inherited. Using :func:`os.spawn*` functions, +all inheritable handles and all inheritable file descriptors are inherited. +Using the :mod:`subprocess` module, all file descriptors except standard +streams are closed, inheritable handles are only inherited if the *close_fds* +parameter is ``False``. + +.. versionadded:: 3.4 + +.. function:: get_inheritable(fd) + + Get the inheritable flag of the specified file descriptor. Return a + :class:`bool`. + +.. function:: set_inheritable(fd, inheritable) + + Set the inheritable flag of the specified file descriptor. + +.. function:: get_handle_inheritable(handle) + + Get the inheritable flag of the specified handle. Return a :class:`bool`. + + Availability: Windows. + +.. function:: set_handle_inheritable(handle, inheritable) + + Set the inheritable flag of the specified handle. + + Availability: Windows. + + .. _os-file-dir: Files and Directories diff -r ac6dab1819c4 -r 17211acb65b1 Doc/library/select.rst --- a/Doc/library/select.rst Thu Aug 22 08:20:31 2013 -0700 +++ b/Doc/library/select.rst Fri Aug 23 00:22:53 2013 +0200 @@ -37,8 +37,13 @@ increases this value, :c:func:`devpoll` may return an incomplete list of active file descriptors. + The new file descriptor is :ref:`non-inheritable `. + .. versionadded:: 3.3 + .. versionchanged:: 3.4 + The new file descriptor is now non-inheritable. + .. function:: epoll(sizehint=-1, flags=0) (Only supported on Linux 2.5.44 and newer.) Return an edge polling object, @@ -49,11 +54,14 @@ :ref:`epoll-objects` below for the methods supported by epolling objects. They also support the :keyword:`with` statement. + The new file descriptor is :ref:`non-inheritable `. + .. versionchanged:: 3.3 Added the *flags* parameter. .. versionchanged:: 3.4 Support for the :keyword:`with` statement was added. + The new file descriptor is now non-inheritable. .. function:: poll() @@ -69,6 +77,11 @@ (Only supported on BSD.) Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. + The new file descriptor is :ref:`non-inheritable `. + + .. versionchanged:: 3.4 + The new file descriptor is now non-inheritable. + .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) diff -r ac6dab1819c4 -r 17211acb65b1 Doc/library/socket.rst --- a/Doc/library/socket.rst Thu Aug 22 08:20:31 2013 -0700 +++ b/Doc/library/socket.rst Fri Aug 23 00:22:53 2013 +0200 @@ -460,7 +460,7 @@ ``'udp'``, otherwise any protocol will match. -.. function:: socket([family[, type[, proto]]]) +.. function:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) Create a new socket using the given address family, socket type and protocol number. The address family should be :const:`AF_INET` (the default), @@ -471,12 +471,18 @@ case where the address family is :const:`AF_CAN` the protocol should be one of :const:`CAN_RAW` or :const:`CAN_BCM`. + The newly created socket is :ref:`non-inheritable `. + .. versionchanged:: 3.3 The AF_CAN family was added. The AF_RDS family was added. - .. versionchanged:: 3.4 - The CAN_BCM protocol was added. + .. versionchanged:: 3.4 + The CAN_BCM protocol was added. + + .. versionchanged:: 3.4 + The socket is now non-inheritable. + .. function:: socketpair([family[, type[, proto]]]) @@ -486,12 +492,17 @@ if defined on the platform; otherwise, the default is :const:`AF_INET`. Availability: Unix. + The newly created sockets are :ref:`non-inheritable `. + .. versionchanged:: 3.2 The returned socket objects now support the whole socket API, rather than a subset. + .. versionchanged:: 3.4 + The sockets are now non-inheritable. -.. function:: fromfd(fd, family, type[, proto]) + +.. function:: fromfd(fd, family, type, proto=0) Duplicate the file descriptor *fd* (an integer as returned by a file object's :meth:`fileno` method) and build a socket object from the result. Address @@ -502,6 +513,11 @@ a socket passed to a program as standard input or output (such as a server started by the Unix inet daemon). The socket is assumed to be in blocking mode. + The newly created socket is :ref:`non-inheritable `. + + .. versionchanged:: 3.4 + The socket is now non-inheritable. + .. function:: ntohl(x) @@ -730,6 +746,11 @@ *new* socket object usable to send and receive data on the connection, and *address* is the address bound to the socket on the other end of the connection. + The newly created socket is :ref:`non-inheritable `. + + .. versionchanged:: 3.4 + The socket is now non-inheritable. + .. method:: socket.bind(address) @@ -775,6 +796,16 @@ .. versionadded:: 3.2 +.. method:: socket.dup() + + Duplicate the socket. + + The newly created socket is :ref:`non-inheritable `. + + .. versionchanged:: 3.4 + The socket is now non-inheritable. + + .. method:: socket.fileno() Return the socket's file descriptor (a small integer). This is useful with @@ -785,6 +816,15 @@ this limitation. +.. method:: socket.get_inheritable() + + Get the :ref:`inheritable flag ` of the socket's file + descriptor or socket's handle: ``True`` if the socket can be inherited in + child processes, ``False`` if it cannot. + + .. versionadded:: 3.4 + + .. method:: socket.getpeername() Return the remote address to which the socket is connected. This is useful to @@ -1068,6 +1108,14 @@ .. versionadded:: 3.3 +.. method:: socket.set_inheritable(inheritable) + + Set the :ref:`inheritable flag ` of the socket's file + descriptor or socket's handle. + + .. versionadded:: 3.4 + + .. method:: socket.setblocking(flag) Set blocking or non-blocking mode of the socket: if *flag* is false, the diff -r ac6dab1819c4 -r 17211acb65b1 Include/fileutils.h --- a/Include/fileutils.h Thu Aug 22 08:20:31 2013 -0700 +++ b/Include/fileutils.h Fri Aug 23 00:22:53 2013 +0200 @@ -27,11 +27,19 @@ struct stat *statbuf); #endif +PyAPI_FUNC(int) _Py_open( + const char *pathname, + int flags); + PyAPI_FUNC(FILE *) _Py_wfopen( const wchar_t *path, const wchar_t *mode); PyAPI_FUNC(FILE*) _Py_fopen( + const char *pathname, + const char *mode); + +PyAPI_FUNC(FILE*) _Py_fopen_obj( PyObject *path, const char *mode); @@ -53,6 +61,13 @@ wchar_t *buf, size_t size); +PyAPI_FUNC(int) _Py_get_inheritable(int fd); + +PyAPI_FUNC(int) _Py_set_inheritable(int fd, int inheritable, + int *atomic_flag_works); + +PyAPI_FUNC(int) _Py_dup(int fd); + #ifdef __cplusplus } #endif diff -r ac6dab1819c4 -r 17211acb65b1 Lib/_pyio.py --- a/Lib/_pyio.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/_pyio.py Fri Aug 23 00:22:53 2013 +0200 @@ -129,6 +129,8 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/multiprocessing/connection.py Fri Aug 23 00:22:53 2013 +0200 @@ -509,7 +509,7 @@ 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 @@ _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 -r ac6dab1819c4 -r 17211acb65b1 Lib/multiprocessing/forkserver.py --- a/Lib/multiprocessing/forkserver.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/multiprocessing/forkserver.py Fri Aug 23 00:22:53 2013 +0200 @@ -60,8 +60,8 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/multiprocessing/popen_fork.py --- a/Lib/multiprocessing/popen_fork.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/multiprocessing/popen_fork.py Fri Aug 23 00:22:53 2013 +0200 @@ -66,7 +66,7 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/multiprocessing/popen_spawn_posix.py --- a/Lib/multiprocessing/popen_spawn_posix.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/multiprocessing/popen_spawn_posix.py Fri Aug 23 00:22:53 2013 +0200 @@ -54,8 +54,8 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/multiprocessing/semaphore_tracker.py --- a/Lib/multiprocessing/semaphore_tracker.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/multiprocessing/semaphore_tracker.py Fri Aug 23 00:22:53 2013 +0200 @@ -45,7 +45,7 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/multiprocessing/util.py --- a/Lib/multiprocessing/util.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/multiprocessing/util.py Fri Aug 23 00:22:53 2013 +0200 @@ -365,7 +365,7 @@ 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 @@ # # 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/socket.py --- a/Lib/socket.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/socket.py Fri Aug 23 00:22:53 2013 +0200 @@ -137,7 +137,8 @@ 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 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/subprocess.py --- a/Lib/subprocess.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/subprocess.py Fri Aug 23 00:22:53 2013 +0200 @@ -405,7 +405,6 @@ 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 @@ 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 @@ 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 @@ 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 @@ # 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/tempfile.py --- a/Lib/tempfile.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/tempfile.py Fri Aug 23 00:22:53 2013 +0200 @@ -35,33 +35,12 @@ 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 @@ # 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 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/subprocessdata/inherited.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/subprocessdata/inherited.py Fri Aug 23 00:22:53 2013 +0200 @@ -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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_asyncore.py Fri Aug 23 00:22:53 2013 +0200 @@ -744,7 +744,12 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_builtin.py Fri Aug 23 00:22:53 2013 +0200 @@ -1015,6 +1015,11 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_devpoll.py --- a/Lib/test/test_devpoll.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_devpoll.py Fri Aug 23 00:22:53 2013 +0200 @@ -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 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_epoll.py --- a/Lib/test/test_epoll.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_epoll.py Fri Aug 23 00:22:53 2013 +0200 @@ -21,10 +21,11 @@ """ Tests for epoll wrapper. """ +import errno +import os +import select import socket -import errno import time -import select import unittest from test import support @@ -249,6 +250,11 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_kqueue.py --- a/Lib/test/test_kqueue.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_kqueue.py Fri Aug 23 00:22:53 2013 +0200 @@ -206,6 +206,11 @@ # 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_os.py --- a/Lib/test/test_os.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_os.py Fri Aug 23 00:22:53 2013 +0200 @@ -2286,6 +2286,72 @@ 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( @@ -2318,6 +2384,7 @@ OSErrorTests, RemoveDirsTests, CPUCountTests, + FDInheritanceTests, ) if __name__ == "__main__": diff -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_socket.py --- a/Lib/test/test_socket.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_socket.py Fri Aug 23 00:22:53 2013 +0200 @@ -23,10 +23,6 @@ import pickle import struct try: - import fcntl -except ImportError: - fcntl = False -try: import multiprocessing except ImportError: multiprocessing = False @@ -1108,9 +1104,15 @@ 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() @@ -4748,16 +4750,46 @@ 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"), @@ -4926,7 +4958,7 @@ NetworkConnectionAttributesTest, NetworkConnectionBehaviourTest, ContextManagersTest, - CloexecConstantTest, + InheritanceTest, NonblockConstantTest ]) if hasattr(socket, "socketpair"): diff -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_subprocess.py Fri Aug 23 00:22:53 2013 +0200 @@ -1501,16 +1501,28 @@ # 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 -r ac6dab1819c4 -r 17211acb65b1 Lib/test/test_tempfile.py --- a/Lib/test/test_tempfile.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/test/test_tempfile.py Fri Aug 23 00:22:53 2013 +0200 @@ -333,6 +333,7 @@ v="q" file = self.do_create() + self.assertEqual(os.get_inheritable(file.fd), False) fd = "%d" % file.fd try: diff -r ac6dab1819c4 -r 17211acb65b1 Lib/xmlrpc/server.py --- a/Lib/xmlrpc/server.py Thu Aug 22 08:20:31 2013 -0700 +++ b/Lib/xmlrpc/server.py Fri Aug 23 00:22:53 2013 +0200 @@ -584,13 +584,6 @@ 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 diff -r ac6dab1819c4 -r 17211acb65b1 Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/_cursesmodule.c Fri Aug 23 00:22:53 2013 +0200 @@ -1694,26 +1694,24 @@ /* We have to simulate this by writing to a temporary FILE*, then reading back, then writing to the argument stream. */ char fn[100]; - int fd; - FILE *fp; - PyObject *res; + int fd = -1; + FILE *fp = NULL; + PyObject *res = NULL; strcpy(fn, "/tmp/py.curses.putwin.XXXXXX"); fd = mkstemp(fn); if (fd < 0) return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + if (_Py_set_inheritable(fd, 0, NULL) < 0) + goto exit; fp = fdopen(fd, "wb+"); if (fp == NULL) { - close(fd); - remove(fn); - return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto exit; } res = PyCursesCheckERR(putwin(self->win, fp), "putwin"); - if (res == NULL) { - fclose(fp); - remove(fn); - return res; - } + if (res == NULL) + goto exit; fseek(fp, 0, 0); while (1) { char buf[BUFSIZ]; @@ -1727,7 +1725,12 @@ if (res == NULL) break; } - fclose(fp); + +exit: + if (fp != NULL) + fclose(fp); + else if (fd != -1) + close(fd); remove(fn); return res; } @@ -2252,12 +2255,13 @@ PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream) { char fn[100]; - int fd; - FILE *fp; + int fd = -1; + FILE *fp = NULL; PyObject *data; size_t datalen; WINDOW *win; _Py_IDENTIFIER(read); + PyObject *res = NULL; PyCursesInitialised; @@ -2265,44 +2269,47 @@ fd = mkstemp(fn); if (fd < 0) return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + if (_Py_set_inheritable(fd, 0, NULL) < 0) + goto error; fp = fdopen(fd, "wb+"); if (fp == NULL) { - close(fd); - remove(fn); - return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto error; } + data = _PyObject_CallMethodId(stream, &PyId_read, ""); - if (data == NULL) { - fclose(fp); - remove(fn); - return NULL; - } + if (data == NULL) + goto error; if (!PyBytes_Check(data)) { PyErr_Format(PyExc_TypeError, "f.read() returned %.100s instead of bytes", data->ob_type->tp_name); Py_DECREF(data); - fclose(fp); - remove(fn); - return NULL; + goto error; } datalen = PyBytes_GET_SIZE(data); if (fwrite(PyBytes_AS_STRING(data), 1, datalen, fp) != datalen) { Py_DECREF(data); - fclose(fp); - remove(fn); - return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto error; } Py_DECREF(data); + fseek(fp, 0, 0); win = getwin(fp); - fclose(fp); - remove(fn); if (win == NULL) { PyErr_SetString(PyCursesError, catchall_NULL); - return NULL; + goto error; } - return PyCursesWindow_New(win, NULL); + res = PyCursesWindow_New(win, NULL); + +error: + if (fp != NULL) + fclose(fp); + else if (fd != -1) + close(fd); + remove(fn); + return res; } static PyObject * diff -r ac6dab1819c4 -r 17211acb65b1 Modules/_io/fileio.c --- a/Modules/_io/fileio.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/_io/fileio.c Fri Aug 23 00:22:53 2013 +0200 @@ -202,6 +202,9 @@ return 0; } +#ifdef O_CLOEXEC +extern int _Py_open_cloexec_works; +#endif static int fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) @@ -221,6 +224,11 @@ int fd = -1; int closefd = 1; int fd_is_own = 0; +#ifdef O_CLOEXEC + int *atomic_flag_works = &_Py_open_cloexec_works; +#elif !defined(MS_WINDOWS) + int *atomic_flag_works = NULL; +#endif assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -345,6 +353,11 @@ if (append) flags |= O_APPEND; #endif +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif if (fd >= 0) { if (check_fd(fd)) @@ -369,10 +382,18 @@ else #endif self->fd = open(name, flags, 0666); + Py_END_ALLOW_THREADS - } else { - PyObject *fdobj = PyObject_CallFunction( - opener, "Oi", nameobj, flags); + } + else { + PyObject *fdobj; + +#ifndef MS_WINDOWS + /* the opener may clear the atomic flag */ + atomic_flag_works = NULL; +#endif + + fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags); if (fdobj == NULL) goto error; if (!PyLong_Check(fdobj)) { @@ -394,6 +415,11 @@ PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); goto error; } + +#ifndef MS_WINDOWS + if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0) + goto error; +#endif } if (dircheck(self, nameobj) < 0) goto error; diff -r ac6dab1819c4 -r 17211acb65b1 Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/_posixsubprocess.c Fri Aug 23 00:22:53 2013 +0200 @@ -35,7 +35,7 @@ # define FD_DIR "/proc/self/fd" #endif -#define POSIX_CALL(call) if ((call) == -1) goto error +#define POSIX_CALL(call) do { if ((call) == -1) goto error; } while (0) /* Maximum file descriptor, initialized on module load. */ @@ -87,7 +87,7 @@ if (stat("/dev", &dev_stat) != 0) return 0; if (stat(FD_DIR, &dev_fd_stat) != 0) - return 0; + return 0; if (dev_stat.st_dev == dev_fd_stat.st_dev) return 0; /* / == /dev == /dev/fd means it is static. #fail */ return 1; @@ -136,6 +136,29 @@ return 0; } +static int +make_inheritable(PyObject *py_fds_to_keep, int errpipe_write) +{ + Py_ssize_t i, len; + + len = PySequence_Length(py_fds_to_keep); + for (i = 0; i < len; ++i) { + PyObject* fdobj = PySequence_Fast_GET_ITEM(py_fds_to_keep, i); + long fd = PyLong_AsLong(fdobj); + assert(!PyErr_Occurred()); + assert(0 <= fd && fd <= INT_MAX); + if (fd == errpipe_write) { + /* errpipe_write is part of py_fds_to_keep. It must be closed at + exec(), but kept open in the child process until exec() is + called. */ + continue; + } + if (_Py_set_inheritable((int)fd, 1, NULL) < 0) + return -1; + } + return 0; +} + /* Close all file descriptors in the range start_fd inclusive to * end_fd exclusive except for those in py_fds_to_keep. If the @@ -205,18 +228,8 @@ int fd_dir_fd; if (start_fd >= end_fd) return; -#ifdef O_CLOEXEC - fd_dir_fd = open(FD_DIR, O_RDONLY | O_CLOEXEC, 0); -#else - fd_dir_fd = open(FD_DIR, O_RDONLY, 0); -#ifdef FD_CLOEXEC - { - int old = fcntl(fd_dir_fd, F_GETFD); - if (old != -1) - fcntl(fd_dir_fd, F_SETFD, old | FD_CLOEXEC); - } -#endif -#endif + + fd_dir_fd = _Py_open(FD_DIR, O_RDONLY); if (fd_dir_fd == -1) { /* No way to get a list of open fds. */ _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); @@ -356,16 +369,16 @@ /* Buffer large enough to hold a hex integer. We can't malloc. */ char hex_errno[sizeof(saved_errno)*2+1]; + if (make_inheritable(py_fds_to_keep, errpipe_write) < 0) + goto error; + /* Close parent's pipe ends. */ - if (p2cwrite != -1) { + if (p2cwrite != -1) POSIX_CALL(close(p2cwrite)); - } - if (c2pread != -1) { + if (c2pread != -1) POSIX_CALL(close(c2pread)); - } - if (errread != -1) { + if (errread != -1) POSIX_CALL(close(errread)); - } POSIX_CALL(close(errpipe_read)); /* When duping fds, if there arises a situation where one of the fds is @@ -379,38 +392,34 @@ dup2() removes the CLOEXEC flag but we must do it ourselves if dup2() would be a no-op (issue #10806). */ if (p2cread == 0) { - int old = fcntl(p2cread, F_GETFD); - if (old != -1) - fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC); - } else if (p2cread != -1) { + if (_Py_set_inheritable(p2cread, 1, NULL) < 0) + goto error; + } + else if (p2cread != -1) POSIX_CALL(dup2(p2cread, 0)); /* stdin */ + + if (c2pwrite == 1) { + if (_Py_set_inheritable(c2pwrite, 1, NULL) < 0) + goto error; } - if (c2pwrite == 1) { - int old = fcntl(c2pwrite, F_GETFD); - if (old != -1) - fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); - } else if (c2pwrite != -1) { + else if (c2pwrite != -1) POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ + + if (errwrite == 2) { + if (_Py_set_inheritable(errwrite, 1, NULL) < 0) + goto error; } - if (errwrite == 2) { - int old = fcntl(errwrite, F_GETFD); - if (old != -1) - fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); - } else if (errwrite != -1) { + else if (errwrite != -1) POSIX_CALL(dup2(errwrite, 2)); /* stderr */ - } /* Close pipe fds. Make sure we don't close the same fd more than */ /* once, or standard fds. */ - if (p2cread > 2) { + if (p2cread > 2) POSIX_CALL(close(p2cread)); - } - if (c2pwrite > 2 && c2pwrite != p2cread) { + if (c2pwrite > 2 && c2pwrite != p2cread) POSIX_CALL(close(c2pwrite)); - } - if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) { + if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) POSIX_CALL(close(errwrite)); - } if (close_fds) { int local_max_fd = max_fd; @@ -543,7 +552,7 @@ PyObject *result; _Py_IDENTIFIER(isenabled); _Py_IDENTIFIER(disable); - + gc_module = PyImport_ImportModule("gc"); if (gc_module == NULL) return NULL; @@ -720,52 +729,6 @@ Raises: Only on an error in the parent process.\n\ "); -PyDoc_STRVAR(subprocess_cloexec_pipe_doc, -"cloexec_pipe() -> (read_end, write_end)\n\n\ -Create a pipe whose ends have the cloexec flag set."); - -static PyObject * -subprocess_cloexec_pipe(PyObject *self, PyObject *noargs) -{ - int fds[2]; - int res; -#ifdef HAVE_PIPE2 - Py_BEGIN_ALLOW_THREADS - res = pipe2(fds, O_CLOEXEC); - Py_END_ALLOW_THREADS - if (res != 0 && errno == ENOSYS) - { - { -#endif - /* We hold the GIL which offers some protection from other code calling - * fork() before the CLOEXEC flags have been set but we can't guarantee - * anything without pipe2(). */ - long oldflags; - - res = pipe(fds); - - if (res == 0) { - oldflags = fcntl(fds[0], F_GETFD, 0); - if (oldflags < 0) res = oldflags; - } - if (res == 0) - res = fcntl(fds[0], F_SETFD, oldflags | FD_CLOEXEC); - - if (res == 0) { - oldflags = fcntl(fds[1], F_GETFD, 0); - if (oldflags < 0) res = oldflags; - } - if (res == 0) - res = fcntl(fds[1], F_SETFD, oldflags | FD_CLOEXEC); -#ifdef HAVE_PIPE2 - } - } -#endif - if (res != 0) - return PyErr_SetFromErrno(PyExc_OSError); - return Py_BuildValue("(ii)", fds[0], fds[1]); -} - /* module level code ********************************************************/ PyDoc_STRVAR(module_doc, @@ -774,7 +737,6 @@ static PyMethodDef module_methods[] = { {"fork_exec", subprocess_fork_exec, METH_VARARGS, subprocess_fork_exec_doc}, - {"cloexec_pipe", subprocess_cloexec_pipe, METH_NOARGS, subprocess_cloexec_pipe_doc}, {NULL, NULL} /* sentinel */ }; diff -r ac6dab1819c4 -r 17211acb65b1 Modules/_ssl.c --- a/Modules/_ssl.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/_ssl.c Fri Aug 23 00:22:53 2013 +0200 @@ -2350,7 +2350,7 @@ FILE *f; DH *dh; - f = _Py_fopen(filepath, "rb"); + f = _Py_fopen_obj(filepath, "rb"); if (f == NULL) { if (!PyErr_Occurred()) PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath); diff -r ac6dab1819c4 -r 17211acb65b1 Modules/main.c --- a/Modules/main.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/main.c Fri Aug 23 00:22:53 2013 +0200 @@ -143,7 +143,7 @@ { char *startup = Py_GETENV("PYTHONSTARTUP"); if (startup != NULL && startup[0] != '\0') { - FILE *fp = fopen(startup, "r"); + FILE *fp = _Py_fopen(startup, "r"); if (fp != NULL) { (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); PyErr_Clear(); diff -r ac6dab1819c4 -r 17211acb65b1 Modules/mmapmodule.c --- a/Modules/mmapmodule.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/mmapmodule.c Fri Aug 23 00:22:53 2013 +0200 @@ -1208,18 +1208,18 @@ flags |= MAP_ANONYMOUS; #else /* SVR4 method to map anonymous memory is to open /dev/zero */ - fd = devzero = open("/dev/zero", O_RDWR); + fd = devzero = _Py_open("/dev/zero", O_RDWR); if (devzero == -1) { Py_DECREF(m_obj); PyErr_SetFromErrno(PyExc_OSError); return NULL; } #endif - } else { - m_obj->fd = dup(fd); + } + else { + m_obj->fd = _Py_dup(fd); if (m_obj->fd == -1) { Py_DECREF(m_obj); - PyErr_SetFromErrno(PyExc_OSError); return NULL; } } diff -r ac6dab1819c4 -r 17211acb65b1 Modules/ossaudiodev.c --- a/Modules/ossaudiodev.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/ossaudiodev.c Fri Aug 23 00:22:53 2013 +0200 @@ -115,7 +115,9 @@ one open at a time. This does *not* affect later I/O; OSS provides a special ioctl() for non-blocking read/write, which is exposed via oss_nonblock() below. */ - if ((fd = open(devicename, imode|O_NONBLOCK)) == -1) { + fd = _Py_open(devicename, imode|O_NONBLOCK); + + if (fd == -1) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } @@ -177,7 +179,8 @@ devicename = "/dev/mixer"; } - if ((fd = open(devicename, O_RDWR)) == -1) { + fd = _Py_open(devicename, O_RDWR); + if (fd == -1) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } diff -r ac6dab1819c4 -r 17211acb65b1 Modules/posixmodule.c --- a/Modules/posixmodule.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/posixmodule.c Fri Aug 23 00:22:53 2013 +0200 @@ -3541,10 +3541,7 @@ #ifdef HAVE_FDOPENDIR if (path->fd != -1) { /* closedir() closes the FD, so we duplicate it */ - Py_BEGIN_ALLOW_THREADS - fd = dup(path->fd); - Py_END_ALLOW_THREADS - + fd = _Py_dup(path->fd); if (fd == -1) { list = posix_error(); goto exit; @@ -5774,7 +5771,7 @@ static PyObject * posix_openpty(PyObject *self, PyObject *noargs) { - int master_fd, slave_fd; + int master_fd = -1, slave_fd = -1; #ifndef HAVE_OPENPTY char * slave_name; #endif @@ -5787,37 +5784,52 @@ #ifdef HAVE_OPENPTY if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) - return posix_error(); + goto posix_error; + + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) + goto error; + if (_Py_set_inheritable(slave_fd, 0, NULL) < 0) + goto error; + #elif defined(HAVE__GETPTY) slave_name = _getpty(&master_fd, O_RDWR, 0666, 0); if (slave_name == NULL) - return posix_error(); - - slave_fd = open(slave_name, O_RDWR); + goto posix_error; + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) + goto error; + + slave_fd = _Py_open(slave_name, O_RDWR); if (slave_fd < 0) - return posix_error(); -#else - master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ + goto posix_error; + +#else + master_fd = _Py_open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ if (master_fd < 0) - return posix_error(); + goto posix_error; + sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); + /* change permission of slave */ if (grantpt(master_fd) < 0) { PyOS_setsig(SIGCHLD, sig_saved); - return posix_error(); - } + goto posix_error; + } + /* unlock slave */ if (unlockpt(master_fd) < 0) { PyOS_setsig(SIGCHLD, sig_saved); - return posix_error(); - } + goto posix_error; + } + PyOS_setsig(SIGCHLD, sig_saved); + slave_name = ptsname(master_fd); /* get name of slave */ if (slave_name == NULL) - return posix_error(); - slave_fd = open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ + goto posix_error; + + slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ if (slave_fd < 0) - return posix_error(); + goto posix_error; #if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC) ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ @@ -5829,6 +5841,14 @@ return Py_BuildValue("(ii)", master_fd, slave_fd); +posix_error: + posix_error(); +error: + if (master_fd != -1) + close(master_fd); + if (slave_fd != -1) + close(slave_fd); + return NULL; } #endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */ @@ -7438,6 +7458,10 @@ /* Functions acting on file descriptors */ +#ifdef O_CLOEXEC +extern int _Py_open_cloexec_works; +#endif + PyDoc_STRVAR(posix_open__doc__, "open(path, flags, mode=0o777, *, dir_fd=None)\n\n\ Open a file for low level IO. Returns a file handle (integer).\n\ @@ -7457,6 +7481,11 @@ int fd; PyObject *return_value = NULL; static char *keywords[] = {"path", "flags", "mode", "dir_fd", NULL}; +#ifdef O_CLOEXEC + int *atomic_flag_works = &_Py_open_cloexec_works; +#elif !defined(MS_WINDOWS) + int *atomic_flag_works = NULL; +#endif memset(&path, 0, sizeof(path)); path.function_name = "open"; @@ -7471,6 +7500,12 @@ )) return NULL; +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif + Py_BEGIN_ALLOW_THREADS #ifdef MS_WINDOWS if (path.wide) @@ -7490,6 +7525,13 @@ goto exit; } +#ifndef MS_WINDOWS + if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) { + close(fd); + goto exit; + } +#endif + return_value = PyLong_FromLong((long)fd); exit: @@ -7546,13 +7588,14 @@ posix_dup(PyObject *self, PyObject *args) { int fd; + if (!PyArg_ParseTuple(args, "i:dup", &fd)) return NULL; - if (!_PyVerify_fd(fd)) - return posix_error(); - fd = dup(fd); - if (fd < 0) - return posix_error(); + + fd = _Py_dup(fd); + if (fd == -1) + return NULL; + return PyLong_FromLong((long)fd); } @@ -7562,16 +7605,70 @@ Duplicate file descriptor."); static PyObject * -posix_dup2(PyObject *self, PyObject *args) -{ - int fd, fd2, res; - if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) - return NULL; +posix_dup2(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"fd", "fd2", "inheritable", NULL}; + int fd, fd2; + int inheritable = 1; + int res; +#ifdef HAVE_DUP3 + /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */ + int dup3_works = -1; +#endif + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|i:dup2", keywords, + &fd, &fd2, &inheritable)) + return NULL; + if (!_PyVerify_fd_dup2(fd, fd2)) return posix_error(); + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS res = dup2(fd, fd2); + Py_END_ALLOW_THREADS if (res < 0) return posix_error(); + + /* Character files like console cannot be make non-inheritable */ + if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) { + close(fd2); + return NULL; + } +#else + +#ifdef HAVE_DUP3 + if (!inheritable && dup3_works != 0) { + Py_BEGIN_ALLOW_THREADS + res = dup3(fd, fd2, O_CLOEXEC); + Py_END_ALLOW_THREADS + if (res < 0) { + if (dup3_works == -1) + dup3_works = (errno != ENOSYS); + if (dup3_works) + return posix_error(); + } + } + + if (dup3_works == 0) + { +#endif + Py_BEGIN_ALLOW_THREADS + res = dup2(fd, fd2); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + + if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) { + close(fd2); + return NULL; + } +#ifdef HAVE_DUP3 + } +#endif + +#endif + Py_INCREF(Py_None); return Py_None; } @@ -8052,30 +8149,75 @@ #ifdef HAVE_PIPE PyDoc_STRVAR(posix_pipe__doc__, -"pipe() -> (read_end, write_end)\n\n\ -Create a pipe."); +"pipe() -> (read_end, write_end)\n\n" +"Create a pipe. New file descriptors are non-inheritable."); static PyObject * posix_pipe(PyObject *self, PyObject *noargs) { -#if !defined(MS_WINDOWS) int fds[2]; +#ifdef MS_WINDOWS + HANDLE read, write; + SECURITY_ATTRIBUTES attr; + BOOL ok; +#else int res; - res = pipe(fds); - if (res != 0) - return posix_error(); - return Py_BuildValue("(ii)", fds[0], fds[1]); -#else /* MS_WINDOWS */ - HANDLE read, write; - int read_fd, write_fd; - BOOL ok; - ok = CreatePipe(&read, &write, NULL, 0); +#endif + +#ifdef MS_WINDOWS + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = FALSE; + + Py_BEGIN_ALLOW_THREADS + ok = CreatePipe(&read, &write, &attr, 0); + if (ok) { + fds[0] = _open_osfhandle((Py_intptr_t)read, _O_RDONLY); + fds[1] = _open_osfhandle((Py_intptr_t)write, _O_WRONLY); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(read); + CloseHandle(write); + ok = 0; + } + } + Py_END_ALLOW_THREADS + if (!ok) return PyErr_SetFromWindowsErr(0); - read_fd = _open_osfhandle((Py_intptr_t)read, 0); - write_fd = _open_osfhandle((Py_intptr_t)write, 1); - return Py_BuildValue("(ii)", read_fd, write_fd); -#endif /* MS_WINDOWS */ +#else + +#ifdef HAVE_PIPE2 + Py_BEGIN_ALLOW_THREADS + res = pipe2(fds, O_CLOEXEC); + Py_END_ALLOW_THREADS + + if (res != 0 && errno == ENOSYS) + { +#endif + Py_BEGIN_ALLOW_THREADS + res = pipe(fds); + Py_END_ALLOW_THREADS + + if (res == 0) { + if (_Py_set_inheritable(fds[0], 0, NULL) < 0) { + close(fds[0]); + close(fds[1]); + return NULL; + } + if (_Py_set_inheritable(fds[1], 0, NULL) < 0) { + close(fds[0]); + close(fds[1]); + return NULL; + } + } +#ifdef HAVE_PIPE2 + } +#endif + + if (res != 0) + return PyErr_SetFromErrno(PyExc_OSError); +#endif /* !MS_WINDOWS */ + return Py_BuildValue("(ii)", fds[0], fds[1]); } #endif /* HAVE_PIPE */ @@ -10665,6 +10807,102 @@ Py_RETURN_NONE; } +PyDoc_STRVAR(get_inheritable__doc__, + "get_inheritable(fd) -> bool\n" \ + "\n" \ + "Get the close-on-exe flag of the specified file descriptor."); + +static PyObject* +posix_get_inheritable(PyObject *self, PyObject *args) +{ + int fd; + int inheritable; + + if (!PyArg_ParseTuple(args, "i:get_inheritable", &fd)) + return NULL; + + if (!_PyVerify_fd(fd)) + return posix_error(); + + inheritable = _Py_get_inheritable(fd); + if (inheritable < 0) + return NULL; + return PyBool_FromLong(inheritable); +} + +PyDoc_STRVAR(set_inheritable__doc__, + "set_inheritable(fd, inheritable)\n" \ + "\n" \ + "Set the inheritable flag of the specified file descriptor."); + +static PyObject* +posix_set_inheritable(PyObject *self, PyObject *args) +{ + int fd, inheritable; + + if (!PyArg_ParseTuple(args, "ii:set_inheritable", &fd, &inheritable)) + return NULL; + + if (!_PyVerify_fd(fd)) + return posix_error(); + + if (_Py_set_inheritable(fd, inheritable, NULL) < 0) + return NULL; + Py_RETURN_NONE; +} + + +#ifdef MS_WINDOWS +PyDoc_STRVAR(get_handle_inheritable__doc__, + "get_handle_inheritable(fd) -> bool\n" \ + "\n" \ + "Get the close-on-exe flag of the specified file descriptor."); + +static PyObject* +posix_get_handle_inheritable(PyObject *self, PyObject *args) +{ + Py_intptr_t handle; + DWORD flags; + + if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR ":get_handle_inheritable", &handle)) + return NULL; + + if (!GetHandleInformation((HANDLE)handle, &flags)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + + return PyBool_FromLong(flags & HANDLE_FLAG_INHERIT); +} + +PyDoc_STRVAR(set_handle_inheritable__doc__, + "set_handle_inheritable(fd, inheritable)\n" \ + "\n" \ + "Set the inheritable flag of the specified handle."); + +static PyObject* +posix_set_handle_inheritable(PyObject *self, PyObject *args) +{ + int inheritable = 1; + Py_intptr_t handle; + DWORD flags; + + if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:set_handle_inheritable", + &handle, &inheritable)) + return NULL; + + if (inheritable) + flags = HANDLE_FLAG_INHERIT; + else + flags = 0; + if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + Py_RETURN_NONE; +} +#endif /* MS_WINDOWS */ + static PyMethodDef posix_methods[] = { {"access", (PyCFunction)posix_access, @@ -10942,7 +11180,8 @@ {"closerange", posix_closerange, METH_VARARGS, posix_closerange__doc__}, {"device_encoding", device_encoding, METH_VARARGS, device_encoding__doc__}, {"dup", posix_dup, METH_VARARGS, posix_dup__doc__}, - {"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__}, + {"dup2", (PyCFunction)posix_dup2, + METH_VARARGS | METH_KEYWORDS, posix_dup2__doc__}, #ifdef HAVE_LOCKF {"lockf", posix_lockf, METH_VARARGS, posix_lockf__doc__}, #endif @@ -11113,6 +11352,14 @@ #endif {"cpu_count", (PyCFunction)posix_cpu_count, METH_NOARGS, posix_cpu_count__doc__}, + {"get_inheritable", posix_get_inheritable, METH_VARARGS, get_inheritable__doc__}, + {"set_inheritable", posix_set_inheritable, METH_VARARGS, set_inheritable__doc__}, +#ifdef MS_WINDOWS + {"get_handle_inheritable", posix_get_handle_inheritable, + METH_VARARGS, get_handle_inheritable__doc__}, + {"set_handle_inheritable", posix_set_handle_inheritable, + METH_VARARGS, set_handle_inheritable__doc__}, +#endif {NULL, NULL} /* Sentinel */ }; diff -r ac6dab1819c4 -r 17211acb65b1 Modules/selectmodule.c --- a/Modules/selectmodule.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/selectmodule.c Fri Aug 23 00:22:53 2013 +0200 @@ -988,7 +988,7 @@ }; static devpollObject * -newDevPollObject(void) +newDevPollObject() { devpollObject *self; int fd_devpoll, limit_result; @@ -1004,7 +1004,7 @@ */ limit_result = getrlimit(RLIMIT_NOFILE, &limit); if (limit_result != -1) - fd_devpoll = open("/dev/poll", O_RDWR); + fd_devpoll = _Py_open("/dev/poll", O_RDWR); Py_END_ALLOW_THREADS if (limit_result == -1) { @@ -1194,6 +1194,7 @@ if (fd == -1) { Py_BEGIN_ALLOW_THREADS #ifdef HAVE_EPOLL_CREATE1 + flags |= EPOLL_CLOEXEC; if (flags) self->epfd = epoll_create1(flags); else @@ -1209,6 +1210,14 @@ PyErr_SetFromErrno(PyExc_OSError); return NULL; } + +#ifndef HAVE_EPOLL_CREATE1 + if (_Py_set_inheritable(self->epfd, 0, NULL) < 0) { + Py_DECREF(self); + return NULL; + } +#endif + return (PyObject *)self; } @@ -1896,13 +1905,19 @@ PyErr_SetFromErrno(PyExc_OSError); return NULL; } + + if (fd == -1) { + if (_Py_set_inheritable(self->kqfd, 0, NULL) < 0) { + Py_DECREF(self); + return NULL; + } + } return (PyObject *)self; } static PyObject * kqueue_queue_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if ((args != NULL && PyObject_Size(args)) || (kwds != NULL && PyObject_Size(kwds))) { PyErr_SetString(PyExc_ValueError, diff -r ac6dab1819c4 -r 17211acb65b1 Modules/socketmodule.c --- a/Modules/socketmodule.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/socketmodule.c Fri Aug 23 00:22:53 2013 +0200 @@ -97,13 +97,14 @@ /* Socket object documentation */ PyDoc_STRVAR(sock_doc, -"socket([family[, type[, proto]]]) -> socket object\n\ +"socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) -> socket object\n\ \n\ Open a socket of the given type. The family argument specifies the\n\ address family; it defaults to AF_INET. The type argument specifies\n\ whether this is a stream (SOCK_STREAM, this is the default)\n\ or datagram (SOCK_DGRAM) socket. The protocol argument defaults to 0,\n\ specifying the default protocol. Keyword arguments are accepted.\n\ +The socket is created as non-inheritable.\n\ \n\ A socket object represents one endpoint of a network connection.\n\ \n\ @@ -114,7 +115,7 @@ close() -- close the socket\n\ connect(addr) -- connect the socket to a remote address\n\ connect_ex(addr) -- connect, return an error code instead of an exception\n\ -_dup() -- return a new socket fd duplicated from fileno()\n\ +dup() -- return a new socket fd duplicated from fileno()\n\ fileno() -- return underlying file descriptor\n\ getpeername() -- return remote address [*]\n\ getsockname() -- return local address\n\ @@ -356,22 +357,7 @@ #endif #ifdef MS_WINDOWS -/* On Windows a socket is really a handle not an fd */ -static SOCKET -dup_socket(SOCKET handle) -{ - WSAPROTOCOL_INFO info; - - if (WSADuplicateSocket(handle, GetCurrentProcessId(), &info)) - return INVALID_SOCKET; - - return WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED); -} #define SOCKETCLOSE closesocket -#else -/* On Unix we can use dup to duplicate the file descriptor of a socket*/ -#define dup_socket(fd) dup(fd) #endif #ifdef MS_WIN32 @@ -499,6 +485,11 @@ (errno == expected) #endif +#ifdef MS_WINDOWS +/* Does WSASocket() support the WSA_FLAG_NO_HANDLE_INHERIT flag? */ +static int support_wsa_no_inherit = -1; +#endif + /* Convenience function to raise an error according to errno and return a NULL pointer from a function. */ @@ -1955,6 +1946,11 @@ PyObject *addr = NULL; PyObject *res = NULL; int timeout; +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ + static int accept4_works = -1; +#endif + if (!getsockaddrlen(s, &addrlen)) return NULL; memset(&addrbuf, 0, addrlen); @@ -1963,10 +1959,24 @@ return select_error(); BEGIN_SELECT_LOOP(s) + Py_BEGIN_ALLOW_THREADS timeout = internal_select_ex(s, 0, interval); if (!timeout) { +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + if (accept4_works != 0) { + newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, + SOCK_CLOEXEC); + if (newfd == INVALID_SOCKET && accept4_works == -1) { + /* On Linux older than 2.6.28, accept4() fails with ENOSYS */ + accept4_works = (errno != ENOSYS); + } + } + if (accept4_works == 0) + newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); +#else newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); +#endif } Py_END_ALLOW_THREADS @@ -1979,6 +1989,25 @@ if (newfd == INVALID_SOCKET) return s->errorhandler(); +#ifdef MS_WINDOWS + if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { + PyErr_SetFromWindowsErr(0); + SOCKETCLOSE(newfd); + goto finally; + } +#else + +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + if (!accept4_works) +#endif + { + if (_Py_set_inheritable(newfd, 0, NULL) < 0) { + SOCKETCLOSE(newfd); + goto finally; + } + } +#endif + sock = PyLong_FromSocket_t(newfd); if (sock == NULL) { SOCKETCLOSE(newfd); @@ -3909,6 +3938,12 @@ /* Initialize a new socket object. */ +#ifdef SOCK_CLOEXEC +/* socket() and socketpair() fail with EINVAL on Linux kernel older + * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */ +static int sock_cloexec_works = -1; +#endif + /*ARGSUSED*/ static int sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) @@ -3918,6 +3953,13 @@ SOCKET_T fd = INVALID_SOCKET; int family = AF_INET, type = SOCK_STREAM, proto = 0; static char *keywords[] = {"family", "type", "proto", "fileno", 0}; +#ifndef MS_WINDOWS +#ifdef SOCK_CLOEXEC + int *atomic_flag_works = &sock_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif +#endif if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiO:socket", keywords, @@ -3962,14 +4004,74 @@ } } else { +#ifdef MS_WINDOWS + /* Windows implementation */ +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + Py_BEGIN_ALLOW_THREADS - fd = socket(family, type, proto); + if (support_wsa_no_inherit) { + fd = WSASocket(family, type, proto, + NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + if (fd == INVALID_SOCKET) { + /* Windows 7 or Windows 2008 R2 without SP1 or the hotfix */ + support_wsa_no_inherit = 0; + fd = socket(family, type, proto); + } + } + else { + fd = socket(family, type, proto); + } Py_END_ALLOW_THREADS if (fd == INVALID_SOCKET) { set_error(); return -1; } + + if (!support_wsa_no_inherit) { + if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) { + closesocket(fd); + PyErr_SetFromWindowsErr(0); + return -1; + } + } +#else + /* UNIX */ + Py_BEGIN_ALLOW_THREADS +#ifdef SOCK_CLOEXEC + if (sock_cloexec_works != 0) { + fd = socket(family, type | SOCK_CLOEXEC, proto); + if (sock_cloexec_works == -1) { + if (fd >= 0) { + sock_cloexec_works = 1; + } + else if (errno == EINVAL) { + /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ + sock_cloexec_works = 0; + fd = socket(family, type, proto); + } + } + } + else +#endif + { + fd = socket(family, type, proto); + } + Py_END_ALLOW_THREADS + + if (fd == INVALID_SOCKET) { + set_error(); + return -1; + } + + if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) { + SOCKETCLOSE(fd); + return -1; + } +#endif } init_sockobject(s, fd, family, type, proto); @@ -4535,16 +4637,36 @@ { SOCKET_T fd, newfd; PyObject *newfdobj; - +#ifdef MS_WINDOWS + WSAPROTOCOL_INFO info; +#endif fd = PyLong_AsSocket_t(fdobj); if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) return NULL; - newfd = dup_socket(fd); +#ifdef MS_WINDOWS + if (WSADuplicateSocket(fd, GetCurrentProcessId(), &info)) + return set_error(); + + newfd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &info, 0, WSA_FLAG_OVERLAPPED); if (newfd == INVALID_SOCKET) return set_error(); + if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { + closesocket(newfd); + PyErr_SetFromWindowsErr(0); + return NULL; + } +#else + /* On UNIX, dup can be used to duplicate the file descriptor of a socket */ + newfd = _Py_dup(fd); + if (newfd == INVALID_SOCKET) + return NULL; +#endif + newfdobj = PyLong_FromSocket_t(newfd); if (newfdobj == NULL) SOCKETCLOSE(newfd); @@ -4572,6 +4694,12 @@ SOCKET_T sv[2]; int family, type = SOCK_STREAM, proto = 0; PyObject *res = NULL; +#ifdef SOCK_CLOEXEC + int *atomic_flag_works = &sock_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif + int ret; #if defined(AF_UNIX) family = AF_UNIX; @@ -4581,9 +4709,38 @@ if (!PyArg_ParseTuple(args, "|iii:socketpair", &family, &type, &proto)) return NULL; + /* Create a pair of socket fds */ - if (socketpair(family, type, proto, sv) < 0) + Py_BEGIN_ALLOW_THREADS +#ifdef SOCK_CLOEXEC + if (sock_cloexec_works != 0) { + ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv); + if (sock_cloexec_works == -1) { + if (ret >= 0) { + sock_cloexec_works = 1; + } + else if (errno == EINVAL) { + /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ + sock_cloexec_works = 0; + ret = socketpair(family, type, proto, sv); + } + } + } + else +#endif + { + ret = socketpair(family, type, proto, sv); + } + Py_END_ALLOW_THREADS + + if (ret < 0) return set_error(); + + if (_Py_set_inheritable(sv[0], 0, atomic_flag_works) < 0) + goto finally; + if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0) + goto finally; + s0 = new_sockobject(sv[0], family, type, proto); if (s0 == NULL) goto finally; @@ -4605,7 +4762,7 @@ } PyDoc_STRVAR(socketpair_doc, -"socketpair([family[, type[, proto]]]) -> (socket object, socket object)\n\ +"socketpair([family[, type [, proto]]]) -> (socket object, socket object)\n\ \n\ Create a pair of socket objects from the sockets returned by the platform\n\ socketpair() function.\n\ @@ -5539,6 +5696,16 @@ if (!os_init()) return NULL; +#ifdef MS_WINDOWS + if (support_wsa_no_inherit == -1) { + DWORD version = GetVersion(); + DWORD major = (DWORD)LOBYTE(LOWORD(version)); + DWORD minor = (DWORD)HIBYTE(LOWORD(version)); + /* need Windows 7 SP1, 2008 R2 SP1 or later */ + support_wsa_no_inherit = (major >= 6 && minor >= 1); + } +#endif + Py_TYPE(&sock_type) = &PyType_Type; m = PyModule_Create(&socketmodule); if (m == NULL) diff -r ac6dab1819c4 -r 17211acb65b1 Modules/zipimport.c --- a/Modules/zipimport.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Modules/zipimport.c Fri Aug 23 00:22:53 2013 +0200 @@ -870,7 +870,7 @@ const char *charset; int bootstrap; - fp = _Py_fopen(archive, "rb"); + fp = _Py_fopen_obj(archive, "rb"); if (fp == NULL) { if (!PyErr_Occurred()) PyErr_Format(ZipImportError, "can't open Zip file: %R", archive); @@ -1064,7 +1064,7 @@ return NULL; } - fp = _Py_fopen(archive, "rb"); + fp = _Py_fopen_obj(archive, "rb"); if (!fp) { if (!PyErr_Occurred()) PyErr_Format(PyExc_IOError, diff -r ac6dab1819c4 -r 17211acb65b1 PC/_msi.c --- a/PC/_msi.c Thu Aug 22 08:20:31 2013 -0700 +++ b/PC/_msi.c Fri Aug 23 00:22:53 2013 +0200 @@ -55,7 +55,7 @@ static FNFCIOPEN(cb_open) { - int result = _open(pszFile, oflag, pmode); + int result = _open(pszFile, oflag | O_NOINHERIT, pmode); if (result == -1) *err = errno; return result; @@ -179,7 +179,7 @@ CloseHandle(handle); - return _open(pszName, _O_RDONLY | _O_BINARY); + return _open(pszName, _O_RDONLY | _O_BINARY | O_NOINHERIT); } static PyObject* fcicreate(PyObject* obj, PyObject* args) diff -r ac6dab1819c4 -r 17211acb65b1 PC/bdist_wininst/install.c --- a/PC/bdist_wininst/install.c Thu Aug 22 08:20:31 2013 -0700 +++ b/PC/bdist_wininst/install.c Fri Aug 23 00:22:53 2013 +0200 @@ -743,7 +743,7 @@ if (pathname == NULL || pathname[0] == '\0') return 2; - fh = open(pathname, _O_RDONLY); + fh = open(pathname, _O_RDONLY | O_NOINHERIT); if (-1 == fh) { fprintf(stderr, "Could not open postinstall-script %s\n", pathname); diff -r ac6dab1819c4 -r 17211acb65b1 Parser/tokenizer.c --- a/Parser/tokenizer.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Parser/tokenizer.c Fri Aug 23 00:22:53 2013 +0200 @@ -1730,10 +1730,15 @@ FILE *fp; char *p_start =NULL , *p_end =NULL , *encoding = NULL; +#ifndef PGEN + fd = _Py_dup(fd); +#else fd = dup(fd); +#endif if (fd < 0) { return NULL; } + fp = fdopen(fd, "r"); if (fp == NULL) { return NULL; diff -r ac6dab1819c4 -r 17211acb65b1 Python/errors.c --- a/Python/errors.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Python/errors.c Fri Aug 23 00:22:53 2013 +0200 @@ -977,7 +977,7 @@ if (filename == NULL || *filename == '\0' || lineno <= 0) return NULL; - fp = fopen(filename, "r" PY_STDIOTEXTMODE); + fp = _Py_fopen(filename, "r" PY_STDIOTEXTMODE); if (fp == NULL) return NULL; for (i = 0; i < lineno; i++) { diff -r ac6dab1819c4 -r 17211acb65b1 Python/fileutils.c --- a/Python/fileutils.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Python/fileutils.c Fri Aug 23 00:22:53 2013 +0200 @@ -9,10 +9,29 @@ #include #endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + #ifdef __APPLE__ extern wchar_t* _Py_DecodeUTF8_surrogateescape(const char *s, Py_ssize_t size); #endif +#ifdef O_CLOEXEC +/* Does open() supports the O_CLOEXEC flag? Possible values: + + -1: unknown + 0: open() ignores O_CLOEXEC flag, ex: Linux kernel older than 2.6.23 + 1: open() supports O_CLOEXEC flag, close-on-exec is set + + The flag is used by _Py_open(), io.FileIO and os.open() */ +int _Py_open_cloexec_works = -1; +#endif + PyObject * _Py_device_encoding(int fd) { @@ -547,14 +566,215 @@ #endif +int +get_inheritable(int fd, int raise) +{ +#ifdef MS_WINDOWS + HANDLE handle; + DWORD flags; + + if (!_PyVerify_fd(fd)) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + if (raise) + PyErr_SetFromWindowsErr(0); + return -1; + } + + if (!GetHandleInformation(handle, &flags)) { + if (raise) + PyErr_SetFromWindowsErr(0); + return -1; + } + + return (flags & HANDLE_FLAG_INHERIT); +#else + int flags; + + flags = fcntl(fd, F_GETFD, 0); + if (flags == -1) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return !(flags & FD_CLOEXEC); +#endif +} + +/* Get the inheritable flag of the specified file descriptor. + Return 1 if it the file descriptor can be inherited, 0 if it cannot, + raise an exception and return -1 on error. */ +int +_Py_get_inheritable(int fd) +{ + return get_inheritable(fd, 1); +} + +static int +set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works) +{ +#ifdef MS_WINDOWS + HANDLE handle; + DWORD flags; +#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX) + int request; + int err; +#elif defined(HAVE_FCNTL_H) + int flags; + int res; +#endif + + /* atomic_flag_works can only be used to make the file descriptor + non-inheritable */ + assert(!(atomic_flag_works != NULL && inheritable)); + + if (atomic_flag_works != NULL && !inheritable) { + if (*atomic_flag_works == -1) { + int inheritable = get_inheritable(fd, raise); + if (inheritable == -1) + return -1; + *atomic_flag_works = !inheritable; + } + + if (*atomic_flag_works) + return 0; + } + +#ifdef MS_WINDOWS + if (!_PyVerify_fd(fd)) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + if (raise) + PyErr_SetFromWindowsErr(0); + return -1; + } + + if (inheritable) + flags = HANDLE_FLAG_INHERIT; + else + flags = 0; + if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) { + if (raise) + PyErr_SetFromWindowsErr(0); + return -1; + } + return 0; + +#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX) + if (inheritable) + request = FIONCLEX; + else + request = FIOCLEX; + err = ioctl(fd, request); + if (err) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; + +#else + flags = fcntl(fd, F_GETFD); + if (flags < 0) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + if (inheritable) + flags &= ~FD_CLOEXEC; + else + flags |= FD_CLOEXEC; + res = fcntl(fd, F_SETFD, flags); + if (res < 0) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +#endif +} + +/* Make the file descriptor non-inheritable. + Return 0 success, set errno and return -1 on error. */ +static int +make_non_inheritable(int fd) +{ + return set_inheritable(fd, 0, 0, NULL); +} + +/* Set the inheritable flag of the specified file descriptor. + On success: return 0, on error: raise an exception if raise is nonzero + and return -1. + + If atomic_flag_works is not NULL: + + * if *atomic_flag_works==-1, check if the inheritable is set on the file + descriptor: if yes, set *atomic_flag_works to 1, otherwise set to 0 and + set the inheritable flag + * if *atomic_flag_works==1: do nothing + * if *atomic_flag_works==0: set inheritable flag to False + + Set atomic_flag_works to NULL if no atomic flag was used to create the + file descriptor. + + atomic_flag_works can only be used to make a file descriptor + non-inheritable: atomic_flag_works must be NULL if inheritable=1. */ +int +_Py_set_inheritable(int fd, int inheritable, int *atomic_flag_works) +{ + return set_inheritable(fd, inheritable, 1, atomic_flag_works); +} + +/* Open a file with the specified flags (wrapper to open() function). + The file descriptor is created non-inheritable. */ +int +_Py_open(const char *pathname, int flags) +{ + int fd; +#ifdef MS_WINDOWS + fd = open(pathname, flags | O_NOINHERIT); + if (fd < 0) + return fd; +#else + + int *atomic_flag_works; +#ifdef O_CLOEXEC + atomic_flag_works = &_Py_open_cloexec_works; + flags |= O_CLOEXEC; +#else + atomic_flag_works = NULL; +#endif + fd = open(pathname, flags); + if (fd < 0) + return fd; + + if (set_inheritable(fd, 0, 0, atomic_flag_works) < 0) { + close(fd); + return -1; + } +#endif /* !MS_WINDOWS */ + return fd; +} + /* Open a file. Use _wfopen() on Windows, encode the path to the locale - encoding and use fopen() otherwise. */ - + encoding and use fopen() otherwise. The file descriptor is created + non-inheritable. */ FILE * _Py_wfopen(const wchar_t *path, const wchar_t *mode) { + FILE *f; #ifndef MS_WINDOWS - FILE *f; char *cpath; char cmode[10]; size_t r; @@ -568,21 +788,42 @@ return NULL; f = fopen(cpath, cmode); PyMem_Free(cpath); +#else + f = _wfopen(path, mode); +#endif + if (f == NULL) + return NULL; + if (make_non_inheritable(fileno(f)) < 0) { + fclose(f); + return NULL; + } return f; -#else - return _wfopen(path, mode); -#endif } -/* Call _wfopen() on Windows, or encode the path to the filesystem encoding and - call fopen() otherwise. +/* Wrapper to fopen(). The file descriptor is created non-inheritable. */ +FILE* +_Py_fopen(const char *pathname, const char *mode) +{ + FILE *f = fopen(pathname, mode); + if (f == NULL) + return NULL; + if (make_non_inheritable(fileno(f)) < 0) { + fclose(f); + return NULL; + } + return f; +} + +/* Open a file. Call _wfopen() on Windows, or encode the path to the filesystem + encoding and call fopen() otherwise. The file descriptor is created + non-inheritable. Return the new file object on success, or NULL if the file cannot be open or - (if PyErr_Occurred()) on unicode error */ - + (if PyErr_Occurred()) on unicode error. */ FILE* -_Py_fopen(PyObject *path, const char *mode) +_Py_fopen_obj(PyObject *path, const char *mode) { + FILE *f; #ifdef MS_WINDOWS wchar_t *wpath; wchar_t wmode[10]; @@ -602,16 +843,21 @@ if (usize == 0) return NULL; - return _wfopen(wpath, wmode); + f = _wfopen(wpath, wmode); #else - FILE *f; PyObject *bytes; if (!PyUnicode_FSConverter(path, &bytes)) return NULL; f = fopen(PyBytes_AS_STRING(bytes), mode); Py_DECREF(bytes); +#endif + if (f == NULL) + return NULL; + if (make_non_inheritable(fileno(f)) < 0) { + fclose(f); + return NULL; + } return f; -#endif } #ifdef HAVE_READLINK @@ -729,3 +975,72 @@ #endif } +/* Duplicate a file descriptor. The new file descriptor is created as + non-inheritable. Return a new file descriptor on success, raise an OSError + exception and return -1 on error. + + The GIL is released to call dup(). The caller must hold the GIL. */ +int +_Py_dup(int fd) +{ +#ifdef MS_WINDOWS + HANDLE handle; + DWORD ftype; +#endif + + if (!_PyVerify_fd(fd)) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + +#ifdef MS_WINDOWS + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + /* get the file type, ignore the error if it failed */ + ftype = GetFileType(handle); + + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + if (fd < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + /* Character files like console cannot be make non-inheritable */ + if (ftype != FILE_TYPE_CHAR) { + if (_Py_set_inheritable(fd, 0, NULL) < 0) { + close(fd); + return -1; + } + } +#elif defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC) + Py_BEGIN_ALLOW_THREADS + fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + Py_END_ALLOW_THREADS + if (fd < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + +#else + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + if (fd < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + if (_Py_set_inheritable(fd, 0, NULL) < 0) { + close(fd); + return -1; + } +#endif + return fd; +} + diff -r ac6dab1819c4 -r 17211acb65b1 Python/import.c --- a/Python/import.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Python/import.c Fri Aug 23 00:22:53 2013 +0200 @@ -1797,7 +1797,7 @@ &name, PyUnicode_FSDecoder, &pathname, &fob)) return NULL; if (fob != NULL) { - fp = _Py_fopen(pathname, "r"); + fp = _Py_fopen_obj(pathname, "r"); if (fp == NULL) { Py_DECREF(pathname); if (!PyErr_Occurred()) diff -r ac6dab1819c4 -r 17211acb65b1 Python/pythonrun.c --- a/Python/pythonrun.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Python/pythonrun.c Fri Aug 23 00:22:53 2013 +0200 @@ -1451,7 +1451,7 @@ /* Try to run a pyc file. First, re-open in binary */ if (closeit) fclose(fp); - if ((pyc_fp = fopen(filename, "rb")) == NULL) { + if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); goto done; } diff -r ac6dab1819c4 -r 17211acb65b1 Python/random.c --- a/Python/random.c Thu Aug 22 08:20:31 2013 -0700 +++ b/Python/random.c Fri Aug 23 00:22:53 2013 +0200 @@ -101,7 +101,7 @@ assert (0 < size); - fd = open("/dev/urandom", O_RDONLY); + fd = _Py_open("/dev/urandom", O_RDONLY); if (fd < 0) Py_FatalError("Failed to open /dev/urandom"); @@ -134,7 +134,7 @@ return 0; Py_BEGIN_ALLOW_THREADS - fd = open("/dev/urandom", O_RDONLY); + fd = _Py_open("/dev/urandom", O_RDONLY); Py_END_ALLOW_THREADS if (fd < 0) { diff -r ac6dab1819c4 -r 17211acb65b1 configure.ac --- a/configure.ac Thu Aug 22 08:20:31 2013 -0700 +++ b/configure.ac Fri Aug 23 00:22:53 2013 +0200 @@ -2807,7 +2807,7 @@ # checks for library functions AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ - clock confstr ctermid execv faccessat fchmod fchmodat fchown fchownat \ + clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \ fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ futimens futimes gai_strerror \ getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \