[pypy-commit] pypy py3.5: hg merge py3.5-noninherit
arigo
pypy.commits at gmail.com
Sat Aug 27 10:38:48 EDT 2016
Author: Armin Rigo <arigo at 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)
- at 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)
- at unwrap_spec(old_fd=c_int, new_fd=c_int)
-def dup2(space, old_fd, new_fd):
+ at 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)
+ at 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)
+
+ at 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)])
+
+ at 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()
More information about the pypy-commit
mailing list