Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r86604:6007963baadc Date: 2016-08-27 16:37 +0200 http://bitbucket.org/pypy/pypy/changeset/6007963baadc/
Log: hg merge py3.5-noninherit Newly created file descriptors are non-inheritable (PEP 446) diff --git a/lib_pypy/_curses.py b/lib_pypy/_curses.py --- a/lib_pypy/_curses.py +++ b/lib_pypy/_curses.py @@ -554,6 +554,9 @@ def putwin(self, filep): # filestar = ffi.new("FILE *", filep) return _check_ERR(lib.putwin(self._win, filep), "putwin") + # XXX CPython 3.5 says: We have to simulate this by writing to + # a temporary FILE*, then reading back, then writing to the + # argument stream. def redrawln(self, beg, num): return _check_ERR(lib.wredrawln(self._win, beg, num), "redrawln") @@ -704,6 +707,7 @@ def getwin(filep): + # XXX CPython 3.5: there's logic to use a temp file instead return Window(_check_NULL(lib.getwin(filep))) diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py --- a/pypy/module/_io/interp_fileio.py +++ b/pypy/module/_io/interp_fileio.py @@ -4,6 +4,7 @@ OperationError, oefmt, wrap_oserror, wrap_oserror2) from rpython.rlib.rarithmetic import r_longlong from rpython.rlib.rstring import StringBuilder +from rpython.rlib import rposix from os import O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_EXCL import sys, os, stat, errno from pypy.module._io.interp_iobase import W_RawIOBase, convert_size @@ -29,6 +30,7 @@ O_BINARY = getattr(os, "O_BINARY", 0) O_APPEND = getattr(os, "O_APPEND", 0) +_open_inhcache = rposix.SetNonInheritableCache() def _bad_mode(space): raise oefmt(space.w_ValueError, @@ -139,6 +141,7 @@ @unwrap_spec(mode=str, closefd=int) def descr_init(self, space, w_name, mode='r', closefd=True, w_opener=None): + self._close(space) if space.isinstance_w(w_name, space.w_float): raise oefmt(space.w_TypeError, "integer argument expected, got float") @@ -153,6 +156,8 @@ raise oefmt(space.w_ValueError, "negative file descriptor") self.readable, self.writable, self.created, self.appending, flags = decode_mode(space, mode) + if rposix.O_CLOEXEC is not None: + flags |= rposix.O_CLOEXEC fd_is_own = False try: @@ -171,8 +176,7 @@ raise oefmt(space.w_ValueError, "Cannot use closefd=False with file name") - from pypy.module.posix.interp_posix import ( - dispatch_filename, rposix) + from pypy.module.posix.interp_posix import dispatch_filename try: self.fd = dispatch_filename(rposix.open)( space, w_name, flags, 0666) @@ -181,6 +185,11 @@ exception_name='w_IOError') finally: fd_is_own = True + if not rposix._WIN32: + try: + _open_inhcache.set_non_inheritable(self.fd) + except OSError as e: + raise wrap_oserror2(space, e, w_name) else: w_fd = space.call_function(w_opener, w_name, space.wrap(flags)) try: @@ -192,6 +201,11 @@ "expected integer from opener") finally: fd_is_own = True + if not rposix._WIN32: + try: + rposix.set_inheritable(self.fd, False) + except OSError as e: + raise wrap_oserror2(space, e, w_name) self._dircheck(space, w_name) space.setattr(self, space.wrap("name"), w_name) diff --git a/pypy/module/_io/test/test_fileio.py b/pypy/module/_io/test/test_fileio.py --- a/pypy/module/_io/test/test_fileio.py +++ b/pypy/module/_io/test/test_fileio.py @@ -246,6 +246,33 @@ assert f.mode == 'xb' raises(FileExistsError, _io.FileIO, filename, 'x') + def test_non_inheritable(self): + import _io, posix + f = _io.FileIO(self.tmpfile, 'r') + assert posix.get_inheritable(f.fileno()) == False + f.close() + + def test_FileIO_fd_does_not_change_inheritable(self): + import _io, posix + fd1, fd2 = posix.pipe() + posix.set_inheritable(fd1, True) + posix.set_inheritable(fd2, False) + f1 = _io.FileIO(fd1, 'r') + f2 = _io.FileIO(fd2, 'w') + assert posix.get_inheritable(fd1) == True + assert posix.get_inheritable(fd2) == False + f1.close() + f2.close() + + def test_close_upon_reinit(self): + import _io, posix + f = _io.FileIO(self.tmpfile, 'r') + fd1 = f.fileno() + f.__init__(self.tmpfile, 'w') + fd2 = f.fileno() + if fd1 != fd2: + raises(OSError, posix.close, fd1) + def test_flush_at_exit(): from pypy import conftest diff --git a/pypy/module/_posixsubprocess/_posixsubprocess.c b/pypy/module/_posixsubprocess/_posixsubprocess.c --- a/pypy/module/_posixsubprocess/_posixsubprocess.c +++ b/pypy/module/_posixsubprocess/_posixsubprocess.c @@ -106,6 +106,30 @@ } +RPY_EXTERN +int rpy_set_inheritable(int fd, int inheritable); /* rposix.py */ + +static int +make_inheritable(long *py_fds_to_keep, ssize_t num_fds_to_keep, + int errpipe_write) +{ + long i; + + for (i = 0; i < num_fds_to_keep; ++i) { + long fd = py_fds_to_keep[i]; + if (fd == errpipe_write) { + /* errpipe_write is part of py_fds_to_keep. It must be closed at + exec(), but kept open in the child process until exec() is + called. */ + continue; + } + if (rpy_set_inheritable((int)fd, 1) < 0) + return -1; + } + return 0; +} + + /* Close all file descriptors in the range start_fd inclusive to * end_fd exclusive except for those in py_fds_to_keep. If the * range defined by [start_fd, end_fd) is large this will take a @@ -329,6 +353,9 @@ /* Buffer large enough to hold a hex integer. We can't malloc. */ char hex_errno[sizeof(saved_errno)*2+1]; + if (make_inheritable(py_fds_to_keep, num_fds_to_keep, errpipe_write) < 0) + goto error; + /* Close parent's pipe ends. */ if (p2cwrite != -1) { POSIX_CALL(close(p2cwrite)); @@ -352,26 +379,25 @@ 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 (rpy_set_inheritable(p2cread, 1) < 0) + goto error; + } + else if (p2cread != -1) POSIX_CALL(dup2(p2cread, 0)); /* stdin */ + + if (c2pwrite == 1) { + if (rpy_set_inheritable(c2pwrite, 1) < 0) + goto error; } - if (c2pwrite == 1) { - int old = fcntl(c2pwrite, F_GETFD); - if (old != -1) - fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); - } else if (c2pwrite != -1) { + else if (c2pwrite != -1) POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ + + if (errwrite == 2) { + if (rpy_set_inheritable(errwrite, 1) < 0) + goto error; } - if (errwrite == 2) { - int old = fcntl(errwrite, F_GETFD); - if (old != -1) - fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); - } else if (errwrite != -1) { + else if (errwrite != -1) POSIX_CALL(dup2(errwrite, 2)); /* stderr */ - } /* Close pipe fds. Make sure we don't close the same fd more than */ /* once, or standard fds. */ diff --git a/pypy/module/_posixsubprocess/_posixsubprocess.h b/pypy/module/_posixsubprocess/_posixsubprocess.h --- a/pypy/module/_posixsubprocess/_posixsubprocess.h +++ b/pypy/module/_posixsubprocess/_posixsubprocess.h @@ -1,3 +1,4 @@ +#include <unistd.h> /* for ssize_t */ #include "src/precommondefs.h" RPY_EXTERN void diff --git a/pypy/module/_posixsubprocess/interp_subprocess.py b/pypy/module/_posixsubprocess/interp_subprocess.py --- a/pypy/module/_posixsubprocess/interp_subprocess.py +++ b/pypy/module/_posixsubprocess/interp_subprocess.py @@ -5,6 +5,7 @@ from rpython.rtyper.tool import rffi_platform as platform from rpython.translator import cdir from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rlib import rposix from pypy.interpreter.error import ( OperationError, exception_from_saved_errno, oefmt, wrap_oserror) @@ -36,6 +37,7 @@ compile_extra.append("-DHAVE_SETSID") eci = eci.merge( + rposix.eci_inheritable, ExternalCompilationInfo( compile_extra=compile_extra)) diff --git a/pypy/module/_posixsubprocess/test/test_subprocess.py b/pypy/module/_posixsubprocess/test/test_subprocess.py --- a/pypy/module/_posixsubprocess/test/test_subprocess.py +++ b/pypy/module/_posixsubprocess/test/test_subprocess.py @@ -75,3 +75,18 @@ n = 1 raises(OverflowError, _posixsubprocess.fork_exec, 1,Z(),3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17) + + def test_pass_fds_make_inheritable(self): + import subprocess, posix + + fd1, fd2 = posix.pipe() + assert posix.get_inheritable(fd1) is False + assert posix.get_inheritable(fd2) is False + + subprocess.check_call(['/usr/bin/env', 'python', '-c', + 'import os;os.write(%d,b"K")' % fd2], + close_fds=True, pass_fds=[fd2]) + res = posix.read(fd1, 1) + assert res == b"K" + posix.close(fd1) + posix.close(fd2) diff --git a/pypy/module/_socket/interp_func.py b/pypy/module/_socket/interp_func.py --- a/pypy/module/_socket/interp_func.py +++ b/pypy/module/_socket/interp_func.py @@ -143,24 +143,11 @@ @unwrap_spec(fd=int) def dup(space, fd): try: - newfd = rsocket.dup(fd) + newfd = rsocket.dup(fd, inheritable=False) except SocketError as e: raise converted_error(space, e) return space.wrap(newfd) -@unwrap_spec(fd=int, family=int, type=int, proto=int) -def fromfd(space, fd, family, type, proto=0): - """fromfd(fd, family, type[, proto]) -> socket object - - Create a socket object from the given file descriptor. - The remaining arguments are the same as for socket(). - """ - try: - sock = rsocket.fromfd(fd, family, type, proto) - except SocketError as e: - raise converted_error(space, e) - return space.wrap(W_Socket(space, sock)) - @unwrap_spec(family=int, type=int, proto=int) def socketpair(space, family=rsocket.socketpair_default_family, type =rsocket.SOCK_STREAM, @@ -173,7 +160,8 @@ AF_UNIX if defined on the platform; otherwise, the default is AF_INET. """ try: - sock1, sock2 = rsocket.socketpair(family, type, proto) + sock1, sock2 = rsocket.socketpair(family, type, proto, + inheritable=False) except SocketError as e: raise converted_error(space, e) return space.newtuple([ diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -177,7 +177,7 @@ sock = RSocket(family, type, proto, fd=space.c_filedescriptor_w(w_fileno)) else: - sock = RSocket(family, type, proto) + sock = RSocket(family, type, proto, inheritable=False) W_Socket.__init__(self, space, sock) except SocketError as e: raise converted_error(space, e) @@ -228,7 +228,7 @@ For IP sockets, the address info is a pair (hostaddr, port). """ try: - fd, addr = self.sock.accept() + fd, addr = self.sock.accept(inheritable=False) return space.newtuple([space.wrap(fd), addr_as_object(addr, fd, space)]) except SocketError as e: diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -546,11 +546,15 @@ s.ioctl(_socket.SIO_KEEPALIVE_VALS, (1, 100, 100)) def test_dup(self): - import _socket as socket + import _socket as socket, posix s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) fd = socket.dup(s.fileno()) assert s.fileno() != fd + assert posix.get_inheritable(s.fileno()) is False + assert posix.get_inheritable(fd) is False + posix.close(fd) + s.close() def test_dup_error(self): import _socket @@ -652,6 +656,26 @@ assert len(w) == 1, [str(warning) for warning in w] assert r in str(w[0]) + def test_invalid_fd(self): + import _socket + raises(ValueError, _socket.socket, fileno=-1) + + def test_socket_non_inheritable(self): + import _socket, posix + s1 = _socket.socket() + assert posix.get_inheritable(s1.fileno()) is False + s1.close() + + def test_socketpair_non_inheritable(self): + import _socket, posix + if not hasattr(_socket, 'socketpair'): + skip("no socketpair") + s1, s2 = _socket.socketpair() + assert posix.get_inheritable(s1.fileno()) is False + assert posix.get_inheritable(s2.fileno()) is False + s1.close() + s2.close() + class AppTestNetlink: def setup_class(cls): @@ -830,6 +854,16 @@ assert cli.family == socket.AF_INET + def test_accept_non_inheritable(self): + import _socket, posix + cli = _socket.socket() + cli.connect(self.serv.getsockname()) + fileno, addr = self.serv._accept() + assert posix.get_inheritable(fileno) is False + posix.close(fileno) + cli.close() + + class AppTestErrno: spaceconfig = {'usemodules': ['_socket']} diff --git a/pypy/module/fcntl/test/test_fcntl.py b/pypy/module/fcntl/test/test_fcntl.py --- a/pypy/module/fcntl/test/test_fcntl.py +++ b/pypy/module/fcntl/test/test_fcntl.py @@ -32,7 +32,7 @@ f = open(self.tmp + "b", "w+") - fcntl.fcntl(f, 1, 0) + original = fcntl.fcntl(f, 1, 0) fcntl.fcntl(f, 1) fcntl.fcntl(F(int(f.fileno())), 1) raises(TypeError, fcntl.fcntl, "foo") @@ -46,9 +46,16 @@ raises(ValueError, fcntl.fcntl, -1, 1, 0) raises(ValueError, fcntl.fcntl, F(-1), 1, 0) raises(ValueError, fcntl.fcntl, F(int(-1)), 1, 0) - assert fcntl.fcntl(f, 1, 0) == 0 + assert fcntl.fcntl(f, 1, 0) == original assert fcntl.fcntl(f, 2, "foo") == b"foo" - assert fcntl.fcntl(f, 2, memoryview(b"foo")) == b"foo" + assert fcntl.fcntl(f, 2, b"foo") == b"foo" + + # This is supposed to work I think, but CPython 3.5 refuses it + # for reasons I don't understand: + # >>> _testcapi.getargs_s_hash(memoryview(b"foo")) + # TypeError: must be read-only bytes-like object, not memoryview + # + # assert fcntl.fcntl(f, 2, memoryview(b"foo")) == b"foo" try: os.O_LARGEFILE @@ -202,7 +209,7 @@ raises(TypeError, fcntl.ioctl, "foo") raises(TypeError, fcntl.ioctl, 0, "foo") #raises(TypeError, fcntl.ioctl, 0, TIOCGPGRP, float(0)) - raises(TypeError, fcntl.ioctl, 0, TIOCGPGRP, 1, "foo") + raises(TypeError, fcntl.ioctl, 0, TIOCGPGRP, 1, "foo", "bar") child_pid, mfd = pty.fork() if child_pid == 0: @@ -229,13 +236,13 @@ assert res == 0 assert buf.tostring() == expected - exc = raises(TypeError, fcntl.ioctl, mfd, TIOCGPGRP, memoryview(b'abc'), False) - assert str(exc.value) == "ioctl requires a file or file descriptor, an integer and optionally an integer or buffer argument" + raises(TypeError, fcntl.ioctl, mfd, TIOCGPGRP, (), False) res = fcntl.ioctl(mfd, TIOCGPGRP, buf, False) assert res == expected - raises(TypeError, fcntl.ioctl, mfd, TIOCGPGRP, "\x00\x00", True) + # xxx this fails on CPython 3.5, that's a minor bug + #raises(TypeError, fcntl.ioctl, mfd, TIOCGPGRP, "\x00\x00", True) res = fcntl.ioctl(mfd, TIOCGPGRP, "\x00\x00\x00\x00") assert res == expected diff --git a/pypy/module/posix/__init__.py b/pypy/module/posix/__init__.py --- a/pypy/module/posix/__init__.py +++ b/pypy/module/posix/__init__.py @@ -78,6 +78,8 @@ 'get_terminal_size': 'interp_posix.get_terminal_size', 'scandir': 'interp_scandir.scandir', + 'get_inheritable': 'interp_posix.get_inheritable', + 'set_inheritable': 'interp_posix.set_inheritable', } if hasattr(os, 'chown'): @@ -195,6 +197,9 @@ interpleveldefs['_have_functions'] = ( 'space.newlist([space.wrap(x) for x in interp_posix.have_functions])') + if rposix.HAVE_PIPE2: + interpleveldefs['pipe2'] = 'interp_posix.pipe2' + def startup(self, space): from pypy.module.posix import interp_posix from pypy.module.imp import importing diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -211,6 +211,8 @@ space.w_NotImplementedError, "%s: %s unavailable on this platform", funcname, arg) +_open_inhcache = rposix.SetNonInheritableCache() + @unwrap_spec(flags=c_int, mode=c_int, dir_fd=DirFD(rposix.HAVE_OPENAT)) def open(space, w_path, flags, mode=0777, __kwonly__=None, dir_fd=DEFAULT_DIR_FD): @@ -222,12 +224,15 @@ and path should be relative; path will then be relative to that directory. dir_fd may not be implemented on your platform. If it is unavailable, using it will raise a NotImplementedError.""" + if rposix.O_CLOEXEC is not None: + flags |= rposix.O_CLOEXEC try: if rposix.HAVE_OPENAT and dir_fd != DEFAULT_DIR_FD: path = space.fsencode_w(w_path) fd = rposix.openat(path, flags, mode, dir_fd) else: fd = dispatch_filename(rposix.open)(space, w_path, flags, mode) + _open_inhcache.set_non_inheritable(fd) except OSError as e: raise wrap_oserror2(space, e, w_path) return space.wrap(fd) @@ -538,17 +543,17 @@ """Create a copy of the file descriptor. Return the new file descriptor.""" try: - newfd = os.dup(fd) + newfd = rposix.dup(fd, inheritable=False) except OSError as e: raise wrap_oserror(space, e) else: return space.wrap(newfd) -@unwrap_spec(old_fd=c_int, new_fd=c_int) -def dup2(space, old_fd, new_fd): +@unwrap_spec(old_fd=c_int, new_fd=c_int, inheritable=int) +def dup2(space, old_fd, new_fd, inheritable=1): """Duplicate a file descriptor.""" try: - os.dup2(old_fd, new_fd) + rposix.dup2(old_fd, new_fd, inheritable) except OSError as e: raise wrap_oserror(space, e) @@ -891,15 +896,38 @@ result_w[i] = space.fsdecode(w_bytes) return space.newlist(result_w) +@unwrap_spec(fd=c_int) +def get_inheritable(space, fd): + try: + return space.wrap(rposix.get_inheritable(fd)) + except OSError as e: + raise wrap_oserror(space, e) + +@unwrap_spec(fd=c_int, inheritable=int) +def set_inheritable(space, fd, inheritable): + try: + rposix.set_inheritable(fd, inheritable) + except OSError as e: + raise wrap_oserror(space, e) + +_pipe_inhcache = rposix.SetNonInheritableCache() + def pipe(space): "Create a pipe. Returns (read_end, write_end)." try: - fd1, fd2 = os.pipe() + fd1, fd2 = rposix.pipe(rposix.O_CLOEXEC or 0) + _pipe_inhcache.set_non_inheritable(fd1) + _pipe_inhcache.set_non_inheritable(fd2) except OSError as e: raise wrap_oserror(space, e) - # XXX later, use rposix.pipe2() if available! - rposix.set_inheritable(fd1, False) - rposix.set_inheritable(fd2, False) + return space.newtuple([space.wrap(fd1), space.wrap(fd2)]) + +@unwrap_spec(flags=c_int) +def pipe2(space, flags): + try: + fd1, fd2 = rposix.pipe2(flags) + except OSError as e: + raise wrap_oserror(space, e) return space.newtuple([space.wrap(fd1), space.wrap(fd2)]) @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_FCHMODAT), @@ -1238,6 +1266,8 @@ "Open a pseudo-terminal, returning open fd's for both master and slave end." try: master_fd, slave_fd = os.openpty() + rposix.set_inheritable(master_fd, False) + rposix.set_inheritable(slave_fd, False) except OSError as e: raise wrap_oserror(space, e) return space.newtuple([space.wrap(master_fd), space.wrap(slave_fd)]) diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -106,6 +106,7 @@ posix = self.posix fd = posix.open(path, posix.O_RDONLY, 0o777) fd2 = posix.dup(fd) + assert posix.get_inheritable(fd2) == False assert not posix.isatty(fd2) s = posix.read(fd, 1) assert s == b't' @@ -398,6 +399,16 @@ os.write(slave_fd, b'x\n') data = os.read(master_fd, 100) assert data.startswith(b'x') + os.close(master_fd) + os.close(slave_fd) + + def test_openpty_non_inheritable(self): + os = self.posix + master_fd, slave_fd = os.openpty() + assert os.get_inheritable(master_fd) == False + assert os.get_inheritable(slave_fd) == False + os.close(master_fd) + os.close(slave_fd) if hasattr(__import__(os.name), "forkpty"): def test_forkpty(self): @@ -1077,6 +1088,52 @@ x = f.read(1) assert x == 'e' + def test_pipe_inheritable(self): + fd1, fd2 = self.posix.pipe() + assert self.posix.get_inheritable(fd1) == False + assert self.posix.get_inheritable(fd2) == False + self.posix.close(fd1) + self.posix.close(fd2) + + def test_pipe2(self): + if not hasattr(self.posix, 'pipe2'): + skip("no pipe2") + fd1, fd2 = self.posix.pipe2(0) + assert self.posix.get_inheritable(fd1) == True + assert self.posix.get_inheritable(fd2) == True + self.posix.close(fd1) + self.posix.close(fd2) + + def test_O_CLOEXEC(self): + if not hasattr(self.posix, 'pipe2'): + skip("no pipe2") + if not hasattr(self.posix, 'O_CLOEXEC'): + skip("no O_CLOEXEC") + fd1, fd2 = self.posix.pipe2(self.posix.O_CLOEXEC) + assert self.posix.get_inheritable(fd1) == False + assert self.posix.get_inheritable(fd2) == False + self.posix.close(fd1) + self.posix.close(fd2) + + def test_dup2_inheritable(self): + fd1, fd2 = self.posix.pipe() + assert self.posix.get_inheritable(fd2) == False + self.posix.dup2(fd1, fd2) + assert self.posix.get_inheritable(fd2) == True + self.posix.dup2(fd1, fd2, False) + assert self.posix.get_inheritable(fd2) == False + self.posix.dup2(fd1, fd2, True) + assert self.posix.get_inheritable(fd2) == True + self.posix.close(fd1) + self.posix.close(fd2) + + def test_open_inheritable(self): + os = self.posix + fd = os.open(self.path2 + 'test_open_inheritable', + os.O_RDWR | os.O_CREAT, 0o666) + assert os.get_inheritable(fd) == False + os.close(fd) + def test_urandom(self): os = self.posix s = os.urandom(5) diff --git a/pypy/module/select/interp_epoll.py b/pypy/module/select/interp_epoll.py --- a/pypy/module/select/interp_epoll.py +++ b/pypy/module/select/interp_epoll.py @@ -39,7 +39,8 @@ for symbol in public_symbols: setattr(CConfig, symbol, rffi_platform.DefinedConstantInteger(symbol)) -for symbol in ["EPOLL_CTL_ADD", "EPOLL_CTL_MOD", "EPOLL_CTL_DEL"]: +for symbol in ["EPOLL_CTL_ADD", "EPOLL_CTL_MOD", "EPOLL_CTL_DEL", + "EPOLL_CLOEXEC"]: setattr(CConfig, symbol, rffi_platform.ConstantInteger(symbol)) cconfig = rffi_platform.configure(CConfig) @@ -52,13 +53,14 @@ EPOLL_CTL_ADD = cconfig["EPOLL_CTL_ADD"] EPOLL_CTL_MOD = cconfig["EPOLL_CTL_MOD"] EPOLL_CTL_DEL = cconfig["EPOLL_CTL_DEL"] +EPOLL_CLOEXEC = cconfig["EPOLL_CLOEXEC"] DEF_REGISTER_EVENTMASK = (public_symbols["EPOLLIN"] | public_symbols["EPOLLOUT"] | public_symbols["EPOLLPRI"]) -epoll_create = rffi.llexternal( - "epoll_create", [rffi.INT], rffi.INT, compilation_info=eci, +epoll_create1 = rffi.llexternal( + "epoll_create1", [rffi.INT], rffi.INT, compilation_info=eci, save_err=rffi.RFFI_SAVE_ERRNO ) epoll_ctl = rffi.llexternal( @@ -82,14 +84,12 @@ self.epfd = epfd self.register_finalizer(space) - @unwrap_spec(sizehint=int) - def descr__new__(space, w_subtype, sizehint=-1): - if sizehint == -1: - sizehint = FD_SETSIZE - 1 - elif sizehint < 0: + @unwrap_spec(sizehint=int, flags=int) + def descr__new__(space, w_subtype, sizehint=0, flags=0): + if sizehint < 0: # 'sizehint' is otherwise ignored raise oefmt(space.w_ValueError, "sizehint must be greater than zero, got %d", sizehint) - epfd = epoll_create(sizehint) + epfd = epoll_create1(flags | EPOLL_CLOEXEC) if epfd < 0: raise exception_from_saved_errno(space, space.w_IOError) diff --git a/pypy/module/select/interp_kqueue.py b/pypy/module/select/interp_kqueue.py --- a/pypy/module/select/interp_kqueue.py +++ b/pypy/module/select/interp_kqueue.py @@ -1,10 +1,11 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import oefmt -from pypy.interpreter.error import exception_from_saved_errno +from pypy.interpreter.error import exception_from_saved_errno, wrap_oserror from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault from pypy.interpreter.typedef import TypeDef, generic_new_descr, GetSetProperty from rpython.rlib._rsocket_rffi import socketclose_no_errno from rpython.rlib.rarithmetic import r_uint +from rpython.rlib import rposix from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rtyper.tool import rffi_platform from rpython.translator.tool.cbuild import ExternalCompilationInfo @@ -115,6 +116,10 @@ kqfd = syscall_kqueue() if kqfd < 0: raise exception_from_saved_errno(space, space.w_IOError) + try: + rposix.set_inheritable(kqfd, False) + except OSError as e: + raise wrap_oserror(space, e) return space.wrap(W_Kqueue(space, kqfd)) @unwrap_spec(fd=int) diff --git a/pypy/module/select/test/test_devpoll.py b/pypy/module/select/test/test_devpoll.py new file mode 100644 --- /dev/null +++ b/pypy/module/select/test/test_devpoll.py @@ -0,0 +1,4 @@ +# XXX + +# devpoll is not implemented, but if we do, make sure we test for +# non-inheritable file descriptors diff --git a/pypy/module/select/test/test_epoll.py b/pypy/module/select/test/test_epoll.py --- a/pypy/module/select/test/test_epoll.py +++ b/pypy/module/select/test/test_epoll.py @@ -209,3 +209,10 @@ ep = select.epoll() ep.close() ep.close() + + def test_non_inheritable(self): + import select, posix + + ep = select.epoll() + assert posix.get_inheritable(ep.fileno()) == False + ep.close() diff --git a/pypy/module/select/test/test_kqueue.py b/pypy/module/select/test/test_kqueue.py --- a/pypy/module/select/test/test_kqueue.py +++ b/pypy/module/select/test/test_kqueue.py @@ -186,3 +186,10 @@ a.close() b.close() kq.close() + + def test_non_inheritable(self): + import select, posix + + kq = select.kqueue() + assert posix.get_inheritable(kq.fileno()) == False + kq.close() diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -176,6 +176,7 @@ SOCK_DGRAM SOCK_RAW SOCK_RDM SOCK_SEQPACKET SOCK_STREAM +SOCK_CLOEXEC SOL_SOCKET SOL_IPX SOL_AX25 SOL_ATALK SOL_NETROM SOL_ROSE @@ -319,6 +320,8 @@ [('p_proto', rffi.INT), ]) +CConfig.HAVE_ACCEPT4 = platform.Has('accept4') + if _POSIX: CConfig.nfds_t = platform.SimpleType('nfds_t') CConfig.pollfd = platform.Struct('struct pollfd', @@ -541,6 +544,12 @@ socketaccept = external('accept', [socketfd_type, sockaddr_ptr, socklen_t_ptr], socketfd_type, save_err=SAVE_ERR) +HAVE_ACCEPT4 = cConfig.HAVE_ACCEPT4 +if HAVE_ACCEPT4: + socketaccept4 = external('accept4', [socketfd_type, sockaddr_ptr, + socklen_t_ptr, rffi.INT], + socketfd_type, + save_err=SAVE_ERR) socketbind = external('bind', [socketfd_type, sockaddr_ptr, socklen_t], rffi.INT, save_err=SAVE_ERR) socketlisten = external('listen', [socketfd_type, rffi.INT], rffi.INT, diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -372,15 +372,27 @@ raise OSError(get_saved_errno(), '%s failed' % name) return result +def _dup(fd, inheritable=True): + validate_fd(fd) + if inheritable: + res = c_dup(fd) + else: + res = c_dup_noninheritable(fd) + return res + @replace_os_function('dup') -def dup(fd): - validate_fd(fd) - return handle_posix_error('dup', c_dup(fd)) +def dup(fd, inheritable=True): + res = _dup(fd, inheritable) + return handle_posix_error('dup', res) @replace_os_function('dup2') -def dup2(fd, newfd): +def dup2(fd, newfd, inheritable=True): validate_fd(fd) - handle_posix_error('dup2', c_dup2(fd, newfd)) + if inheritable: + res = c_dup2(fd, newfd) + else: + res = c_dup2_noninheritable(fd, newfd) + handle_posix_error('dup2', res) #___________________________________________________________________ @@ -1122,37 +1134,77 @@ c_open_osfhandle = external('_open_osfhandle', [rffi.INTPTR_T, rffi.INT], rffi.INT) + HAVE_PIPE2 = False + HAVE_DUP3 = False + O_CLOEXEC = None else: INT_ARRAY_P = rffi.CArrayPtr(rffi.INT) c_pipe = external('pipe', [INT_ARRAY_P], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO) + class CConfig: + _compilation_info_ = eci + HAVE_PIPE2 = rffi_platform.Has('pipe2') + HAVE_DUP3 = rffi_platform.Has('dup3') + O_CLOEXEC = rffi_platform.DefinedConstantInteger('O_CLOEXEC') + config = rffi_platform.configure(CConfig) + HAVE_PIPE2 = config['HAVE_PIPE2'] + HAVE_DUP3 = config['HAVE_DUP3'] + O_CLOEXEC = config['O_CLOEXEC'] + if HAVE_PIPE2: + c_pipe2 = external('pipe2', [INT_ARRAY_P, rffi.INT], rffi.INT, + save_err=rffi.RFFI_SAVE_ERRNO) @replace_os_function('pipe') -def pipe(): +def pipe(flags=0): + # 'flags' might be ignored. Check the result. if _WIN32: + # 'flags' ignored pread = lltype.malloc(rwin32.LPHANDLE.TO, 1, flavor='raw') pwrite = lltype.malloc(rwin32.LPHANDLE.TO, 1, flavor='raw') try: - if not CreatePipe( - pread, pwrite, lltype.nullptr(rffi.VOIDP.TO), 0): - raise WindowsError(rwin32.GetLastError_saved(), - "CreatePipe failed") + ok = CreatePipe( + pread, pwrite, lltype.nullptr(rffi.VOIDP.TO), 0) hread = rffi.cast(rffi.INTPTR_T, pread[0]) hwrite = rffi.cast(rffi.INTPTR_T, pwrite[0]) finally: lltype.free(pwrite, flavor='raw') lltype.free(pread, flavor='raw') - fdread = c_open_osfhandle(hread, 0) - fdwrite = c_open_osfhandle(hwrite, 1) + if ok: + fdread = c_open_osfhandle(hread, 0) + fdwrite = c_open_osfhandle(hwrite, 1) + if fdread == -1 or fdwrite == -1: + rwin32.CloseHandle(hread) + rwin32.CloseHandle(hwrite) + ok = 0 + if not ok: + raise WindowsError(rwin32.GetLastError_saved(), + "CreatePipe failed") return (fdread, fdwrite) else: filedes = lltype.malloc(INT_ARRAY_P.TO, 2, flavor='raw') try: - handle_posix_error('pipe', c_pipe(filedes)) + if HAVE_PIPE2 and _pipe2_syscall.attempt_syscall(): + res = c_pipe2(filedes, flags) + if _pipe2_syscall.fallback(res): + res = c_pipe(filedes) + else: + res = c_pipe(filedes) # 'flags' ignored + handle_posix_error('pipe', res) return (widen(filedes[0]), widen(filedes[1])) finally: lltype.free(filedes, flavor='raw') +def pipe2(flags): + # Only available if there is really a c_pipe2 function. + # No fallback to pipe() if we get ENOSYS. + filedes = lltype.malloc(INT_ARRAY_P.TO, 2, flavor='raw') + try: + res = c_pipe2(filedes, flags) + handle_posix_error('pipe2', res) + return (widen(filedes[0]), widen(filedes[1])) + finally: + lltype.free(filedes, flavor='raw') + c_link = external('link', [rffi.CCHARP, rffi.CCHARP], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO,) c_symlink = external('symlink', [rffi.CCHARP, rffi.CCHARP], rffi.INT, @@ -2088,14 +2140,46 @@ eci_inheritable = eci.merge(ExternalCompilationInfo( - separate_module_sources=[""" + separate_module_sources=[r""" +#include <errno.h> + RPY_EXTERN int rpy_set_inheritable(int fd, int inheritable) { - /* XXX minimal impl. XXX */ - int request = inheritable ? FIONCLEX : FIOCLEX; - return ioctl(fd, request, NULL); + static int ioctl_works = -1; + int flags; + + if (ioctl_works != 0) { + int request = inheritable ? FIONCLEX : FIOCLEX; + int err = ioctl(fd, request, NULL); + if (!err) { + ioctl_works = 1; + return 0; + } + + if (errno != ENOTTY && errno != EACCES) { + return -1; + } + else { + /* ENOTTY: The ioctl is declared but not supported by the + kernel. EACCES: SELinux policy, this can be the case on + Android. */ + ioctl_works = 0; + } + /* fallback to fcntl() if ioctl() does not work */ + } + + flags = fcntl(fd, F_GETFD); + if (flags < 0) + return -1; + + if (inheritable) + flags &= ~FD_CLOEXEC; + else + flags |= FD_CLOEXEC; + return fcntl(fd, F_SETFD, flags); } + RPY_EXTERN int rpy_get_inheritable(int fd) { @@ -2104,8 +2188,64 @@ return -1; return !(flags & FD_CLOEXEC); } - """], - post_include_bits=['RPY_EXTERN int rpy_set_inheritable(int, int);'])) + +RPY_EXTERN +int rpy_dup_noninheritable(int fd) +{ +#ifdef _WIN32 +#error NotImplementedError +#endif + +#ifdef F_DUPFD_CLOEXEC + return fcntl(fd, F_DUPFD_CLOEXEC, 0); +#else + fd = dup(fd); + if (fd >= 0) { + if (rpy_set_inheritable(fd, 0) != 0) { + close(fd); + return -1; + } + } + return fd; +#endif +} + +RPY_EXTERN +int rpy_dup2_noninheritable(int fd, int fd2) +{ +#ifdef _WIN32 +#error NotImplementedError +#endif + +#ifdef F_DUP2FD_CLOEXEC + return fcntl(fd, F_DUP2FD_CLOEXEC, fd2); + +#else +# if %(HAVE_DUP3)d /* HAVE_DUP3 */ + static int dup3_works = -1; + if (dup3_works != 0) { + if (dup3(fd, fd2, O_CLOEXEC) >= 0) + return 0; + if (dup3_works == -1) + dup3_works = (errno != ENOSYS); + if (dup3_works) + return -1; + } +# endif + if (dup2(fd, fd2) < 0) + return -1; + if (rpy_set_inheritable(fd2, 0) != 0) { + close(fd2); + return -1; + } + return 0; +#endif +} + """ % {'HAVE_DUP3': HAVE_DUP3}], + post_include_bits=['RPY_EXTERN int rpy_set_inheritable(int, int);\n' + 'RPY_EXTERN int rpy_get_inheritable(int);\n' + 'RPY_EXTERN int rpy_dup_noninheritable(int);\n' + 'RPY_EXTERN int rpy_dup2_noninheritable(int, int);\n'])) c_set_inheritable = external('rpy_set_inheritable', [rffi.INT, rffi.INT], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO, @@ -2113,12 +2253,56 @@ c_get_inheritable = external('rpy_get_inheritable', [rffi.INT], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO, compilation_info=eci_inheritable) +c_dup_noninheritable = external('rpy_dup_noninheritable', [rffi.INT], + rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO, + compilation_info=eci_inheritable) +c_dup2_noninheritable = external('rpy_dup2_noninheritable', [rffi.INT,rffi.INT], + rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO, + compilation_info=eci_inheritable) def set_inheritable(fd, inheritable): - error = c_set_inheritable(fd, inheritable) - handle_posix_error('set_inheritable', error) + result = c_set_inheritable(fd, inheritable) + handle_posix_error('set_inheritable', result) def get_inheritable(fd): res = c_get_inheritable(fd) res = handle_posix_error('get_inheritable', res) return res != 0 + +class SetNonInheritableCache(object): + """Make one prebuilt instance of this for each path that creates + file descriptors, where you don't necessarily know if that function + returns inheritable or non-inheritable file descriptors. + """ + _immutable_fields_ = ['cached_inheritable?'] + cached_inheritable = -1 # -1 = don't know yet; 0 = off; 1 = on + + def set_non_inheritable(self, fd): + if self.cached_inheritable == -1: + self.cached_inheritable = get_inheritable(fd) + if self.cached_inheritable == 1: + # 'fd' is inheritable; we must manually turn it off + set_inheritable(fd, False) + + def _cleanup_(self): + self.cached_inheritable = -1 + +class ENoSysCache(object): + """Cache whether a system call returns ENOSYS or not.""" + _immutable_fields_ = ['cached_nosys?'] + cached_nosys = -1 # -1 = don't know; 0 = no; 1 = yes, getting ENOSYS + + def attempt_syscall(self): + return self.cached_nosys != 1 + + def fallback(self, res): + nosys = self.cached_nosys + if nosys == -1: + nosys = (res < 0 and get_saved_errno() == errno.ENOSYS) + self.cached_nosys = nosys + return nosys + + def _cleanup_(self): + self.cached_nosys = -1 + +_pipe2_syscall = ENoSysCache() diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py --- a/rpython/rlib/rsocket.py +++ b/rpython/rlib/rsocket.py @@ -8,10 +8,11 @@ # XXX this does not support yet the least common AF_xxx address families # supported by CPython. See http://bugs.pypy.org/issue1942 +from errno import EINVAL from rpython.rlib import _rsocket_rffi as _c, jit, rgc from rpython.rlib.objectmodel import instantiate, keepalive_until_here from rpython.rlib.rarithmetic import intmask, r_uint -from rpython.rlib import rthread +from rpython.rlib import rthread, rposix from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rtyper.lltypesystem.rffi import sizeof, offsetof from rpython.rtyper.extregistry import ExtRegistryEntry @@ -522,12 +523,28 @@ timeout = -1.0 def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, - fd=_c.INVALID_SOCKET): + fd=_c.INVALID_SOCKET, inheritable=True): """Create a new socket.""" if _c.invalid_socket(fd): - fd = _c.socket(family, type, proto) - if _c.invalid_socket(fd): - raise self.error_handler() + if not inheritable and SOCK_CLOEXEC is not None: + # Non-inheritable: we try to call socket() with + # SOCK_CLOEXEC, which may fail. If we get EINVAL, + # then we fall back to the SOCK_CLOEXEC-less case. + fd = _c.socket(family, type | SOCK_CLOEXEC, proto) + if fd < 0: + if _c.geterrno() == EINVAL: + # Linux older than 2.6.27 does not support + # SOCK_CLOEXEC. An EINVAL might be caused by + # random other things, though. Don't cache. + pass + else: + raise self.error_handler() + if _c.invalid_socket(fd): + fd = _c.socket(family, type, proto) + if _c.invalid_socket(fd): + raise self.error_handler() + if not inheritable: + sock_set_inheritable(fd, False) # PLAT RISCOS self.fd = fd self.family = family @@ -630,20 +647,33 @@ return addr, addr.addr_p, addrlen_p @jit.dont_look_inside - def accept(self): + def accept(self, inheritable=True): """Wait for an incoming connection. Return (new socket fd, client address).""" if self._select(False) == 1: raise SocketTimeout address, addr_p, addrlen_p = self._addrbuf() try: - newfd = _c.socketaccept(self.fd, addr_p, addrlen_p) + remove_inheritable = not inheritable + if (not inheritable and SOCK_CLOEXEC is not None + and _c.HAVE_ACCEPT4 + and _accept4_syscall.attempt_syscall()): + newfd = _c.socketaccept4(self.fd, addr_p, addrlen_p, + SOCK_CLOEXEC) + if _accept4_syscall.fallback(newfd): + newfd = _c.socketaccept(self.fd, addr_p, addrlen_p) + else: + remove_inheritable = False + else: + newfd = _c.socketaccept(self.fd, addr_p, addrlen_p) addrlen = addrlen_p[0] finally: lltype.free(addrlen_p, flavor='raw') address.unlock() if _c.invalid_socket(newfd): raise self.error_handler() + if remove_inheritable: + sock_set_inheritable(newfd, False) address.addrlen = rffi.cast(lltype.Signed, addrlen) return (newfd, address) @@ -1032,6 +1062,12 @@ return result make_socket._annspecialcase_ = 'specialize:arg(4)' +def sock_set_inheritable(fd, inheritable): + try: + rposix.set_inheritable(fd, inheritable) + except OSError as e: + raise CSocketError(e.errno) + class SocketError(Exception): applevelerrcls = 'error' def __init__(self): @@ -1090,7 +1126,7 @@ if hasattr(_c, 'socketpair'): def socketpair(family=socketpair_default_family, type=SOCK_STREAM, proto=0, - SocketClass=RSocket): + SocketClass=RSocket, inheritable=True): """socketpair([family[, type[, proto]]]) -> (socket object, socket object) Create a pair of socket objects from the sockets returned by the platform @@ -1099,17 +1135,42 @@ AF_UNIX if defined on the platform; otherwise, the default is AF_INET. """ result = lltype.malloc(_c.socketpair_t, 2, flavor='raw') - res = _c.socketpair(family, type, proto, result) - if res < 0: - raise last_error() - fd0 = rffi.cast(lltype.Signed, result[0]) - fd1 = rffi.cast(lltype.Signed, result[1]) - lltype.free(result, flavor='raw') + try: + res = -1 + remove_inheritable = not inheritable + if not inheritable and SOCK_CLOEXEC is not None: + # Non-inheritable: we try to call socketpair() with + # SOCK_CLOEXEC, which may fail. If we get EINVAL, + # then we fall back to the SOCK_CLOEXEC-less case. + res = _c.socketpair(family, type | SOCK_CLOEXEC, + proto, result) + if res < 0: + if _c.geterrno() == EINVAL: + # Linux older than 2.6.27 does not support + # SOCK_CLOEXEC. An EINVAL might be caused by + # random other things, though. Don't cache. + pass + else: + raise last_error() + else: + remove_inheritable = False + # + if res < 0: + res = _c.socketpair(family, type, proto, result) + if res < 0: + raise last_error() + fd0 = rffi.cast(lltype.Signed, result[0]) + fd1 = rffi.cast(lltype.Signed, result[1]) + finally: + lltype.free(result, flavor='raw') + if remove_inheritable: + sock_set_inheritable(fd0, False) + sock_set_inheritable(fd1, False) return (make_socket(fd0, family, type, proto, SocketClass), make_socket(fd1, family, type, proto, SocketClass)) if _c.WIN32: - def dup(fd): + def dup(fd, inheritable=True): with lltype.scoped_alloc(_c.WSAPROTOCOL_INFO, zero=True) as info: if _c.WSADuplicateSocket(fd, rwin32.GetCurrentProcessId(), info): raise last_error() @@ -1120,15 +1181,16 @@ raise last_error() return result else: - def dup(fd): - fd = _c.dup(fd) + def dup(fd, inheritable=True): + fd = rposix._dup(fd, inheritable) if fd < 0: raise last_error() return fd -def fromfd(fd, family, type, proto=0, SocketClass=RSocket): +def fromfd(fd, family, type, proto=0, SocketClass=RSocket, inheritable=True): # Dup the fd so it and the socket can be closed independently - return make_socket(dup(fd), family, type, proto, SocketClass) + fd = dup(fd, inheritable=inheritable) + return make_socket(fd, family, type, proto, SocketClass) def getdefaulttimeout(): return defaults.timeout @@ -1405,3 +1467,5 @@ if timeout < 0.0: timeout = -1.0 defaults.timeout = timeout + +_accept4_syscall = rposix.ENoSysCache() diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py --- a/rpython/rlib/test/test_rposix.py +++ b/rpython/rlib/test/test_rposix.py @@ -589,3 +589,18 @@ assert rposix.get_inheritable(fd1) == False os.close(fd1) os.close(fd2) + +def test_SetNonInheritableCache(): + cache = rposix.SetNonInheritableCache() + fd1, fd2 = os.pipe() + assert rposix.get_inheritable(fd1) == True + assert rposix.get_inheritable(fd1) == True + assert cache.cached_inheritable == -1 + cache.set_non_inheritable(fd1) + assert cache.cached_inheritable == 1 + cache.set_non_inheritable(fd2) + assert cache.cached_inheritable == 1 + assert rposix.get_inheritable(fd1) == False + assert rposix.get_inheritable(fd1) == False + os.close(fd1) + os.close(fd2) diff --git a/rpython/rlib/test/test_rsocket.py b/rpython/rlib/test/test_rsocket.py --- a/rpython/rlib/test/test_rsocket.py +++ b/rpython/rlib/test/test_rsocket.py @@ -119,6 +119,16 @@ s1.close() s2.close() +def test_socketpair_inheritable(): + if sys.platform == "win32": + py.test.skip('No socketpair on Windows') + for inh in [False, True]: + s1, s2 = socketpair(inheritable=inh) + assert rposix.get_inheritable(s1.fd) == inh + assert rposix.get_inheritable(s2.fd) == inh + s1.close() + s2.close() + def test_socketpair_recvinto_1(): class Buffer: def setslice(self, start, string): @@ -378,6 +388,12 @@ s1.close() s2.close() +def test_inheritable(): + for inh in [False, True]: + s1 = RSocket(inheritable=inh) + assert rposix.get_inheritable(s1.fd) == inh + s1.close() + def test_getaddrinfo_http(): lst = getaddrinfo('localhost', 'http') assert isinstance(lst, list) diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -237,8 +237,10 @@ " directly to a VOIDP argument") _oops._annspecialcase_ = 'specialize:memo' + nb_args = len(args) unrolling_arg_tps = unrolling_iterable(enumerate(args)) def wrapper(*args): + assert len(args) == nb_args real_args = () # XXX 'to_free' leaks if an allocation fails with MemoryError # and was not the first in this function diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py b/rpython/rtyper/lltypesystem/test/test_rffi.py --- a/rpython/rtyper/lltypesystem/test/test_rffi.py +++ b/rpython/rtyper/lltypesystem/test/test_rffi.py @@ -49,6 +49,7 @@ eci = ExternalCompilationInfo(includes=['stuff.h'], include_dirs=[udir]) z = llexternal('X', [Signed], Signed, compilation_info=eci) + py.test.raises(AssertionError, z, 8, 9) def f(): return z(8) diff --git a/rpython/translator/sandbox/test/test_sandbox.py b/rpython/translator/sandbox/test/test_sandbox.py --- a/rpython/translator/sandbox/test/test_sandbox.py +++ b/rpython/translator/sandbox/test/test_sandbox.py @@ -58,7 +58,7 @@ exe = compile(entry_point) g, f = run_in_subprocess(exe) expect(f, g, "ll_os.ll_os_open", ("/tmp/foobar", os.O_RDONLY, 0777), 77) - expect(f, g, "ll_os.ll_os_dup", (77,), 78) + expect(f, g, "ll_os.ll_os_dup", (77, True), 78) g.close() tail = f.read() f.close() @@ -94,7 +94,7 @@ exe = compile(entry_point) g, f = run_in_subprocess(exe) - expect(f, g, "ll_os.ll_os_dup2", (34, 56), None) + expect(f, g, "ll_os.ll_os_dup2", (34, 56, True), None) expect(f, g, "ll_os.ll_os_access", ("spam", 77), True) g.close() tail = f.read() @@ -134,7 +134,7 @@ exe = compile(entry_point) g, f = run_in_subprocess(exe) expect(f, g, "ll_time.ll_time_time", (), 3.141592) - expect(f, g, "ll_os.ll_os_dup", (3141,), 3) + expect(f, g, "ll_os.ll_os_dup", (3141, True), 3) g.close() tail = f.read() f.close() @@ -149,7 +149,7 @@ exe = compile(entry_point) g, f = run_in_subprocess(exe) expect(f, g, "ll_os.ll_os_getcwd", (), "/tmp/foo/bar") - expect(f, g, "ll_os.ll_os_dup", (len("/tmp/foo/bar"),), 3) + expect(f, g, "ll_os.ll_os_dup", (len("/tmp/foo/bar"), True), 3) g.close() tail = f.read() f.close() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit