diff -r f87d85f7596c -r bc88690df059 Doc/library/asyncore.rst --- a/Doc/library/asyncore.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/asyncore.rst Tue Jan 29 13:35:00 2013 +0100 @@ -184,7 +184,7 @@ any that have been added to the map duri Most of these are nearly identical to their socket partners. - .. method:: create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + .. method:: create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM, cloexec=None) This is identical to the creation of a normal socket, and will use the same options for creation. Refer to the :mod:`socket` documentation for @@ -193,6 +193,9 @@ any that have been added to the map duri .. versionchanged:: 3.3 *family* and *type* arguments can be omitted. + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. method:: connect(address) diff -r f87d85f7596c -r bc88690df059 Doc/library/functions.rst --- a/Doc/library/functions.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/functions.rst Tue Jan 29 13:35:00 2013 +0100 @@ -823,7 +823,7 @@ are always available. They are listed h .. index:: single: file object; open() built-in function -.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) +.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None, cloexec=None) Open *file* and return a corresponding :term:`file object`. If the file cannot be opened, an :exc:`OSError` is raised. @@ -942,6 +942,10 @@ are always available. They are listed h :mod:`os.open` as *opener* results in functionality similar to passing ``None``). + If *cloexec* is ``True``, set the :ref:`close-on-exec flag ` on + the newly created file descriptor. *cloexec* is ignored if *file* is an + integer file descriptor. + The following example uses the :ref:`dir_fd ` parameter of the :func:`os.open` function to open a file relative to a given directory:: @@ -959,6 +963,9 @@ are always available. They are listed h The *opener* parameter was added. The ``'x'`` mode was added. + .. versionchanged:: 3.4 + The *cloexec* parameter 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 diff -r f87d85f7596c -r bc88690df059 Doc/library/io.rst --- a/Doc/library/io.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/io.rst Tue Jan 29 13:35:00 2013 +0100 @@ -110,7 +110,7 @@ High-level Module Interface :func:`os.stat`) if possible. -.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True) +.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, cloexec=None) This is an alias for the builtin :func:`open` function. @@ -487,7 +487,7 @@ I/O Base Classes Raw File I/O ^^^^^^^^^^^^ -.. class:: FileIO(name, mode='r', closefd=True, opener=None) +.. class:: FileIO(name, mode='r', closefd=True, opener=None, cloexec=None) :class:`FileIO` represents an OS-level file containing bytes data. It implements the :class:`RawIOBase` interface (and therefore the @@ -517,6 +517,10 @@ Raw File I/O :mod:`os.open` as *opener* results in functionality similar to passing ``None``). + If *cloexec* is ``True``, set the :ref:`close-on-exec flag ` on + the newly created file descriptor. *cloexec* is ignored if *name* is an + integer file descriptor. + See the :func:`open` built-in function for examples on using the *opener* parameter. @@ -524,6 +528,9 @@ Raw File I/O The *opener* parameter was added. The ``'x'`` mode was added. + .. versionchanged:: 3.4 + The *cloexec* parameter was added. + In addition to the attributes and methods from :class:`IOBase` and :class:`RawIOBase`, :class:`FileIO` provides the following data attributes: diff -r f87d85f7596c -r bc88690df059 Doc/library/os.rst --- a/Doc/library/os.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/os.rst Tue Jan 29 13:35:00 2013 +0100 @@ -683,19 +683,27 @@ as internal buffering of data. if it is connected to a terminal; else return :const:`None`. -.. function:: dup(fd) - - Return a duplicate of file descriptor *fd*. +.. function:: dup(fd, cloexec=None) + + Return a duplicate of file descriptor *fd*. If *cloexec* is ``True``, set + the :ref:`close-on-exec flag `. Availability: Unix, Windows. - -.. function:: dup2(fd, fd2) + .. versionchanged:: 3.4 + *cloexec* parameter was added. + + +.. function:: dup2(fd, fd2, cloexec=None) Duplicate file descriptor *fd* to *fd2*, closing the latter first if necessary. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag ` on *fd2*. Availability: Unix, Windows. + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. function:: fchmod(fd, mode) @@ -843,7 +851,7 @@ as internal buffering of data. :data:`os.SEEK_HOLE` or :data:`os.SEEK_DATA`. -.. function:: open(file, flags, mode=0o777, *, dir_fd=None) +.. function:: open(file, flags, mode=0o777, *, dir_fd=None, cloexec=None) 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 @@ -857,8 +865,13 @@ as internal buffering of data. This function can support :ref:`paths relative to directory descriptors `. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + Availability: Unix, Windows. + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. note:: This function is intended for low-level I/O. For normal usage, use the @@ -870,24 +883,32 @@ as internal buffering of data. The *dir_fd* argument. -.. function:: openpty() +.. function:: openpty(cloexec=None) .. 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. If *cloexec* is ``True``, set + the :ref:`close-on-exec flag `. 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. Availability: some flavors of Unix. - -.. function:: pipe() - - Create a pipe. Return a pair of file descriptors ``(r, w)`` usable for reading + .. versionchanged:: 3.4 + *cloexec* parameter was added. + + +.. function:: pipe(cloexec=None) + + Create a pipe. If *cloexec* is ``True``, set the :ref:`close-on-exec flag + `. Return a pair of file descriptors ``(r, w)`` usable for reading and writing, respectively. Availability: Unix, Windows. + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. function:: pipe2(flags) @@ -1192,6 +1213,51 @@ Querying the size of a terminal Height of the terminal window in characters. +.. _cloexec: + +Close-on-exec flag +~~~~~~~~~~~~~~~~~~ + +A file descriptor has a close-on-exec flag which indicates if the file +descriptor will be inherited or not. + +On UNIX, the file descriptor will be closed on the execution of child processes +if the close-on-exec flag is set, the file descriptor is inherited by child +processes if the flag is cleared. + +On Windows, the file descriptor cannot be inherited if the close-on-exec flag is +set, the file descriptor is inherited by child processes if the flag is cleared +and if :c:func:`CreateProcess` is called with the *bInheritHandles* parameter +set to ``TRUE`` (when :class:`subprocess.Popen` is created with +``close_fds=False`` for example). + +Example of functions having the *cloexec* parameter: :func:`open`, +:func:`os.pipe`, :func:`socket.socket`. The default value of the *cloexec* +parameter is :func:`sys.getdefaultcloexec`, it is ``False`` at startup. It +can be set to ``True`` using :func:`sys.setdefaultcloexec`, by setting the +:envvar:`PYTHONCLOEXEC` environment variable, and using :option:`-e` command +line option. + + +.. function:: get_cloexec(fd) + + Get close-on-exe flag of the specified file descriptor. Return a :class:`bool`. + + Availability: Windows, Linux, FreeBSD, NetBSD, OpenBSD, Mac OS X, Solaris. + + .. versionadded:: 3.4 + +.. function:: set_cloexec(fd, cloexec=True) + + Set or clear close-on-exe flag on the specified file descriptor. + + Availability: Windows, Linux, FreeBSD, NetBSD, OpenBSD, Mac OS X, Solaris. + + .. versionadded:: 3.4 + + + + .. _os-file-dir: Files and Directories diff -r f87d85f7596c -r bc88690df059 Doc/library/select.rst --- a/Doc/library/select.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/select.rst Tue Jan 29 13:35:00 2013 +0100 @@ -25,7 +25,7 @@ The module defines the following: Following :pep:`3151`, this class was made an alias of :exc:`OSError`. -.. function:: devpoll() +.. function:: devpoll(cloexec=None) (Only supported on Solaris and derivatives.) Returns a ``/dev/poll`` polling object; see section :ref:`devpoll-objects` below for the @@ -37,9 +37,14 @@ The module defines the following: increases this value, :c:func:`devpoll` may return an incomplete list of active file descriptors. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + .. versionadded:: 3.3 -.. function:: epoll(sizehint=-1, flags=0) + .. versionchanged:: 3.4 + *cloexec* parameter was added. + +.. function:: epoll(sizehint=-1, flags=0, cloexec=None) (Only supported on Linux 2.5.44 and newer.) Return an edge polling object, which can be used as Edge or Level Triggered interface for I/O @@ -49,11 +54,14 @@ The module defines the following: :ref:`epoll-objects` below for the methods supported by epolling objects. They also support the :keyword:`with` statement. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + .. versionchanged:: 3.3 Added the *flags* parameter. .. versionchanged:: 3.4 Support for the :keyword:`with` statement was added. + *cloexec* parameter was added. .. function:: poll() @@ -64,11 +72,16 @@ The module defines the following: by polling objects. -.. function:: kqueue() +.. function:: kqueue(cloexec=None) (Only supported on BSD.) Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) @@ -199,6 +212,13 @@ object. block until there is an event for this poll object. +.. method:: devpoll.fileno() + + Return the file descriptor number. + + .. versionadded:: 3.4 + + .. _epoll-objects: Edge and Level Trigger Polling (epoll) Objects diff -r f87d85f7596c -r bc88690df059 Doc/library/socket.rst --- a/Doc/library/socket.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/socket.rst Tue Jan 29 13:35:00 2013 +0100 @@ -445,7 +445,7 @@ The module :mod:`socket` exports the fol ``'udp'``, otherwise any protocol will match. -.. function:: socket([family[, type[, proto]]]) +.. function:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, cloexec=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), @@ -455,25 +455,38 @@ The module :mod:`socket` exports the fol constants. The protocol number is usually zero and may be omitted in that case or :const:`CAN_RAW` in case the address family is :const:`AF_CAN`. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag ` on + the newly created file descriptor. *cloexec* is ignored when *fileno* is + used. + .. versionchanged:: 3.3 The AF_CAN family was added. The AF_RDS family was added. + .. versionchanged:: 3.4 + *cloexec* parameter was added. -.. function:: socketpair([family[, type[, proto]]]) + +.. function:: socketpair(family=AF_INET, type=SOCK_STREAM, proto=0, cloexec=None) Build a pair of connected socket objects using the given address family, socket type, and protocol number. Address family, socket type, and protocol number are as for the :func:`socket` function above. The default family is :const:`AF_UNIX` if defined on the platform; otherwise, the default is :const:`AF_INET`. + + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + Availability: Unix. .. versionchanged:: 3.2 The returned socket objects now support the whole socket API, rather than a subset. + .. versionchanged:: 3.4 + *cloexec* parameter was added. -.. function:: fromfd(fd, family, type[, proto]) + +.. function:: fromfd(fd, family, type, proto=0, cloexec=None) 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 @@ -484,6 +497,11 @@ The module :mod:`socket` exports the fol 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. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. function:: ntohl(x) @@ -705,13 +723,18 @@ Socket objects have the following method correspond to Unix system calls applicable to sockets. -.. method:: socket.accept() +.. method:: socket.accept(cloexec=None) Accept a connection. The socket must be bound to an address and listening for connections. The return value is a pair ``(conn, address)`` where *conn* is a *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. + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + + .. versionchanged:: 3.4 + *cloexec* parameter was added. + .. method:: socket.bind(address) @@ -757,6 +780,16 @@ correspond to Unix system calls applicab .. versionadded:: 3.2 +.. method:: socket.dup(cloexec=None) + + Duplicate the socket. + + If *cloexec* is ``True``, set the :ref:`close-on-exec flag `. + + .. versionchanged:: 3.4 + *cloexec* parameter was added. + + .. method:: socket.fileno() Return the socket's file descriptor (a small integer). This is useful with diff -r f87d85f7596c -r bc88690df059 Doc/library/sys.rst --- a/Doc/library/sys.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/library/sys.rst Tue Jan 29 13:35:00 2013 +0100 @@ -401,6 +401,16 @@ always available. Use :func:`getswitchinterval` instead. +.. function:: getdefaultcloexec() + + Return the default value of the *cloexec* parameter: + see :ref:`close-on-exec flag `. + + .. seealso:: :func:`sys.setdefaultcloexec`. + + .. versionadded:: 3.4 + + .. function:: getdefaultencoding() Return the name of the current default string encoding used by the Unicode @@ -903,6 +913,16 @@ always available. :func:`setswitchinterval` instead. +.. function:: setdefaultcloexec(cloexec=True) + + Set the default value of the *cloexec* parameter: + see the :ref:`close-on-exec flag `. + + .. seealso:: :func:`sys.getdefaultcloexec`. + + .. versionadded:: 3.4 + + .. function:: setdlopenflags(n) Set the flags used by the interpreter for :c:func:`dlopen` calls, such as when diff -r f87d85f7596c -r bc88690df059 Doc/using/cmdline.rst --- a/Doc/using/cmdline.rst Wed Jan 16 15:19:16 2013 +0100 +++ b/Doc/using/cmdline.rst Tue Jan 29 13:35:00 2013 +0100 @@ -191,6 +191,14 @@ Miscellaneous options options). See also :envvar:`PYTHONDEBUG`. +.. cmdoption:: -e + + Set the default value of the *cloexec* parameter to ``True``: see the + :ref:`close-on-exec flag `. + + .. versionadded:: 3.4 + + .. cmdoption:: -E Ignore all :envvar:`PYTHON*` environment variables, e.g. @@ -559,6 +567,17 @@ conflict. Python traceback. This is equivalent to :option:`-X` ``faulthandler`` option. + .. versionadded:: 3.3 + +.. envvar:: PYTHONCLOEXEC + + If this is set, set the default value of the *cloexec* parameter to + ``True``: see the :ref:`close-on-exec flag `. This is equivalent to + :option:`-e` option. + + .. versionadded:: 3.4 + + Debug-mode variables ~~~~~~~~~~~~~~~~~~~~ diff -r f87d85f7596c -r bc88690df059 Include/fileutils.h --- a/Include/fileutils.h Wed Jan 16 15:19:16 2013 +0100 +++ b/Include/fileutils.h Tue Jan 29 13:35:00 2013 +0100 @@ -5,6 +5,8 @@ extern "C" { #endif +PyAPI_DATA(int) Py_DefaultCloexec; + PyAPI_FUNC(PyObject *) _Py_device_encoding(int); PyAPI_FUNC(wchar_t *) _Py_char2wchar( @@ -27,6 +29,14 @@ PyAPI_FUNC(int) _Py_stat( struct stat *statbuf); #endif +PyAPI_FUNC(int) _Py_open( + const char *pathname, + int flags); + +PyAPI_FUNC(int) _Py_open_cloexec( + const char *pathname, + int flags); + PyAPI_FUNC(FILE *) _Py_wfopen( const wchar_t *path, const wchar_t *mode); @@ -53,6 +63,16 @@ PyAPI_FUNC(wchar_t*) _Py_wgetcwd( wchar_t *buf, size_t size); +PyAPI_FUNC(int) _Py_get_cloexec(int fd); + +PyAPI_FUNC(int) _Py_set_cloexec(int fd, int cloexec, int *atomic_flag_works); + +PyAPI_FUNC(void) _Py_try_set_cloexec(int fd, int cloexec); + +PyAPI_FUNC(void) _Py_try_set_default_cloexec(int fd); + +PyAPI_FUNC(int) _Py_cloexec_converter(PyObject* arg, void* addr); + #ifdef __cplusplus } #endif diff -r f87d85f7596c -r bc88690df059 Lib/_pyio.py --- a/Lib/_pyio.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/_pyio.py Tue Jan 29 13:35:00 2013 +0100 @@ -32,7 +32,7 @@ BlockingIOError = BlockingIOError def open(file, mode="r", buffering=-1, encoding=None, errors=None, - newline=None, closefd=True, opener=None): + newline=None, closefd=True, opener=None, cloexec=None): r"""Open file and return a stream. Raise OSError upon failure. @@ -135,6 +135,8 @@ def open(file, mode="r", buffering=-1, e descriptor (passing os.open as *opener* results in functionality similar to passing None). + If cloexec is True, set the close-on-exec flag. + open() returns a file object whose type depends on the mode, and through which the standard file operations such as reading and writing are performed. When open() is used to open a file in a text mode ('w', @@ -191,7 +193,7 @@ def open(file, mode="r", buffering=-1, e (writing and "w" or "") + (appending and "a" or "") + (updating and "+" or ""), - closefd, opener=opener) + closefd, opener=opener, cloexec=cloexec) line_buffering = False if buffering == 1 or buffering < 0 and raw.isatty(): buffering = -1 diff -r f87d85f7596c -r bc88690df059 Lib/asyncore.py --- a/Lib/asyncore.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/asyncore.py Tue Jan 29 13:35:00 2013 +0100 @@ -284,9 +284,9 @@ class dispatcher: del map[fd] self._fileno = None - def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): + def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM, cloexec=None): self.family_and_type = family, type - sock = socket.socket(family, type) + sock = socket.socket(family, type, cloexec=cloexec) sock.setblocking(0) self.set_socket(sock) diff -r f87d85f7596c -r bc88690df059 Lib/http/server.py --- a/Lib/http/server.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/http/server.py Tue Jan 29 13:35:00 2013 +0100 @@ -1125,8 +1125,8 @@ class CGIHTTPRequestHandler(SimpleHTTPRe os.setuid(nobody) except OSError: pass - os.dup2(self.rfile.fileno(), 0) - os.dup2(self.wfile.fileno(), 1) + os.dup2(self.rfile.fileno(), 0, cloexec=False) + os.dup2(self.wfile.fileno(), 1, cloexec=False) os.execve(scriptfile, args, env) except: self.server.handle_error(self.request, self.client_address) diff -r f87d85f7596c -r bc88690df059 Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/multiprocessing/connection.py Tue Jan 29 13:35:00 2013 +0100 @@ -529,7 +529,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 -r f87d85f7596c -r bc88690df059 Lib/pty.py --- a/Lib/pty.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/pty.py Tue Jan 29 13:35:00 2013 +0100 @@ -112,9 +112,9 @@ def fork(): os.close(master_fd) # Slave becomes stdin/stdout/stderr of child. - os.dup2(slave_fd, STDIN_FILENO) - os.dup2(slave_fd, STDOUT_FILENO) - os.dup2(slave_fd, STDERR_FILENO) + os.dup2(slave_fd, STDIN_FILENO, cloexec=False) + os.dup2(slave_fd, STDOUT_FILENO, cloexec=False) + os.dup2(slave_fd, STDERR_FILENO, cloexec=False) if (slave_fd > STDERR_FILENO): os.close (slave_fd) diff -r f87d85f7596c -r bc88690df059 Lib/socket.py --- a/Lib/socket.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/socket.py Tue Jan 29 13:35:00 2013 +0100 @@ -90,8 +90,8 @@ class socket(_socket.socket): __slots__ = ["__weakref__", "_io_refs", "_closed"] - def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): - _socket.socket.__init__(self, family, type, proto, fileno) + def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, cloexec=None): + _socket.socket.__init__(self, family, type, proto, fileno, cloexec) self._io_refs = 0 self._closed = False @@ -115,24 +115,25 @@ class socket(_socket.socket): def __getstate__(self): raise TypeError("Cannot serialize socket object") - def dup(self): + def dup(self, cloexec=None): """dup() -> socket object + Duplicate the socket. If cloexec is True, set the close-on-exec flag. Return a new socket object connected to the same system resource. """ - fd = dup(self.fileno()) + fd = dup(self.fileno(), cloexec=cloexec) sock = self.__class__(self.family, self.type, self.proto, fileno=fd) sock.settimeout(self.gettimeout()) return sock - def accept(self): + def accept(self, cloexec=None): """accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For IP sockets, the address info is a pair (hostaddr, port). """ - fd, addr = self._accept() + fd, addr = self._accept(cloexec=cloexec) sock = socket(self.family, self.type, self.proto, fileno=fd) # Issue #7995: if no default timeout is set and the listening # socket had a (non-zero) timeout, force the new socket in blocking @@ -210,13 +211,14 @@ class socket(_socket.socket): self._closed = True return super().detach() -def fromfd(fd, family, type, proto=0): +def fromfd(fd, family, type, proto=0, cloexec=None): """ fromfd(fd, family, type[, proto]) -> socket object Create a socket object from a duplicate of the given file descriptor. The remaining arguments are the same as for socket(). + If cloexec is True, set the close-on-exec flag. """ - nfd = dup(fd) + nfd = dup(fd, cloexec=cloexec) return socket(family, type, proto, nfd) if hasattr(_socket.socket, "share"): @@ -230,7 +232,7 @@ if hasattr(_socket.socket, "share"): if hasattr(_socket, "socketpair"): - def socketpair(family=None, type=SOCK_STREAM, proto=0): + def socketpair(family=None, type=SOCK_STREAM, proto=0, cloexec=None): """socketpair([family[, type[, proto]]]) -> (socket object, socket object) Create a pair of socket objects from the sockets returned by the platform @@ -243,7 +245,7 @@ if hasattr(_socket, "socketpair"): family = AF_UNIX except NameError: family = AF_INET - a, b = _socket.socketpair(family, type, proto) + a, b = _socket.socketpair(family, type, proto, cloexec=cloexec) a = socket(family, type, proto, a.detach()) b = socket(family, type, proto, b.detach()) return a, b diff -r f87d85f7596c -r bc88690df059 Lib/subprocess.py --- a/Lib/subprocess.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/subprocess.py Tue Jan 29 13:35:00 2013 +0100 @@ -400,7 +400,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. @@ -1230,7 +1229,7 @@ class Popen(object): if stdin is None: pass elif stdin == PIPE: - p2cread, p2cwrite = _create_pipe() + p2cread, p2cwrite = os.pipe(cloexec=True) elif stdin == DEVNULL: p2cread = self._get_devnull() elif isinstance(stdin, int): @@ -1242,7 +1241,7 @@ class Popen(object): if stdout is None: pass elif stdout == PIPE: - c2pread, c2pwrite = _create_pipe() + c2pread, c2pwrite = os.pipe(cloexec=True) elif stdout == DEVNULL: c2pwrite = self._get_devnull() elif isinstance(stdout, int): @@ -1254,7 +1253,7 @@ class Popen(object): if stderr is None: pass elif stderr == PIPE: - errread, errwrite = _create_pipe() + errread, errwrite = os.pipe(cloexec=True) elif stderr == STDOUT: errwrite = c2pwrite elif stderr == DEVNULL: @@ -1306,7 +1305,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(cloexec=True) try: try: # We must avoid complex work that could involve diff -r f87d85f7596c -r bc88690df059 Lib/tempfile.py --- a/Lib/tempfile.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/tempfile.py Tue Jan 29 13:35:00 2013 +0100 @@ -34,33 +34,12 @@ import os as _os 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 @@ -89,8 +68,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: @@ -210,8 +189,7 @@ def _mkstemp_inner(dir, pre, suf, flags) name = next(names) file = _os.path.join(dir, pre + name + suf) try: - fd = _os.open(file, flags, 0o600) - _set_cloexec(fd) + fd = _os.open(file, flags, 0o600, cloexec=True) return (fd, _os.path.abspath(file)) except FileExistsError: continue # try again diff -r f87d85f7596c -r bc88690df059 Lib/test/mock_socket.py --- a/Lib/test/mock_socket.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/mock_socket.py Tue Jan 29 13:35:00 2013 +0100 @@ -102,7 +102,7 @@ class MockSocket: pass -def socket(family=None, type=None, proto=None): +def socket(family=None, type=None, proto=None, cloexec=None): return MockSocket() diff -r f87d85f7596c -r bc88690df059 Lib/test/regrtest.py --- a/Lib/test/regrtest.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/regrtest.py Tue Jan 29 13:35:00 2013 +0100 @@ -1054,7 +1054,7 @@ class saved_test_environment: 'sys.warnoptions', 'threading._dangling', 'multiprocessing.process._dangling', 'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES', - 'support.TESTFN', + 'support.TESTFN', 'sys_getdefaultcloexec', ) def get_sys_argv(self): @@ -1107,6 +1107,11 @@ class saved_test_environment: def restore_sys_gettrace(self, trace_fxn): sys.settrace(trace_fxn) + def get_sys_getdefaultcloexec(self): + return sys.getdefaultcloexec() + def restore_sys_getdefaultcloexec(self, trace_fxn): + sys.setdefaultcloexec(trace_fxn) + def get___import__(self): return builtins.__import__ def restore___import__(self, import_): diff -r f87d85f7596c -r bc88690df059 Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_asyncore.py Tue Jan 29 13:35:00 2013 +0100 @@ -739,7 +739,8 @@ class BaseTestAPI: def test_create_socket(self): s = asyncore.dispatcher() - s.create_socket(self.family) + # disable cloexec to avoid SOCK_CLOEXEC flag in the type attribute + s.create_socket(self.family, cloexec=False) self.assertEqual(s.socket.family, self.family) SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0) self.assertEqual(s.socket.type, socket.SOCK_STREAM | SOCK_NONBLOCK) diff -r f87d85f7596c -r bc88690df059 Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_builtin.py Tue Jan 29 13:35:00 2013 +0100 @@ -943,29 +943,24 @@ class BuiltinTest(unittest.TestCase): def write_testfile(self): # NB the first 4 lines are also used to test input, below fp = open(TESTFN, 'w') - try: + self.addCleanup(unlink, TESTFN) + with fp: fp.write('1+1\n') fp.write('The quick brown fox jumps over the lazy dog') fp.write('.\n') fp.write('Dear John\n') fp.write('XXX'*100) fp.write('YYY'*100) - finally: - fp.close() def test_open(self): self.write_testfile() - fp = open(TESTFN, 'r') - try: + with open(TESTFN, 'r') as fp: self.assertEqual(fp.readline(4), '1+1\n') self.assertEqual(fp.readline(), 'The quick brown fox jumps over the lazy dog.\n') self.assertEqual(fp.readline(4), 'Dear') self.assertEqual(fp.readline(100), ' John\n') self.assertEqual(fp.read(300), 'XXX'*100) self.assertEqual(fp.read(1000), 'YYY'*100) - finally: - fp.close() - unlink(TESTFN) def test_open_default_encoding(self): old_environ = dict(os.environ) @@ -979,16 +974,29 @@ class BuiltinTest(unittest.TestCase): self.write_testfile() current_locale_encoding = locale.getpreferredencoding(False) - fp = open(TESTFN, 'w') - try: + with open(TESTFN, 'w') as fp: self.assertEqual(fp.encoding, current_locale_encoding) - finally: - fp.close() - unlink(TESTFN) finally: os.environ.clear() os.environ.update(old_environ) + @unittest.skipUnless(hasattr(os, "get_cloexec"), "need os.get_cloexec()") + def test_open_cloexec(self): + self.write_testfile() + old_cloexec = sys.getdefaultcloexec() + try: + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + fileobj = open(TESTFN) + else: + fileobj = open(TESTFN, cloexec=cloexec) + with fileobj: + self.assertEqual(os.get_cloexec(fileobj.fileno()), cloexec) + finally: + sys.setdefaultcloexec(old_cloexec) + def test_ord(self): self.assertEqual(ord(' '), 32) self.assertEqual(ord('A'), 65) diff -r f87d85f7596c -r bc88690df059 Lib/test/test_bytes.py --- a/Lib/test/test_bytes.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_bytes.py Tue Jan 29 13:35:00 2013 +0100 @@ -700,7 +700,9 @@ class BytesTest(BaseBytesTest, unittest. type2test = bytes def test_buffer_is_readonly(self): - fd = os.dup(sys.stdin.fileno()) + # On Windows, setting HANDLE_FLAG_INHERIT flag on a standard stream + # fails with ERROR_INVALID_PARAMETER (87) + fd = os.dup(sys.stdin.fileno(), cloexec=False) with open(fd, "rb", buffering=0) as f: self.assertRaises(TypeError, f.readinto, b"") diff -r f87d85f7596c -r bc88690df059 Lib/test/test_epoll.py --- a/Lib/test/test_epoll.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_epoll.py Tue Jan 29 13:35:00 2013 +0100 @@ -80,7 +80,7 @@ class TestEPoll(unittest.TestCase): self.assertRaises(OSError, select.epoll, flags=12356) def test_badcreate(self): - self.assertRaises(TypeError, select.epoll, 1, 2, 3) + self.assertRaises(TypeError, select.epoll, 1, 2, 3, 4) self.assertRaises(TypeError, select.epoll, 'foo') self.assertRaises(TypeError, select.epoll, None) self.assertRaises(TypeError, select.epoll, ()) diff -r f87d85f7596c -r bc88690df059 Lib/test/test_os.py --- a/Lib/test/test_os.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_os.py Tue Jan 29 13:35:00 2013 +0100 @@ -2201,6 +2201,113 @@ class OSErrorTests(unittest.TestCase): else: self.fail("No exception thrown by {}".format(func)) + +@unittest.skipUnless(hasattr(os, 'get_cloexec'), "need os.get_cloexec()") +class CloexecTests(unittest.TestCase): + def setUp(self): + self.old_cloexec = sys.getdefaultcloexec() + + def tearDown(self): + sys.setdefaultcloexec(self.old_cloexec) + + def create_testfile(self): + fileobj = open(support.TESTFN, 'xb') + self.addCleanup(support.unlink, support.TESTFN) + fileobj.close() + + def test_get_cloexec(self): + self.create_testfile() + + for cloexec in (False, True): + fd = os.open(support.TESTFN, os.O_RDONLY, cloexec=cloexec) + self.assertEqual(os.get_cloexec(fd), cloexec) + os.close(fd) + + def test_set_cloexec(self): + self.create_testfile() + + for cloexec in (False, True): + fd = os.open(support.TESTFN, os.O_RDONLY, cloexec=not cloexec) + os.set_cloexec(fd, cloexec) + self.assertEqual(os.get_cloexec(fd), cloexec) + os.close(fd) + + def test_open(self): + self.create_testfile() + + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + fd = os.open(support.TESTFN, os.O_RDONLY) + else: + fd = os.open(support.TESTFN, os.O_RDONLY, cloexec=cloexec) + self.assertEqual(os.get_cloexec(fd), cloexec) + os.close(fd) + + @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()") + def test_pipe_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + rfd, wfd = os.pipe() + else: + rfd, wfd = os.pipe(cloexec=cloexec) + self.assertEqual(os.get_cloexec(rfd), cloexec) + self.assertEqual(os.get_cloexec(wfd), cloexec) + os.close(rfd) + os.close(wfd) + + @unittest.skipUnless(hasattr(os, 'dup'), "need os.dup()") + def test_dup_cloexec(self): + self.create_testfile() + + for cloexec in (False, True): + for set_default in (False, True): + fd1 = os.open(support.TESTFN, os.O_RDONLY, + cloexec=not cloexec) + if set_default: + sys.setdefaultcloexec(cloexec) + fd2 = os.dup(fd1) + else: + fd2 = os.dup(fd1, cloexec=cloexec) + self.assertEqual(os.get_cloexec(fd2), cloexec) + os.close(fd1) + os.close(fd2) + + @unittest.skipUnless(hasattr(os, 'dup2'), "need os.dup2()") + def test_dup2_cloexec(self): + self.create_testfile() + + for cloexec in (False, True): + for set_default in (False, True): + fd1 = os.open(support.TESTFN, os.O_RDONLY, cloexec=not cloexec) + fd2 = os.open(support.TESTFN, os.O_RDONLY, cloexec=not cloexec) + if set_default: + sys.setdefaultcloexec(cloexec) + os.dup2(fd1, fd2) + else: + os.dup2(fd1, fd2, cloexec=cloexec) + self.assertEqual(os.get_cloexec(fd2), cloexec) + os.close(fd1) + os.close(fd2) + + @unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") + def test_openpty_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + master_fd, slave_fd = os.openpty() + else: + master_fd, slave_fd = os.openpty(cloexec=cloexec) + self.assertEqual(os.get_cloexec(master_fd), cloexec) + self.assertEqual(os.get_cloexec(slave_fd), cloexec) + os.close(master_fd) + os.close(slave_fd) + + @support.reap_threads def test_main(): support.run_unittest( @@ -2231,6 +2338,7 @@ def test_main(): TermsizeTests, OSErrorTests, RemoveDirsTests, + CloexecTests, ) if __name__ == "__main__": diff -r f87d85f7596c -r bc88690df059 Lib/test/test_select.py --- a/Lib/test/test_select.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_select.py Tue Jan 29 13:35:00 2013 +0100 @@ -75,8 +75,56 @@ class SelectTestCase(unittest.TestCase): a[:] = [F()] * 10 self.assertEqual(select.select([], a, []), ([], a[:5], [])) + +@unittest.skipUnless(hasattr(os, 'get_cloexec'), "need os.get_cloexec()") +class CloexecTests(unittest.TestCase): + def setUp(self): + self.old_cloexec = sys.getdefaultcloexec() + + def tearDown(self): + sys.setdefaultcloexec(self.old_cloexec) + + @unittest.skipUnless(hasattr(select, 'devpoll'), "need select.devpoll()") + def test_devpoll_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + devpoll = select.devpoll() + else: + devpoll = select.devpoll(cloexec=cloexec) + self.assertEqual(os.get_cloexec(devpoll.fileno()), + cloexec) + devpoll = None + + @unittest.skipUnless(hasattr(select, 'epoll'), "need select.epoll()") + def test_epoll_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + epoll = select.epoll() + else: + epoll = select.epoll(cloexec=cloexec) + with epoll: + self.assertEqual(os.get_cloexec(epoll.fileno()), + cloexec) + + @unittest.skipUnless(hasattr(select, 'kqueue'), "need select.kqueue()") + def test_kqueue_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + kqueue = select.kqueue() + else: + kqueue = select.kqueue(cloexec=cloexec) + self.assertEqual(os.get_cloexec(kqueue.fileno()), + cloexec) + + def test_main(): - support.run_unittest(SelectTestCase) + support.run_unittest(SelectTestCase, CloexecTests) support.reap_children() if __name__ == "__main__": diff -r f87d85f7596c -r bc88690df059 Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_shutil.py Tue Jan 29 13:35:00 2013 +0100 @@ -1527,7 +1527,7 @@ class TestCopyFile(unittest.TestCase): self._delete = True def test_w_source_open_fails(self): - def _open(filename, mode='r'): + def _open(filename, mode='r', cloexec=None): if filename == 'srcfile': raise OSError('Cannot open "srcfile"') assert 0 # shouldn't reach here. @@ -1540,7 +1540,7 @@ class TestCopyFile(unittest.TestCase): srcfile = self.Faux() - def _open(filename, mode='r'): + def _open(filename, mode='r', cloexec=None): if filename == 'srcfile': return srcfile if filename == 'destfile': @@ -1560,7 +1560,7 @@ class TestCopyFile(unittest.TestCase): srcfile = self.Faux() destfile = self.Faux(True) - def _open(filename, mode='r'): + def _open(filename, mode='r', cloexec=None): if filename == 'srcfile': return srcfile if filename == 'destfile': @@ -1582,7 +1582,7 @@ class TestCopyFile(unittest.TestCase): srcfile = self.Faux(True) destfile = self.Faux() - def _open(filename, mode='r'): + def _open(filename, mode='r', cloexec=None): if filename == 'srcfile': return srcfile if filename == 'destfile': diff -r f87d85f7596c -r bc88690df059 Lib/test/test_socket.py --- a/Lib/test/test_socket.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_socket.py Tue Jan 29 13:35:00 2013 +0100 @@ -24,10 +24,6 @@ import math import pickle import struct try: - import fcntl -except ImportError: - fcntl = False -try: import multiprocessing except ImportError: multiprocessing = False @@ -1073,7 +1069,9 @@ class GeneralModuleTests(unittest.TestCa def testNewAttributes(self): # testing .family, .type and .protocol - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # Disable cloexec to avoid SOCK_CLOEXEC flag in the type attribute + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, cloexec=False) self.assertEqual(sock.family, socket.AF_INET) self.assertEqual(sock.type, socket.SOCK_STREAM) self.assertEqual(sock.proto, 0) @@ -4653,16 +4651,69 @@ class ContextManagersTest(ThreadedTCPSoc 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): +@unittest.skipUnless(hasattr(os, 'get_cloexec'), "need os.get_cloexec()") +class CloexecTest(unittest.TestCase): + def setUp(self): + self.old_cloexec = sys.getdefaultcloexec() + + def tearDown(self): + sys.setdefaultcloexec(self.old_cloexec) + + @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(os.get_cloexec(sock.fileno())) + + def test_socket_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + sock = socket.socket() + else: + sock = socket.socket(cloexec=cloexec) + with sock: + self.assertEqual(os.get_cloexec(sock.fileno()), cloexec) + + def test_dup_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + sock = socket.socket(cloexec=not cloexec) + if set_default: + sys.setdefaultcloexec(cloexec) + newsock = sock.dup() + else: + newsock = sock.dup(cloexec=cloexec) + sock.close() + self.assertEqual(os.get_cloexec(newsock.fileno()), cloexec) + newsock.close() + + def test_socket_set_cloexec(self): + sock = socket.socket(cloexec=False) + with sock: + # On Windows, fileno() returns an handle, not a file descriptor. + # Check if it is accepted. + os.set_cloexec(sock.fileno()) + self.assertEqual(os.get_cloexec(sock.fileno()), True) + + @unittest.skipUnless(hasattr(socket, "socketpair"), + "need socket.socketpair()") + def test_socketpair_cloexec(self): + for cloexec in (False, True): + for set_default in (False, True): + if set_default: + sys.setdefaultcloexec(cloexec) + s1, s2 = socket.socketpair() + else: + s1, s2 = socket.socketpair(cloexec=cloexec) + self.assertEqual(os.get_cloexec(s1.fileno()), cloexec) + self.assertEqual(os.get_cloexec(s2.fileno()), cloexec) + s1.close() + s2.close() @unittest.skipUnless(hasattr(socket, "SOCK_NONBLOCK"), @@ -4831,7 +4882,7 @@ def test_main(): NetworkConnectionAttributesTest, NetworkConnectionBehaviourTest, ContextManagersTest, - CloexecConstantTest, + CloexecTest, NonblockConstantTest ]) if hasattr(socket, "socketpair"): diff -r f87d85f7596c -r bc88690df059 Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_subprocess.py Tue Jan 29 13:35:00 2013 +0100 @@ -1438,7 +1438,7 @@ class POSIXProcessTestCase(BaseTestCase) self.assertEqual((out, err), (b'apple', b'orange')) finally: for b, a in zip(newfds, fds): - os.dup2(b, a) + os.dup2(b, a, cloexec=False) for b in newfds: os.close(b) @@ -1498,7 +1498,7 @@ class POSIXProcessTestCase(BaseTestCase) finally: # restore the original fd's underneath sys.stdin, etc. for std, saved in enumerate(saved_fds): - os.dup2(saved, std) + os.dup2(saved, std, cloexec=False) os.close(saved) for fd in temp_fds: @@ -1550,7 +1550,7 @@ class POSIXProcessTestCase(BaseTestCase) err = support.strip_python_stderr(os.read(stderr_no, 1024)) finally: for std, saved in enumerate(saved_fds): - os.dup2(saved, std) + os.dup2(saved, std, cloexec=False) os.close(saved) self.assertEqual(out, b"got STDIN") @@ -1711,14 +1711,14 @@ class POSIXProcessTestCase(BaseTestCase) def test_close_fds(self): fd_status = support.findfile("fd_status.py", subdir="subprocessdata") - fds = os.pipe() + fds = os.pipe(cloexec=False) self.addCleanup(os.close, fds[0]) self.addCleanup(os.close, fds[1]) open_fds = set(fds) # add a bunch more fds for _ in range(9): - fd = os.open("/dev/null", os.O_RDONLY) + fd = os.open("/dev/null", os.O_RDONLY, cloexec=False) self.addCleanup(os.close, fd) open_fds.add(fd) @@ -1763,7 +1763,7 @@ class POSIXProcessTestCase(BaseTestCase) open_fds = set() for x in range(5): - fds = os.pipe() + fds = os.pipe(cloexec=False) self.addCleanup(os.close, fds[0]) self.addCleanup(os.close, fds[1]) open_fds.update(fds) diff -r f87d85f7596c -r bc88690df059 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/test/test_sys.py Tue Jan 29 13:35:00 2013 +0100 @@ -8,6 +8,7 @@ import operator import codecs import gc import sysconfig +from test.script_helper import assert_python_ok # count the number of test runs, used to create unique # strings to intern in test_intern() @@ -608,7 +609,6 @@ class SysModuleTest(unittest.TestCase): def test_debugmallocstats(self): # Test sys._debugmallocstats() - from test.script_helper import assert_python_ok args = ['-c', 'import sys; sys._debugmallocstats()'] ret, out, err = assert_python_ok(*args) self.assertIn(b"free PyDictObjects", err) @@ -642,6 +642,35 @@ class SysModuleTest(unittest.TestCase): c = sys.getallocatedblocks() self.assertIn(c, range(b - 50, b + 50)) + def test_getdefaultcloexec_cmdline(self): + code = 'import sys; print(sys.getdefaultcloexec())' + + ok, stdout, stderr = assert_python_ok('-c', code) + self.assertEqual(stdout.rstrip(), b'False') + + ok, stdout, stderr = assert_python_ok('-c', code, PYTHONCLOEXEC='1') + self.assertEqual(stdout.rstrip(), b'True') + + ok, stdout, stderr = assert_python_ok('-E', '-c', code, PYTHONCLOEXEC='1') + self.assertEqual(stdout.rstrip(), b'False') + + ok, stdout, stderr = assert_python_ok('-e', '-c', code) + self.assertEqual(stdout.rstrip(), b'True') + + def test_setdefaultcloexec(self): + old_cloexec = sys.getdefaultcloexec() + try: + sys.setdefaultcloexec() + self.assertEqual(sys.getdefaultcloexec(), True) + + sys.setdefaultcloexec(True) + self.assertEqual(sys.getdefaultcloexec(), True) + + sys.setdefaultcloexec(False) + self.assertEqual(sys.getdefaultcloexec(), False) + finally: + sys.setdefaultcloexec(old_cloexec) + class SizeofTest(unittest.TestCase): diff -r f87d85f7596c -r bc88690df059 Lib/xmlrpc/server.py --- a/Lib/xmlrpc/server.py Wed Jan 16 15:19:16 2013 +0100 +++ b/Lib/xmlrpc/server.py Tue Jan 29 13:35:00 2013 +0100 @@ -587,10 +587,8 @@ class SimpleXMLRPCServer(socketserver.TC # [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) + if hasattr(os, 'set_cloexec'): + os.set_cloexec(self.fileno()) class MultiPathXMLRPCServer(SimpleXMLRPCServer): """Multipath XML-RPC Server diff -r f87d85f7596c -r bc88690df059 Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/_cursesmodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -1701,6 +1701,7 @@ PyCursesWindow_PutWin(PyCursesWindowObje fd = mkstemp(fn); if (fd < 0) return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + _Py_try_set_cloexec(fd, 1); fp = fdopen(fd, "wb+"); if (fp == NULL) { close(fd); @@ -2264,6 +2265,7 @@ PyCurses_GetWin(PyCursesWindowObject *se fd = mkstemp(fn); if (fd < 0) return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + _Py_try_set_cloexec(fd, 1); fp = fdopen(fd, "wb+"); if (fp == NULL) { close(fd); diff -r f87d85f7596c -r bc88690df059 Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/_io/_iomodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -94,8 +94,8 @@ PyDoc_STRVAR(module_doc, * The main open() function */ PyDoc_STRVAR(open_doc, -"open(file, mode='r', buffering=-1, encoding=None,\n" -" errors=None, newline=None, closefd=True, opener=None) -> file object\n" +"open(file, mode='r', buffering=-1, encoding=None, errors=None,\n" +" newline=None, closefd=True, opener=None, cloexec=None) -> file object\n" "\n" "Open file and return a stream. Raise IOError upon failure.\n" "\n" @@ -199,6 +199,8 @@ PyDoc_STRVAR(open_doc, "file descriptor (passing os.open as *opener* results in functionality\n" "similar to passing None).\n" "\n" +"If cloexec is True, set the close-on-exec flag.\n" +"\n" "open() returns a file object whose type depends on the mode, and\n" "through which the standard file operations such as reading and writing\n" "are performed. When open() is used to open a file in a text mode ('w',\n" @@ -219,8 +221,9 @@ io_open(PyObject *self, PyObject *args, { char *kwlist[] = {"file", "mode", "buffering", "encoding", "errors", "newline", - "closefd", "opener", NULL}; + "closefd", "opener", "cloexec", NULL}; PyObject *file, *opener = Py_None; + int cloexec = Py_DefaultCloexec; char *mode = "r"; int buffering = -1, closefd = 1; char *encoding = NULL, *errors = NULL, *newline = NULL; @@ -238,10 +241,11 @@ io_open(PyObject *self, PyObject *args, _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziOO&:open", kwlist, &file, &mode, &buffering, &encoding, &errors, &newline, - &closefd, &opener)) { + &closefd, &opener, + _Py_cloexec_converter, &cloexec)) { return NULL; } @@ -345,7 +349,7 @@ io_open(PyObject *self, PyObject *args, /* Create the Raw file stream */ raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type, - "OsiO", file, rawmode, closefd, opener); + "OsiOi", file, rawmode, closefd, opener, cloexec); if (raw == NULL) return NULL; diff -r f87d85f7596c -r bc88690df059 Modules/_io/fileio.c --- a/Modules/_io/fileio.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/_io/fileio.c Tue Jan 29 13:35:00 2013 +0100 @@ -202,14 +202,18 @@ check_fd(int fd) return 0; } +#ifdef O_CLOEXEC +extern int _Py_open_cloexec_works; +#endif static int fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) { fileio *self = (fileio *) oself; - static char *kwlist[] = {"file", "mode", "closefd", "opener", NULL}; + static char *kwlist[] = {"file", "mode", "closefd", "opener", "cloexec", NULL}; const char *name = NULL; PyObject *nameobj, *stringobj = NULL, *opener = Py_None; + int cloexec = Py_DefaultCloexec; char *mode = "r"; char *s; #ifdef MS_WINDOWS @@ -221,6 +225,11 @@ fileio_init(PyObject *oself, PyObject *a int fd = -1; int closefd = 1; int fd_is_own = 0; +#ifdef O_CLOEXEC + int *atomic_flag_works = &_Py_open_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -233,9 +242,10 @@ fileio_init(PyObject *oself, PyObject *a self->fd = -1; } - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|siO:fileio", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|siOO&:fileio", kwlist, &nameobj, &mode, &closefd, - &opener)) + &opener, + _Py_cloexec_converter, &cloexec)) return -1; if (PyFloat_Check(nameobj)) { @@ -345,6 +355,13 @@ fileio_init(PyObject *oself, PyObject *a if (append) flags |= O_APPEND; #endif + if (cloexec) { +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif + } if (fd >= 0) { if (check_fd(fd)) @@ -369,10 +386,16 @@ fileio_init(PyObject *oself, PyObject *a else #endif self->fd = open(name, flags, 0666); + Py_END_ALLOW_THREADS - } else { - PyObject *fdobj = PyObject_CallFunction( - opener, "Oi", nameobj, flags); + } + else { + PyObject *fdobj; + + /* the opener may clear the atomic flag */ + atomic_flag_works = NULL; + + fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags); if (fdobj == NULL) goto error; if (!PyLong_Check(fdobj)) { @@ -394,6 +417,12 @@ fileio_init(PyObject *oself, PyObject *a PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); goto error; } + +#ifndef MS_WINDOWS + if (cloexec + && _Py_set_cloexec(self->fd, 1, atomic_flag_works) < 0) + goto error; +#endif } if (dircheck(self, nameobj) < 0) goto error; diff -r f87d85f7596c -r bc88690df059 Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/_posixsubprocess.c Tue Jan 29 13:35:00 2013 +0100 @@ -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 @@ static int 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; @@ -211,18 +211,8 @@ static void 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_cloexec(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); @@ -363,15 +353,12 @@ child_exec(char *const exec_array[], char hex_errno[sizeof(saved_errno)*2+1]; /* 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 @@ -384,39 +371,27 @@ child_exec(char *const exec_array[], /* Dup fds for child. 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 (p2cread == 0) + _Py_try_set_cloexec(p2cread, 0); + else if (p2cread != -1) POSIX_CALL(dup2(p2cread, 0)); /* stdin */ - } - if (c2pwrite == 1) { - int old = fcntl(c2pwrite, F_GETFD); - if (old != -1) - fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); - } else if (c2pwrite != -1) { + if (c2pwrite == 1) + _Py_try_set_cloexec(c2pwrite, 0); + else if (c2pwrite != -1) POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ - } - if (errwrite == 2) { - int old = fcntl(errwrite, F_GETFD); - if (old != -1) - fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); - } else if (errwrite != -1) { + if (errwrite == 2) + _Py_try_set_cloexec(errwrite, 0); + 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; @@ -549,7 +524,7 @@ subprocess_fork_exec(PyObject* self, PyO PyObject *result; _Py_IDENTIFIER(isenabled); _Py_IDENTIFIER(disable); - + gc_module = PyImport_ImportModule("gc"); if (gc_module == NULL) return NULL; @@ -726,52 +701,6 @@ Returns: the child process's PID.\n\ 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, @@ -780,7 +709,6 @@ PyDoc_STRVAR(module_doc, 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 f87d85f7596c -r bc88690df059 Modules/main.c --- a/Modules/main.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/main.c Tue Jan 29 13:35:00 2013 +0100 @@ -22,6 +22,10 @@ #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + #if defined(MS_WINDOWS) #define PYTHONHOMEHELP "\\lib" #else @@ -43,7 +47,7 @@ static wchar_t **orig_argv; static int orig_argc; /* command line options */ -#define BASE_OPTS L"bBc:dEhiJm:OqRsStuvVW:xX:?" +#define BASE_OPTS L"bBc:deEhiJm:OqRsStuvVW:xX:?" #define PROGRAM_OPTS BASE_OPTS @@ -59,6 +63,8 @@ Options and arguments (and corresponding -B : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x\n\ -c cmd : program passed in as string (terminates option list)\n\ -d : debug output from parser; also PYTHONDEBUG=x\n\ +-e : set default value of the cloexec (close-on-exec) parameter\n\ + to True; also PYTHONCLOEXEC=1\n\ -E : ignore PYTHON* environment variables (such as PYTHONPATH)\n\ -h : print this help message and exit (also --help)\n\ "; @@ -104,6 +110,8 @@ PYTHONHASHSEED: if this variable is set to seed the hashes of str, bytes and datetime objects. It can also be\n\ set to an integer in the range [0,4294967295] to get hash values with a\n\ predictable seed.\n\ +PYTHONCLOEXEC: set default value of the cloexec (close-on-exec) parameter\n\ + to True.\n\ "; static int @@ -141,7 +149,12 @@ static void RunStartupFile(PyCompilerFla { char *startup = Py_GETENV("PYTHONSTARTUP"); if (startup != NULL && startup[0] != '\0') { - FILE *fp = fopen(startup, "r"); + FILE *fp; + int fd = _Py_open(startup, O_RDONLY); + if (fd >= 0) + fp = fdopen(fd, "r"); + else + fp = NULL; if (fp != NULL) { (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); PyErr_Clear(); @@ -403,6 +416,10 @@ Py_Main(int argc, wchar_t **argv) Py_DontWriteBytecodeFlag++; break; + case 'e': + Py_DefaultCloexec = 1; + break; + case 's': Py_NoUserSiteDirectory++; break; diff -r f87d85f7596c -r bc88690df059 Modules/mmapmodule.c --- a/Modules/mmapmodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/mmapmodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -1205,7 +1205,7 @@ new_mmap_object(PyTypeObject *type, PyOb 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); @@ -1219,6 +1219,7 @@ new_mmap_object(PyTypeObject *type, PyOb PyErr_SetFromErrno(PyExc_OSError); return NULL; } + _Py_try_set_default_cloexec(m_obj->fd); } m_obj->data = mmap(NULL, map_size, diff -r f87d85f7596c -r bc88690df059 Modules/ossaudiodev.c --- a/Modules/ossaudiodev.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/ossaudiodev.c Tue Jan 29 13:35:00 2013 +0100 @@ -115,7 +115,9 @@ newossobject(PyObject *arg) 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 @@ newossmixerobject(PyObject *arg) 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 f87d85f7596c -r bc88690df059 Modules/posixmodule.c --- a/Modules/posixmodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/posixmodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -3298,6 +3298,8 @@ exit: /* closedir() closes the FD, so we duplicate it */ Py_BEGIN_ALLOW_THREADS fd = dup(path.fd); + if (fd != -1) + _Py_try_set_default_cloexec(fd); Py_END_ALLOW_THREADS if (fd == -1) { @@ -5447,14 +5449,17 @@ error: #if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) PyDoc_STRVAR(posix_openpty__doc__, -"openpty() -> (master_fd, slave_fd)\n\n\ -Open a pseudo-terminal, returning open fd's for both master and slave end.\n"); - -static PyObject * -posix_openpty(PyObject *self, PyObject *noargs) -{ - int master_fd, slave_fd; +"openpty(cloexec=None) -> (master_fd, slave_fd)\n\n\ +Open a pseudo-terminal, returning open fd's for both master and slave end.\n\ +If cloexec is True, set the close-on-exec flag."); + +static PyObject * +posix_openpty(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *keywords[] = {"cloexec", NULL}; + int master_fd = -1, slave_fd = -1; #ifndef HAVE_OPENPTY + int flags; char * slave_name; #endif #if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) @@ -5463,22 +5468,51 @@ posix_openpty(PyObject *self, PyObject * extern char *ptsname(int fildes); #endif #endif + int cloexec = Py_DefaultCloexec; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:openpty", keywords, + _Py_cloexec_converter, &cloexec)) + return NULL; #ifdef HAVE_OPENPTY if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) return posix_error(); + + if (cloexec && _Py_set_cloexec(master_fd, 1, NULL) < 0) + goto error; + if (cloexec && _Py_set_cloexec(slave_fd, 1, 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); + if (cloexec && _Py_set_cloexec(master_fd, 1, NULL) < 0) + goto error; + + flags = O_RDWR; +#ifdef O_CLOEXEC + if (cloexec) + flags |= O_CLOEXEC; +#endif + slave_fd = open(slave_name, flags); if (slave_fd < 0) return posix_error(); -#else - master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ + if (cloexec && _Py_set_cloexec(slave_fd, 1, NULL) < 0) + goto error; + +#else + flags = O_RDWR | O_NOCTTY; +#ifdef O_CLOEXEC + if (cloexec) + flags |= O_CLOEXEC; +#endif + master_fd = open(DEV_PTY_FILE, flags); /* open master */ if (master_fd < 0) return posix_error(); + if (cloexec && _Py_set_cloexec(master_fd, 1, NULL) < 0) + goto error; + sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); /* change permission of slave */ if (grantpt(master_fd) < 0) { @@ -5494,9 +5528,16 @@ posix_openpty(PyObject *self, PyObject * 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 */ + flags = O_RDWR | O_NOCTTY; +#ifdef O_CLOEXEC + if (cloexec) + flags |= O_CLOEXEC; +#endif + slave_fd = open(slave_name, flags); /* open slave */ if (slave_fd < 0) return posix_error(); + if (cloexec && _Py_set_cloexec(slave_fd, 1, NULL) < 0) + goto error; #if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC) ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ @@ -5508,6 +5549,12 @@ posix_openpty(PyObject *self, PyObject * return Py_BuildValue("(ii)", master_fd, slave_fd); +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) */ @@ -6992,14 +7039,20 @@ posix_tcsetpgrp(PyObject *self, PyObject /* 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(path, flags, mode=0o777, *, dir_fd=None, cloexec=None)\n\n\ Open a file for low level IO. Returns a file handle (integer).\n\ \n\ If dir_fd is not None, it should be a file descriptor open to a directory,\n\ and path should be relative; path will then be relative to that directory.\n\ dir_fd may not be implemented on your platform.\n\ - If it is unavailable, using it will raise a NotImplementedError."); + If it is unavailable, using it will raise a NotImplementedError.\n\ +\n\ +If cloexec is True, set the close-on-exec flag."); static PyObject * posix_open(PyObject *self, PyObject *args, PyObject *kwargs) @@ -7008,22 +7061,31 @@ posix_open(PyObject *self, PyObject *arg int flags; int mode = 0777; int dir_fd = DEFAULT_DIR_FD; + int cloexec = Py_DefaultCloexec; int fd; PyObject *return_value = NULL; - static char *keywords[] = {"path", "flags", "mode", "dir_fd", NULL}; + static char *keywords[] = {"path", "flags", "mode", "dir_fd", "cloexec", NULL}; memset(&path, 0, sizeof(path)); path.function_name = "open"; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|i$O&:open", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|i$O&O&:open", keywords, path_converter, &path, &flags, &mode, #ifdef HAVE_OPENAT - dir_fd_converter, &dir_fd -#else - dir_fd_unavailable, &dir_fd -#endif - )) - return NULL; + dir_fd_converter, &dir_fd, +#else + dir_fd_unavailable, &dir_fd, +#endif + _Py_cloexec_converter, &cloexec)) + return NULL; + + if (cloexec) { +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif + } Py_BEGIN_ALLOW_THREADS #ifdef MS_WINDOWS @@ -7044,6 +7106,20 @@ posix_open(PyObject *self, PyObject *arg goto exit; } +#ifndef MS_WINDOWS + if (cloexec) { +#ifdef O_CLOEXEC + int *atomic_flag_works = &_Py_open_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif + if (_Py_set_cloexec(fd, 1, atomic_flag_works) < 0) { + close(fd); + goto exit; + } + } +#endif + return_value = PyLong_FromLong((long)fd); exit: @@ -7093,39 +7169,105 @@ posix_closerange(PyObject *self, PyObjec PyDoc_STRVAR(posix_dup__doc__, -"dup(fd) -> fd2\n\n\ -Return a duplicate of a file descriptor."); - -static PyObject * -posix_dup(PyObject *self, PyObject *args) -{ + "dup(fd, cloexec=None) -> fd2\n\n" + "Return a duplicate of a file descriptor.\n" + "If cloexec is True, set the close-on-exec flag."); + +static PyObject * +posix_dup(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"fd", "cloexec", NULL}; int fd; - if (!PyArg_ParseTuple(args, "i:dup", &fd)) - return NULL; + int cloexec = Py_DefaultCloexec; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O&:dup", keywords, + &fd, _Py_cloexec_converter, &cloexec)) + return NULL; + if (!_PyVerify_fd(fd)) return posix_error(); - fd = dup(fd); - if (fd < 0) - return posix_error(); +#if defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC) + if (cloexec) { + Py_BEGIN_ALLOW_THREADS + fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + } + else { +#endif + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + if (cloexec && _Py_set_cloexec(fd, 1, NULL) < 0) { + close(fd); + return NULL; + } +#if defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC) + } +#endif return PyLong_FromLong((long)fd); } PyDoc_STRVAR(posix_dup2__doc__, -"dup2(old_fd, new_fd)\n\n\ -Duplicate file descriptor."); - -static PyObject * -posix_dup2(PyObject *self, PyObject *args) -{ +"dup2(fd, fd2, cloexec=None)\n\n\ +Duplicate file descriptor. If cloexec is True, set the close-on-exec flag on fd2."); + +static PyObject * +posix_dup2(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *keywords[] = {"fd", "fd2", "cloexec", NULL}; int fd, fd2, res; - if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) + int cloexec = Py_DefaultCloexec; +#ifdef HAVE_DUP3 + /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */ + int dup3_works = -1; +#endif + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|O&:dup2", keywords, + &fd, &fd2, + _Py_cloexec_converter, &cloexec)) return NULL; if (!_PyVerify_fd_dup2(fd, fd2)) return posix_error(); - res = dup2(fd, fd2); - if (res < 0) - return posix_error(); + +#ifdef HAVE_DUP3 + if (dup3_works != 0) { + int flags; + if (cloexec) + flags = O_CLOEXEC; + else + flags = 0; + Py_BEGIN_ALLOW_THREADS + res = dup3(fd, fd2, flags); + 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 (cloexec && _Py_set_cloexec(fd2, 1, NULL) < 0) { + close(fd2); + return NULL; + } +#ifdef HAVE_DUP3 + } +#endif + Py_INCREF(Py_None); return Py_None; } @@ -7605,30 +7747,85 @@ posix_isatty(PyObject *self, PyObject *a #ifdef HAVE_PIPE PyDoc_STRVAR(posix_pipe__doc__, -"pipe() -> (read_end, write_end)\n\n\ -Create a pipe."); - -static PyObject * -posix_pipe(PyObject *self, PyObject *noargs) -{ -#if !defined(MS_WINDOWS) +"pipe() -> (read_end, write_end)\n\n" +"Create a pipe. If cloexec is True, set the close-on-exec flag."); + +static PyObject * +posix_pipe(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"cloexec", NULL}; int fds[2]; + int cloexec = Py_DefaultCloexec; +#ifdef MS_WINDOWS + HANDLE read, write; + SECURITY_ATTRIBUTES attr; + BOOL ok; +#else +#ifdef HAVE_PIPE2 + int flags; +#endif 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 + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&:pipe", keywords, + _Py_cloexec_converter, &cloexec)) + return NULL; + +#ifdef MS_WINDOWS + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = (cloexec ? FALSE : TRUE); + + Py_BEGIN_ALLOW_THREADS + ok = CreatePipe(&read, &write, &attr, 0); + if (ok) { + fds[0] = _open_osfhandle((Py_intptr_t)read, _O_RDONLY); + ok = (fds[0] != -1); + } + if (ok) { + fds[1] = _open_osfhandle((Py_intptr_t)write, _O_WRONLY); + ok = (fds[1] != -1); + } + 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 + flags = cloexec ? O_CLOEXEC : 0; + + Py_BEGIN_ALLOW_THREADS + res = pipe2(fds, flags); + Py_END_ALLOW_THREADS + + if (res != 0 && errno == ENOSYS) + { +#endif + Py_BEGIN_ALLOW_THREADS + res = pipe(fds); + Py_END_ALLOW_THREADS + + if (cloexec && res == 0) { + if (_Py_set_cloexec(fds[0], 1, NULL) < 0) { + close(fds[0]); + close(fds[1]); + return NULL; + } + if (_Py_set_cloexec(fds[1], 1, 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 */ @@ -10181,6 +10378,76 @@ get_terminal_size(PyObject *self, PyObje } #endif /* defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) */ +#if defined(MS_WINDOWS) \ + || (defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)) \ + || defined(HAVE_FCNTL_H) +#define POSIX_SET_CLOEXEC +#endif + +#ifdef POSIX_SET_CLOEXEC +PyDoc_STRVAR(get_cloexec__doc__, + "get_cloexec(fd) -> bool\n" \ + "\n" \ + "Get the close-on-exe flag of the specified file descriptor."); + +static PyObject* +posix_get_cloexec(PyObject *self, PyObject *args) +{ + int fd; + int cloexec; + + if (!PyArg_ParseTuple(args, "i:get_cloexec", &fd)) + return NULL; + +#ifdef MS_WINDOWS + if (!_PyVerify_fd(fd)) { + /* fd may be an handle, ex: a Windows socket */ + fd = _open_osfhandle((Py_intptr_t)fd, _O_RDONLY); + if (fd == -1) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + } +#endif + + cloexec = _Py_get_cloexec(fd); + if (cloexec < 0) + return NULL; + return PyBool_FromLong(cloexec); +} + +PyDoc_STRVAR(set_cloexec__doc__, + "set_cloexec(fd, cloexec=True)\n" \ + "\n" \ + "Set or clear close-on-exe flag on the specified file descriptor."); + +static PyObject* +posix_set_cloexec(PyObject *self, PyObject *args) +{ + int fd; + int cloexec = 1; + + if (!PyArg_ParseTuple(args, "i|i:set_cloexec", &fd, &cloexec)) + return NULL; + +#ifdef MS_WINDOWS + if (!_PyVerify_fd(fd)) { + /* fd may be an handle, ex: a Windows socket */ + fd = _open_osfhandle((Py_intptr_t)fd, _O_RDONLY); + if (fd == -1) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + } +#endif + + if (_Py_set_cloexec(fd, cloexec, NULL) < 0) + return NULL; + Py_RETURN_NONE; +} +#endif /* POSIX_SET_CLOEXEC */ + + static PyMethodDef posix_methods[] = { {"access", (PyCFunction)posix_access, @@ -10345,7 +10612,8 @@ static PyMethodDef posix_methods[] = { #endif #endif /* HAVE_SCHED_H */ #if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) - {"openpty", posix_openpty, METH_NOARGS, posix_openpty__doc__}, + {"openpty", (PyCFunction)posix_openpty, + METH_VARARGS | METH_KEYWORDS, posix_openpty__doc__}, #endif /* HAVE_OPENPTY || HAVE__GETPTY || HAVE_DEV_PTMX */ #ifdef HAVE_FORKPTY {"forkpty", posix_forkpty, METH_NOARGS, posix_forkpty__doc__}, @@ -10457,8 +10725,10 @@ static PyMethodDef posix_methods[] = { {"close", posix_close, METH_VARARGS, posix_close__doc__}, {"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__}, + {"dup", (PyCFunction)posix_dup, + METH_VARARGS | METH_KEYWORDS, posix_dup__doc__}, + {"dup2", (PyCFunction)posix_dup2, + METH_VARARGS | METH_KEYWORDS, posix_dup2__doc__}, #ifdef HAVE_LOCKF {"lockf", posix_lockf, METH_VARARGS, posix_lockf__doc__}, #endif @@ -10484,7 +10754,8 @@ static PyMethodDef posix_methods[] = { {"fstat", posix_fstat, METH_VARARGS, posix_fstat__doc__}, {"isatty", posix_isatty, METH_VARARGS, posix_isatty__doc__}, #ifdef HAVE_PIPE - {"pipe", posix_pipe, METH_NOARGS, posix_pipe__doc__}, + {"pipe", (PyCFunction)posix_pipe, + METH_VARARGS | METH_KEYWORDS, posix_pipe__doc__}, #endif #ifdef HAVE_PIPE2 {"pipe2", posix_pipe2, METH_O, posix_pipe2__doc__}, @@ -10626,6 +10897,10 @@ static PyMethodDef posix_methods[] = { #if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) {"get_terminal_size", get_terminal_size, METH_VARARGS, termsize__doc__}, #endif +#ifdef POSIX_SET_CLOEXEC + {"get_cloexec", posix_get_cloexec, METH_VARARGS, get_cloexec__doc__}, + {"set_cloexec", posix_set_cloexec, METH_VARARGS, set_cloexec__doc__}, +#endif {NULL, NULL} /* Sentinel */ }; diff -r f87d85f7596c -r bc88690df059 Modules/selectmodule.c --- a/Modules/selectmodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/selectmodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -882,6 +882,17 @@ devpoll_poll(devpollObject *self, PyObje return NULL; } +static PyObject* +devpoll_fileno(devpollObject *self) +{ + return PyLong_FromLong(self->fd_devpoll); +} + +PyDoc_STRVAR(devpoll_fileno_doc, +"fileno() -> int\n\ +\n\ +Return the file descriptor."); + static PyMethodDef devpoll_methods[] = { {"register", (PyCFunction)devpoll_register, METH_VARARGS, devpoll_register_doc}, @@ -891,11 +902,13 @@ static PyMethodDef devpoll_methods[] = { METH_O, devpoll_unregister_doc}, {"poll", (PyCFunction)devpoll_poll, METH_VARARGS, devpoll_poll_doc}, + {"fileno", (PyCFunction)devpoll_fileno, METH_NOARGS, + devpoll_fileno_doc}, {NULL, NULL} /* sentinel */ }; static devpollObject * -newDevPollObject(void) +newDevPollObject(int cloexec) { devpollObject *self; int fd_devpoll, limit_result; @@ -923,6 +936,11 @@ newDevPollObject(void) return NULL; } + if (cloexec && _Py_set_cloexec(fd_devpoll, 1, NULL) < 0) { + close(fd_devpoll); + return NULL; + } + fds = PyMem_NEW(struct pollfd, limit.rlim_cur); if (fds == NULL) { close(fd_devpoll); @@ -1009,9 +1027,16 @@ PyDoc_STRVAR(devpoll_doc, unregistering file descriptors, and then polling them for I/O events."); static PyObject * -select_devpoll(PyObject *self, PyObject *unused) +select_devpoll(PyObject *self, PyObject *args, PyObject *kwds) { - return (PyObject *)newDevPollObject(); + static char *kwlist[] = {"cloexec", NULL}; + int cloexec = Py_DefaultCloexec; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:devpoll", kwlist, + _Py_cloexec_converter, &cloexec)) + return NULL; + + return (PyObject *)newDevPollObject(cloexec); } #endif @@ -1091,7 +1116,8 @@ pyepoll_internal_close(pyEpoll_Object *s } static PyObject * -newPyEpoll_Object(PyTypeObject *type, int sizehint, int flags, SOCKET fd) +newPyEpoll_Object(PyTypeObject *type, int sizehint, int flags, SOCKET fd, + int cloexec) { pyEpoll_Object *self; @@ -1103,6 +1129,8 @@ newPyEpoll_Object(PyTypeObject *type, in if (fd == -1) { Py_BEGIN_ALLOW_THREADS #ifdef HAVE_EPOLL_CREATE1 + if (cloexec) + flags |= EPOLL_CLOEXEC; if (flags) self->epfd = epoll_create1(flags); else @@ -1118,6 +1146,14 @@ newPyEpoll_Object(PyTypeObject *type, in PyErr_SetFromErrno(PyExc_OSError); return NULL; } + +#ifndef HAVE_EPOLL_CREATE1 + if (cloexec && _Py_set_cloexec(self->epfd, 1, NULL) < 0) { + Py_DECREF(self); + return NULL; + } +#endif + return (PyObject *)self; } @@ -1126,17 +1162,19 @@ static PyObject * pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { int flags = 0, sizehint = FD_SETSIZE - 1; - static char *kwlist[] = {"sizehint", "flags", NULL}; + static char *kwlist[] = {"sizehint", "flags", "cloexec", NULL}; + int cloexec = Py_DefaultCloexec; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:epoll", kwlist, - &sizehint, &flags)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiO&:epoll", kwlist, + &sizehint, &flags, + _Py_cloexec_converter, &cloexec)) return NULL; if (sizehint < 0) { PyErr_SetString(PyExc_ValueError, "negative sizehint"); return NULL; } - return newPyEpoll_Object(type, sizehint, flags, -1); + return newPyEpoll_Object(type, sizehint, flags, -1, cloexec); } @@ -1194,7 +1232,7 @@ pyepoll_fromfd(PyObject *cls, PyObject * if (!PyArg_ParseTuple(args, "i:fromfd", &fd)) return NULL; - return newPyEpoll_Object((PyTypeObject*)cls, FD_SETSIZE - 1, 0, fd); + return newPyEpoll_Object((PyTypeObject*)cls, FD_SETSIZE - 1, 0, fd, 0); } PyDoc_STRVAR(pyepoll_fromfd_doc, @@ -1762,7 +1800,7 @@ kqueue_queue_internal_close(kqueue_queue } static PyObject * -newKqueue_Object(PyTypeObject *type, SOCKET fd) +newKqueue_Object(PyTypeObject *type, SOCKET fd, int cloexec) { kqueue_queue_Object *self; assert(type != NULL && type->tp_alloc != NULL); @@ -1784,21 +1822,27 @@ newKqueue_Object(PyTypeObject *type, SOC PyErr_SetFromErrno(PyExc_OSError); return NULL; } + + if (fd == -1 && cloexec) { + if (_Py_set_cloexec(self->kqfd, 1, NULL) < 0) { + Py_DECREF(self); + return NULL; + } + } return (PyObject *)self; } static PyObject * kqueue_queue_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"cloexec", NULL}; + int cloexec = Py_DefaultCloexec; - if ((args != NULL && PyObject_Size(args)) || - (kwds != NULL && PyObject_Size(kwds))) { - PyErr_SetString(PyExc_ValueError, - "select.kqueue doesn't accept arguments"); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:kqueue", kwlist, + _Py_cloexec_converter, &cloexec)) return NULL; - } - return newKqueue_Object(type, -1); + return newKqueue_Object(type, -1, cloexec); } static void @@ -1855,7 +1899,7 @@ kqueue_queue_fromfd(PyObject *cls, PyObj if (!PyArg_ParseTuple(args, "i:fromfd", &fd)) return NULL; - return newKqueue_Object((PyTypeObject*)cls, fd); + return newKqueue_Object((PyTypeObject*)cls, fd, 0); } PyDoc_STRVAR(kqueue_queue_fromfd_doc, @@ -2119,7 +2163,8 @@ static PyMethodDef select_methods[] = { {"poll", select_poll, METH_NOARGS, poll_doc}, #endif /* HAVE_POLL */ #ifdef HAVE_SYS_DEVPOLL_H - {"devpoll", select_devpoll, METH_NOARGS, devpoll_doc}, + {"devpoll", (PyCFunction)select_devpoll, + METH_VARARGS | METH_KEYWORDS, devpoll_doc}, #endif {0, 0}, /* sentinel */ }; diff -r f87d85f7596c -r bc88690df059 Modules/socketmodule.c --- a/Modules/socketmodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Modules/socketmodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -100,13 +100,14 @@ Local naming conventions: /* 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, cloexec=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\ +If cloexec is True, set close-on-exec flag.\n\ \n\ A socket object represents one endpoint of a network connection.\n\ \n\ @@ -117,7 +118,7 @@ bind(addr) -- bind the socket to a local 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(cloexec=None) -- 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\ @@ -1641,7 +1642,7 @@ getsockaddrarg(PySocketSockObject *s, Py return 0; } #endif - + #ifdef PF_SYSTEM case PF_SYSTEM: switch (s->sock_proto) { @@ -1649,10 +1650,10 @@ getsockaddrarg(PySocketSockObject *s, Py case SYSPROTO_CONTROL: { struct sockaddr_ctl *addr; - + addr = (struct sockaddr_ctl *)addr_ret; addr->sc_family = AF_SYSTEM; - addr->ss_sysaddr = AF_SYS_CONTROL; + addr->ss_sysaddr = AF_SYS_CONTROL; if (PyUnicode_Check(args)) { struct ctl_info info; @@ -1678,17 +1679,17 @@ getsockaddrarg(PySocketSockObject *s, Py "cannot find kernel control with provided name"); return 0; } - + addr->sc_id = info.ctl_id; addr->sc_unit = 0; } else if (!PyArg_ParseTuple(args, "II", &(addr->sc_id), &(addr->sc_unit))) { PyErr_SetString(PyExc_TypeError, "getsockaddrarg: " "expected str or tuple of two ints"); - + return 0; } - + *len_ret = sizeof(*addr); return 1; } @@ -1805,7 +1806,7 @@ getsockaddrlen(PySocketSockObject *s, so return 1; } #endif - + #ifdef PF_SYSTEM case PF_SYSTEM: switch(s->sock_proto) { @@ -1946,8 +1947,9 @@ get_cmsg_data_len(struct msghdr *msg, st /* s._accept() -> (fd, address) */ static PyObject * -sock_accept(PySocketSockObject *s) +sock_accept(PySocketSockObject *s, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"cloexec", 0}; sock_addr_t addrbuf; SOCKET_T newfd = INVALID_SOCKET; socklen_t addrlen; @@ -1955,6 +1957,17 @@ sock_accept(PySocketSockObject *s) PyObject *addr = NULL; PyObject *res = NULL; int timeout; + int cloexec = Py_DefaultCloexec; +#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 + + /* Get the buffer's memory */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:_accept", kwlist, + _Py_cloexec_converter, &cloexec)) + return NULL; + if (!getsockaddrlen(s, &addrlen)) return NULL; memset(&addrbuf, 0, addrlen); @@ -1963,10 +1976,26 @@ sock_accept(PySocketSockObject *s) 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) { + int flags; + if (cloexec) + flags = SOCK_CLOEXEC; + else + flags = 0; + newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, flags); + if (newfd == INVALID_SOCKET && accept4_works == -1) + 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 +2008,29 @@ sock_accept(PySocketSockObject *s) if (newfd == INVALID_SOCKET) return s->errorhandler(); + if (cloexec +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + && accept4_works == 0 +#endif + ) + { + int os_fd; +#ifdef MS_WINDOWS + os_fd = _open_osfhandle((Py_intptr_t)newfd, O_RDWR); + if (os_fd == -1) { + PyErr_SetFromWindowsErr(0); + SOCKETCLOSE(newfd); + goto finally; + } +#else + os_fd = newfd; +#endif + if (_Py_set_cloexec(os_fd, 1, NULL) < 0) { + SOCKETCLOSE(newfd); + goto finally; + } + } + sock = PyLong_FromSocket_t(newfd); if (sock == NULL) { SOCKETCLOSE(newfd); @@ -1999,11 +2051,12 @@ finally: } PyDoc_STRVAR(accept_doc, -"_accept() -> (integer, address info)\n\ +"_accept(cloexec=None) -> (integer, address info)\n\ \n\ Wait for an incoming connection. Return a new socket file descriptor\n\ representing the connection, and the address of the client.\n\ -For IP sockets, the address info is a pair (hostaddr, port)."); +For IP sockets, the address info is a pair (hostaddr, port).\n\ +If cloexec is True, set close-on-exec flag."); /* s.setblocking(flag) method. Argument: False -- non-blocking mode; same as settimeout(0) @@ -3741,8 +3794,8 @@ socket.fromshare()."); /* List of methods for socket objects */ static PyMethodDef sock_methods[] = { - {"_accept", (PyCFunction)sock_accept, METH_NOARGS, - accept_doc}, + {"_accept", (PyCFunction)sock_accept, + METH_VARARGS | METH_KEYWORDS, accept_doc}, {"bind", (PyCFunction)sock_bind, METH_O, bind_doc}, {"close", (PyCFunction)sock_close, METH_NOARGS, @@ -3882,19 +3935,32 @@ sock_new(PyTypeObject *type, PyObject *a /* 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) { PySocketSockObject *s = (PySocketSockObject *)self; PyObject *fdobj = NULL; + int cloexec = Py_DefaultCloexec; SOCKET_T fd = INVALID_SOCKET; int family = AF_INET, type = SOCK_STREAM, proto = 0; - static char *keywords[] = {"family", "type", "proto", "fileno", 0}; + static char *keywords[] = {"family", "type", "proto", "fileno", "cloexec", 0}; +#ifdef SOCK_CLOEXEC + int *atomic_flag_works = &sock_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|iiiO:socket", keywords, - &family, &type, &proto, &fdobj)) + "|iiiOO&:socket", keywords, + &family, &type, &proto, &fdobj, + _Py_cloexec_converter, &cloexec)) return -1; if (fdobj != NULL && fdobj != Py_None) { @@ -3936,14 +4002,44 @@ sock_initobj(PyObject *self, PyObject *a } else { Py_BEGIN_ALLOW_THREADS - fd = socket(family, type, proto); +#ifdef SOCK_CLOEXEC + if (cloexec && sock_cloexec_works != 0) { + fd = socket(family, type | SOCK_CLOEXEC, proto); + if (sock_cloexec_works == -1 && fd < 0) { + sock_cloexec_works = (errno != EINVAL); + if (!sock_cloexec_works) { + 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 (cloexec) { + int os_fd; +#ifdef MS_WINDOWS + os_fd = _open_osfhandle((Py_intptr_t)fd, O_RDWR); + if (os_fd == -1) { + PyErr_SetFromWindowsErr(0); + return -1; + } +#else + os_fd = fd; +#endif + if (_Py_set_cloexec(os_fd, 1, atomic_flag_works) < 0) + return -1; + } + } + init_sockobject(s, fd, family, type, proto); return 0; @@ -4499,11 +4595,17 @@ Return the protocol number for the named /* dup() function for socket fds */ static PyObject * -socket_dup(PyObject *self, PyObject *fdobj) +socket_dup(PyObject *self, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"fd", "cloexec", 0}; SOCKET_T fd, newfd; - PyObject *newfdobj; - + PyObject *fdobj, *newfdobj; + int cloexec; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&:dup", kwlist, + &fdobj, + _Py_cloexec_converter, &cloexec)) + return NULL; fd = PyLong_AsSocket_t(fdobj); if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) @@ -4513,6 +4615,24 @@ socket_dup(PyObject *self, PyObject *fdo if (newfd == INVALID_SOCKET) return set_error(); + if (cloexec) { + int os_fd; +#ifdef MS_WINDOWS + os_fd = _open_osfhandle((Py_intptr_t)newfd, O_RDWR); + if (os_fd == -1) { + PyErr_SetFromWindowsErr(0); + SOCKETCLOSE(newfd); + return NULL; + } +#else + os_fd = newfd; +#endif + if (_Py_set_cloexec(os_fd, 1, NULL) < 0) { + SOCKETCLOSE(newfd); + return NULL; + } + } + newfdobj = PyLong_FromSocket_t(newfd); if (newfdobj == NULL) SOCKETCLOSE(newfd); @@ -4534,24 +4654,59 @@ sockets; on some platforms os.dup() won' /*ARGSUSED*/ static PyObject * -socket_socketpair(PyObject *self, PyObject *args) +socket_socketpair(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"family", "type", "proto", "cloexec", 0}; PySocketSockObject *s0 = NULL, *s1 = NULL; SOCKET_T sv[2]; - int family, type = SOCK_STREAM, proto = 0; + int family = AF_INET, type = SOCK_STREAM, proto = 0; PyObject *res = NULL; + int cloexec = Py_DefaultCloexec; +#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; #else family = AF_INET; #endif - if (!PyArg_ParseTuple(args, "|iii:socketpair", - &family, &type, &proto)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iiiO&:socketpair", kwlist, + &family, &type, &proto, + _Py_cloexec_converter, &cloexec)) return NULL; + /* Create a pair of socket fds */ - if (socketpair(family, type, proto, sv) < 0) + Py_BEGIN_ALLOW_THREADS +#ifdef SOCK_CLOEXEC + if (cloexec && sock_cloexec_works != 0) { + ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv); + if (sock_cloexec_works == -1 && ret < 0) { + sock_cloexec_works = (errno != EINVAL); + if (!sock_cloexec_works) + 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 (cloexec) { + if (_Py_set_cloexec(sv[0], 1, atomic_flag_works) < 0) + goto finally; + if (_Py_set_cloexec(sv[1], 1, atomic_flag_works) < 0) + goto finally; + } + s0 = new_sockobject(sv[0], family, type, proto); if (s0 == NULL) goto finally; @@ -4573,12 +4728,13 @@ finally: } PyDoc_STRVAR(socketpair_doc, -"socketpair([family[, type[, proto]]]) -> (socket object, socket object)\n\ +"socketpair(family=AF_INET, type=SOCK_STREAM, proto=0, cloexec=None) -> (socket object, socket object)\n\ \n\ Create a pair of socket objects from the sockets returned by the platform\n\ socketpair() function.\n\ The arguments are the same as for socket() except the default family is\n\ -AF_UNIX if defined on the platform; otherwise, the default is AF_INET."); +AF_UNIX if defined on the platform; otherwise, the default is AF_INET.\n\ +If cloexec is True, set close-on-exec flag."); #endif /* HAVE_SOCKETPAIR */ @@ -5350,12 +5506,12 @@ static PyMethodDef socket_methods[] = { {"getprotobyname", socket_getprotobyname, METH_VARARGS, getprotobyname_doc}, #ifndef NO_DUP - {"dup", socket_dup, - METH_O, dup_doc}, + {"dup", (PyCFunction)socket_dup, + METH_VARARGS | METH_KEYWORDS, dup_doc}, #endif #ifdef HAVE_SOCKETPAIR - {"socketpair", socket_socketpair, - METH_VARARGS, socketpair_doc}, + {"socketpair", (PyCFunction)socket_socketpair, + METH_VARARGS | METH_KEYWORDS, socketpair_doc}, #endif {"ntohs", socket_ntohs, METH_VARARGS, ntohs_doc}, diff -r f87d85f7596c -r bc88690df059 Parser/tokenizer.c --- a/Parser/tokenizer.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Parser/tokenizer.c Tue Jan 29 13:35:00 2013 +0100 @@ -1722,6 +1722,10 @@ PyTokenizer_FindEncodingFilename(int fd, if (fd < 0) { return NULL; } +#ifndef PGEN + _Py_try_set_default_cloexec(fd); +#endif + fp = fdopen(fd, "r"); if (fp == NULL) { return NULL; diff -r f87d85f7596c -r bc88690df059 Python/errors.c --- a/Python/errors.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Python/errors.c Tue Jan 29 13:35:00 2013 +0100 @@ -2,6 +2,7 @@ /* Error handling */ #include "Python.h" +#include #ifndef __STDC__ #ifndef MS_WINDOWS @@ -941,13 +942,17 @@ PyErr_SyntaxLocationEx(const char *filen PyObject * PyErr_ProgramText(const char *filename, int lineno) { + int fd; FILE *fp; int i; char linebuf[1000]; if (filename == NULL || *filename == '\0' || lineno <= 0) return NULL; - fp = fopen(filename, "r" PY_STDIOTEXTMODE); + fd = _Py_open(filename, O_RDONLY); + if (fd < 0) + return NULL; + fp = fdopen(fd, "r" PY_STDIOTEXTMODE); if (fp == NULL) return NULL; for (i = 0; i < lineno; i++) { diff -r f87d85f7596c -r bc88690df059 Python/fileutils.c --- a/Python/fileutils.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Python/fileutils.c Tue Jan 29 13:35:00 2013 +0100 @@ -9,10 +9,31 @@ #include #endif +#if defined(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 +int Py_DefaultCloexec = 0; + +#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 open_cloexec(), io.FileIO and os.open() */ +int _Py_open_cloexec_works = -1; +#endif + PyObject * _Py_device_encoding(int fd) { @@ -547,14 +568,238 @@ int #endif +/* Get the close-on-exec flag of the specified file descriptor. + Return 1 if it is set, 0 if is not set, + raise an exception and return -1 on error. */ +int +_Py_get_cloexec(int fd) +{ +#ifdef MS_WINDOWS + HANDLE handle; + BOOL success; + DWORD flags; + + if (!_PyVerify_fd(fd)) { + PyErr_SetFromWindowsErr(ERROR_INVALID_HANDLE); + return -1; + } + + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + success = GetHandleInformation(handle, &flags); + if (!success) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + return !(flags & HANDLE_FLAG_INHERIT); +#else + int flags; + + flags = fcntl(fd, F_GETFD, 0); + if (flags == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return (flags & FD_CLOEXEC); +#endif +} + +static int +set_cloexec(int fd, int cloexec, int raise) +{ +#ifdef MS_WINDOWS + HANDLE handle; + DWORD flags; + BOOL success; +#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX) + int request; + int err; +#elif defined(HAVE_FCNTL_H) + int flags; + int res; +#endif + +#ifdef MS_WINDOWS + if (!_PyVerify_fd(fd)) { + if (raise) + PyErr_SetFromWindowsErr(ERROR_INVALID_HANDLE); + return -1; + } + + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + if (raise) + PyErr_SetFromWindowsErr(0); + return -1; + } + + if (cloexec) + flags = 0; + else + flags = HANDLE_FLAG_INHERIT; + success = SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags); + if (!success) { + if (raise) + PyErr_SetFromWindowsErr(0); + return -1; + } + return 0; +#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX) + if (cloexec) + request = FIOCLEX; + else + request = FIONCLEX; + err = ioctl(fd, request); + if (err) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +#elif defined(HAVE_FCNTL_H) + flags = fcntl(fd, F_GETFD); + if (flags < 0) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + if (cloexec) + 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; +#else + if (raise) + PyErr_SetString(PyExc_NotImplementedError, + "close-on-exec flag is not supported on your platform"); + return -1; +#endif +} + +/* Set or clear close-on-exec flag of the specified file descriptor. + On success: return 0, on error: raise an exception if raise is nonzero + and return -1. */ +int +_Py_set_cloexec(int fd, int cloexec, int *atomic_flag_works) +{ + if (atomic_flag_works != NULL) { + if (*atomic_flag_works == -1) { + int works = _Py_get_cloexec(fd); + if (works == -1) + return -1; + *atomic_flag_works = works; + } + + if (*atomic_flag_works) + return 0; + } + + return set_cloexec(fd, cloexec, 1); +} + +/* Try to set or clear the close-on-exec flag of the specified file descriptor. + If setting the close-on-exec flag failed, ignore the error. */ +void +_Py_try_set_cloexec(int fd, int cloexec) +{ + (void)set_cloexec(fd, cloexec, 0); +} + +/* Try to set the default value of the close-on-exec flag of the specified file + descriptor. If setting the close-on-exec flag failed, ignore the error. + + The function considers that the close-on-exec flag is not set on the + specified file descriptor, and so does nothing if the default is False. */ +void +_Py_try_set_default_cloexec(int fd) +{ + if (!Py_DefaultCloexec) { + /* close-on-exec flag is cleared by default */ + return; + } + (void)set_cloexec(fd, 1, 0); +} + +static int +open_cloexec(const char *pathname, int flags, int cloexec) +{ + int fd; + + if (!cloexec) + return open(pathname, flags); + +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif + fd = open(pathname, flags); + if (fd < 0) + return fd; + +#if defined(O_CLOEXEC) && !defined(MS_WINDOWS) + if (_Py_open_cloexec_works == -1) { + int flags = fcntl(fd, F_GETFD, 0); + if (flags != -1) + _Py_open_cloexec_works = (flags & FD_CLOEXEC); + else + _Py_open_cloexec_works = 0; + } + + if (!_Py_open_cloexec_works) { + /* Linux kernel older than 2.6.23 ignores O_CLOEXEC flag */ + (void)set_cloexec(fd, 1, 0); + } +#elif !defined(MS_WINDOWS) + (void)set_cloexec(fd, 1, 0); +#endif + return fd; +} + +/* Open a file with the specified flags (wrapper to open() function). + + Try to apply the default value of the close-on-exec flag on the newly + created file descriptor. If setting the close-on-exec flag failed, ignore + the error. */ +int +_Py_open(const char *pathname, int flags) +{ + return open_cloexec(pathname, flags, Py_DefaultCloexec); +} + +/* Open a file with the specified flags (wrapper to open() function). + + Try to set close-on-exec flag of the newly created file descriptor. + If setting the close-on-exec flag failed, ignore the error. */ +int +_Py_open_cloexec(const char *pathname, int flags) +{ + return open_cloexec(pathname, flags, 1); +} + /* Open a file. Use _wfopen() on Windows, encode the path to the locale - encoding and use fopen() otherwise. */ + encoding and use fopen() otherwise. + Try to apply the default value of the close-on-exec flag on the newly + created file descriptor. If setting the close-on-exec flag failed, ignore + the error. */ 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; @@ -570,19 +815,28 @@ FILE * PyMem_Free(cpath); return f; #else - return _wfopen(path, mode); + f = _wfopen(path, mode); #endif + if (f == NULL) + return NULL; + if (Py_DefaultCloexec) + (void)set_cloexec(fileno(f), 1, 0); + return f; } -/* Call _wfopen() on Windows, or encode the path to the filesystem encoding and - call fopen() otherwise. +/* Open a file. Call _wfopen() on Windows, or encode the path to the filesystem + encoding and call fopen() otherwise. + + Try to apply the default value of the close-on-exec flag on the newly + created file descriptor. If setting the close-on-exec flag failed, ignore + the error. 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) { + FILE *f; #ifdef MS_WINDOWS wchar_t *wpath; wchar_t wmode[10]; @@ -602,16 +856,19 @@ FILE* 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 (Py_DefaultCloexec) + (void)set_cloexec(fileno(f), 1, 0); return f; -#endif } #ifdef HAVE_READLINK @@ -728,3 +985,26 @@ wchar_t* #endif } +/* Converter for PyArg_ParseTuple() to parse the 'cloexec' parameter: + + - if the argument is None, return sys.getdefaultcloexec() + - otherwise, return bool(argument) + + Return 0 on error, 1 on success. +*/ +int +_Py_cloexec_converter(PyObject* arg, void* addr) +{ + int cloexec; + if (arg == Py_None) { + cloexec = Py_DefaultCloexec; + } + else { + cloexec = PyObject_IsTrue(arg); + if (cloexec == -1) + return 0; + } + *(int *)addr = cloexec; + return 1; +} + diff -r f87d85f7596c -r bc88690df059 Python/pythonrun.c --- a/Python/pythonrun.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Python/pythonrun.c Tue Jan 29 13:35:00 2013 +0100 @@ -29,6 +29,10 @@ #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + #ifdef MS_WINDOWS #undef BYTE #include "windows.h" @@ -275,6 +279,8 @@ void check its value further. */ if ((p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0') Py_HashRandomizationFlag = add_flag(Py_HashRandomizationFlag, p); + if ((p = Py_GETENV("PYTHONCLOEXEC")) && *p != '\0') + Py_DefaultCloexec = 1; _PyRandom_Init(); @@ -1418,11 +1424,17 @@ PyRun_SimpleFileExFlags(FILE *fp, const len = strlen(filename); ext = filename + len - (len > 4 ? 4 : 0); if (maybe_pyc_file(fp, filename, ext, closeit)) { + int fd; FILE *pyc_fp; /* Try to run a pyc file. First, re-open in binary */ if (closeit) fclose(fp); - if ((pyc_fp = fopen(filename, "rb")) == NULL) { + fd = _Py_open(filename, O_RDONLY); + if (fd != -1) + pyc_fp = fdopen(fd, "rb"); + else + pyc_fp = NULL; + if (pyc_fp == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); goto done; } diff -r f87d85f7596c -r bc88690df059 Python/random.c --- a/Python/random.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Python/random.c Tue Jan 29 13:35:00 2013 +0100 @@ -128,7 +128,7 @@ dev_urandom_noraise(char *buffer, Py_ssi 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"); @@ -161,10 +161,9 @@ dev_urandom_python(char *buffer, Py_ssiz 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) - { + if (fd < 0) { PyErr_SetString(PyExc_NotImplementedError, "/dev/urandom (or equivalent) not found"); return -1; diff -r f87d85f7596c -r bc88690df059 Python/sysmodule.c --- a/Python/sysmodule.c Wed Jan 16 15:19:16 2013 +0100 +++ b/Python/sysmodule.c Tue Jan 29 13:35:00 2013 +0100 @@ -1057,6 +1057,35 @@ PyDoc_STRVAR(sys_clear_type_cache__doc__ Clear the internal type lookup cache."); +static PyObject * +sys_getdefaultcloexec(PyObject* self) +{ + return PyBool_FromLong(Py_DefaultCloexec); +} + +PyDoc_STRVAR(getdefaultcloexec_doc, +"getdefaultcloexec() -> bool\n\ +\n\ +Return the current default value of 'cloexec' (close-on-exec) parameter."); + +static PyObject * +sys_setdefaultcloexec(PyObject* self, PyObject *args) +{ + int cloexec = 1; + + if (!PyArg_ParseTuple(args, "|i:setdefaultcloexec", &cloexec)) + return NULL; + + Py_DefaultCloexec = cloexec; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(setdefaultcloexec_doc, +"setdefaultcloexec(cloexec=True)\n\ +\n\ +Set the default value of the 'cloexec' (close-on-exec) parameter."); + + static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ {"callstats", (PyCFunction)PyEval_GetCallStats, METH_NOARGS, @@ -1131,6 +1160,10 @@ static PyMethodDef sys_methods[] = { {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, {"_debugmallocstats", sys_debugmallocstats, METH_VARARGS, debugmallocstats_doc}, + {"getdefaultcloexec", (PyCFunction)sys_getdefaultcloexec, + METH_NOARGS, getdefaultcloexec_doc}, + {"setdefaultcloexec", (PyCFunction)sys_setdefaultcloexec, + METH_VARARGS, setdefaultcloexec_doc}, {NULL, NULL} /* sentinel */ }; diff -r f87d85f7596c -r bc88690df059 configure.ac --- a/configure.ac Wed Jan 16 15:19:16 2013 +0100 +++ b/configure.ac Tue Jan 29 13:35:00 2013 +0100 @@ -2793,7 +2793,7 @@ AC_CHECK_FUNCS(alarm accept4 setitimer g sigtimedwait sigwait sigwaitinfo snprintf strftime strlcpy symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \ - wcscoll wcsftime wcsxfrm writev _getpty) + wcscoll wcsftime wcsxfrm writev _getpty dup3) AC_CHECK_DECL(dirfd, AC_DEFINE(HAVE_DIRFD, 1,