Issue #18571: Implementation of the PEP 446: file descriptors and file handles

are now created non-inheritable; add functions os.get/set_inheritable(),
os.get/set_handle_inheritable() and socket.socket.get/set_inheritable().
This commit is contained in:
Victor Stinner 2013-08-28 00:53:59 +02:00
parent 46e1ce214b
commit daf455554b
51 changed files with 1448 additions and 317 deletions

View file

@ -1694,26 +1694,24 @@ PyCursesWindow_PutWin(PyCursesWindowObject *self, PyObject *stream)
/* We have to simulate this by writing to a temporary FILE*,
then reading back, then writing to the argument stream. */
char fn[100];
int fd;
FILE *fp;
PyObject *res;
int fd = -1;
FILE *fp = NULL;
PyObject *res = NULL;
strcpy(fn, "/tmp/py.curses.putwin.XXXXXX");
fd = mkstemp(fn);
if (fd < 0)
return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
if (_Py_set_inheritable(fd, 0, NULL) < 0)
goto exit;
fp = fdopen(fd, "wb+");
if (fp == NULL) {
close(fd);
remove(fn);
return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
goto exit;
}
res = PyCursesCheckERR(putwin(self->win, fp), "putwin");
if (res == NULL) {
fclose(fp);
remove(fn);
return res;
}
if (res == NULL)
goto exit;
fseek(fp, 0, 0);
while (1) {
char buf[BUFSIZ];
@ -1727,7 +1725,12 @@ PyCursesWindow_PutWin(PyCursesWindowObject *self, PyObject *stream)
if (res == NULL)
break;
}
fclose(fp);
exit:
if (fp != NULL)
fclose(fp);
else if (fd != -1)
close(fd);
remove(fn);
return res;
}
@ -2252,12 +2255,13 @@ static PyObject *
PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream)
{
char fn[100];
int fd;
FILE *fp;
int fd = -1;
FILE *fp = NULL;
PyObject *data;
size_t datalen;
WINDOW *win;
_Py_IDENTIFIER(read);
PyObject *res = NULL;
PyCursesInitialised;
@ -2265,44 +2269,47 @@ PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream)
fd = mkstemp(fn);
if (fd < 0)
return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
if (_Py_set_inheritable(fd, 0, NULL) < 0)
goto error;
fp = fdopen(fd, "wb+");
if (fp == NULL) {
close(fd);
remove(fn);
return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
goto error;
}
data = _PyObject_CallMethodId(stream, &PyId_read, "");
if (data == NULL) {
fclose(fp);
remove(fn);
return NULL;
}
if (data == NULL)
goto error;
if (!PyBytes_Check(data)) {
PyErr_Format(PyExc_TypeError,
"f.read() returned %.100s instead of bytes",
data->ob_type->tp_name);
Py_DECREF(data);
fclose(fp);
remove(fn);
return NULL;
goto error;
}
datalen = PyBytes_GET_SIZE(data);
if (fwrite(PyBytes_AS_STRING(data), 1, datalen, fp) != datalen) {
Py_DECREF(data);
fclose(fp);
remove(fn);
return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
goto error;
}
Py_DECREF(data);
fseek(fp, 0, 0);
win = getwin(fp);
fclose(fp);
remove(fn);
if (win == NULL) {
PyErr_SetString(PyCursesError, catchall_NULL);
return NULL;
goto error;
}
return PyCursesWindow_New(win, NULL);
res = PyCursesWindow_New(win, NULL);
error:
if (fp != NULL)
fclose(fp);
else if (fd != -1)
close(fd);
remove(fn);
return res;
}
static PyObject *

View file

@ -202,6 +202,9 @@ 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)
@ -221,6 +224,11 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
int fd = -1;
int closefd = 1;
int fd_is_own = 0;
#ifdef O_CLOEXEC
int *atomic_flag_works = &_Py_open_cloexec_works;
#elif !defined(MS_WINDOWS)
int *atomic_flag_works = NULL;
#endif
assert(PyFileIO_Check(oself));
if (self->fd >= 0) {
@ -345,6 +353,11 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
if (append)
flags |= O_APPEND;
#endif
#ifdef MS_WINDOWS
flags |= O_NOINHERIT;
#elif defined(O_CLOEXEC)
flags |= O_CLOEXEC;
#endif
if (fd >= 0) {
if (check_fd(fd))
@ -369,10 +382,18 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
else
#endif
self->fd = open(name, flags, 0666);
Py_END_ALLOW_THREADS
} else {
PyObject *fdobj = PyObject_CallFunction(
opener, "Oi", nameobj, flags);
}
else {
PyObject *fdobj;
#ifndef MS_WINDOWS
/* the opener may clear the atomic flag */
atomic_flag_works = NULL;
#endif
fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags);
if (fdobj == NULL)
goto error;
if (!PyLong_Check(fdobj)) {
@ -394,6 +415,11 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
goto error;
}
#ifndef MS_WINDOWS
if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
goto error;
#endif
}
if (dircheck(self, nameobj) < 0)
goto error;

View file

@ -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 @@ _is_fdescfs_mounted_on_dev_fd(void)
if (stat("/dev", &dev_stat) != 0)
return 0;
if (stat(FD_DIR, &dev_fd_stat) != 0)
return 0;
return 0;
if (dev_stat.st_dev == dev_fd_stat.st_dev)
return 0; /* / == /dev == /dev/fd means it is static. #fail */
return 1;
@ -136,6 +136,29 @@ _is_fd_in_sorted_fd_sequence(int fd, PyObject *fd_sequence)
return 0;
}
static int
make_inheritable(PyObject *py_fds_to_keep, int errpipe_write)
{
Py_ssize_t i, len;
len = PySequence_Length(py_fds_to_keep);
for (i = 0; i < len; ++i) {
PyObject* fdobj = PySequence_Fast_GET_ITEM(py_fds_to_keep, i);
long fd = PyLong_AsLong(fdobj);
assert(!PyErr_Occurred());
assert(0 <= fd && fd <= INT_MAX);
if (fd == errpipe_write) {
/* errpipe_write is part of py_fds_to_keep. It must be closed at
exec(), but kept open in the child process until exec() is
called. */
continue;
}
if (_Py_set_inheritable((int)fd, 1, NULL) < 0)
return -1;
}
return 0;
}
/* Close all file descriptors in the range start_fd inclusive to
* end_fd exclusive except for those in py_fds_to_keep. If the
@ -205,18 +228,8 @@ _close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep)
int fd_dir_fd;
if (start_fd >= end_fd)
return;
#ifdef O_CLOEXEC
fd_dir_fd = open(FD_DIR, O_RDONLY | O_CLOEXEC, 0);
#else
fd_dir_fd = open(FD_DIR, O_RDONLY, 0);
#ifdef FD_CLOEXEC
{
int old = fcntl(fd_dir_fd, F_GETFD);
if (old != -1)
fcntl(fd_dir_fd, F_SETFD, old | FD_CLOEXEC);
}
#endif
#endif
fd_dir_fd = _Py_open(FD_DIR, O_RDONLY);
if (fd_dir_fd == -1) {
/* No way to get a list of open fds. */
_close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep);
@ -356,16 +369,16 @@ child_exec(char *const exec_array[],
/* Buffer large enough to hold a hex integer. We can't malloc. */
char hex_errno[sizeof(saved_errno)*2+1];
if (make_inheritable(py_fds_to_keep, errpipe_write) < 0)
goto error;
/* Close parent's pipe ends. */
if (p2cwrite != -1) {
if (p2cwrite != -1)
POSIX_CALL(close(p2cwrite));
}
if (c2pread != -1) {
if (c2pread != -1)
POSIX_CALL(close(c2pread));
}
if (errread != -1) {
if (errread != -1)
POSIX_CALL(close(errread));
}
POSIX_CALL(close(errpipe_read));
/* When duping fds, if there arises a situation where one of the fds is
@ -379,38 +392,34 @@ child_exec(char *const exec_array[],
dup2() removes the CLOEXEC flag but we must do it ourselves if dup2()
would be a no-op (issue #10806). */
if (p2cread == 0) {
int old = fcntl(p2cread, F_GETFD);
if (old != -1)
fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC);
} else if (p2cread != -1) {
if (_Py_set_inheritable(p2cread, 1, NULL) < 0)
goto error;
}
else if (p2cread != -1)
POSIX_CALL(dup2(p2cread, 0)); /* stdin */
}
if (c2pwrite == 1) {
int old = fcntl(c2pwrite, F_GETFD);
if (old != -1)
fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC);
} else if (c2pwrite != -1) {
if (_Py_set_inheritable(c2pwrite, 1, NULL) < 0)
goto error;
}
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) {
POSIX_CALL(dup2(errwrite, 2)); /* stderr */
if (_Py_set_inheritable(errwrite, 1, NULL) < 0)
goto error;
}
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 (cwd)
POSIX_CALL(chdir(cwd));
@ -544,7 +553,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
PyObject *result;
_Py_IDENTIFIER(isenabled);
_Py_IDENTIFIER(disable);
gc_module = PyImport_ImportModule("gc");
if (gc_module == NULL)
return NULL;
@ -721,52 +730,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,
@ -775,7 +738,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 */
};

View file

@ -2350,7 +2350,7 @@ load_dh_params(PySSLContext *self, PyObject *filepath)
FILE *f;
DH *dh;
f = _Py_fopen(filepath, "rb");
f = _Py_fopen_obj(filepath, "rb");
if (f == NULL) {
if (!PyErr_Occurred())
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);

View file

@ -143,7 +143,7 @@ static void RunStartupFile(PyCompilerFlags *cf)
{
char *startup = Py_GETENV("PYTHONSTARTUP");
if (startup != NULL && startup[0] != '\0') {
FILE *fp = fopen(startup, "r");
FILE *fp = _Py_fopen(startup, "r");
if (fp != NULL) {
(void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
PyErr_Clear();

View file

@ -1208,18 +1208,18 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
flags |= MAP_ANONYMOUS;
#else
/* SVR4 method to map anonymous memory is to open /dev/zero */
fd = devzero = open("/dev/zero", O_RDWR);
fd = devzero = _Py_open("/dev/zero", O_RDWR);
if (devzero == -1) {
Py_DECREF(m_obj);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
#endif
} else {
m_obj->fd = dup(fd);
}
else {
m_obj->fd = _Py_dup(fd);
if (m_obj->fd == -1) {
Py_DECREF(m_obj);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
}

View file

@ -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;
}

View file

@ -3535,10 +3535,7 @@ _posix_listdir(path_t *path, PyObject *list)
#ifdef HAVE_FDOPENDIR
if (path->fd != -1) {
/* closedir() closes the FD, so we duplicate it */
Py_BEGIN_ALLOW_THREADS
fd = dup(path->fd);
Py_END_ALLOW_THREADS
fd = _Py_dup(path->fd);
if (fd == -1) {
list = posix_error();
goto exit;
@ -5768,7 +5765,7 @@ 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;
int master_fd = -1, slave_fd = -1;
#ifndef HAVE_OPENPTY
char * slave_name;
#endif
@ -5781,37 +5778,52 @@ posix_openpty(PyObject *self, PyObject *noargs)
#ifdef HAVE_OPENPTY
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0)
return posix_error();
goto posix_error;
if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
goto error;
if (_Py_set_inheritable(slave_fd, 0, NULL) < 0)
goto error;
#elif defined(HAVE__GETPTY)
slave_name = _getpty(&master_fd, O_RDWR, 0666, 0);
if (slave_name == NULL)
return posix_error();
goto posix_error;
if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
goto error;
slave_fd = open(slave_name, O_RDWR);
slave_fd = _Py_open(slave_name, O_RDWR);
if (slave_fd < 0)
return posix_error();
goto posix_error;
#else
master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */
master_fd = _Py_open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */
if (master_fd < 0)
return posix_error();
goto posix_error;
sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL);
/* change permission of slave */
if (grantpt(master_fd) < 0) {
PyOS_setsig(SIGCHLD, sig_saved);
return posix_error();
goto posix_error;
}
/* unlock slave */
if (unlockpt(master_fd) < 0) {
PyOS_setsig(SIGCHLD, sig_saved);
return posix_error();
goto posix_error;
}
PyOS_setsig(SIGCHLD, sig_saved);
slave_name = ptsname(master_fd); /* get name of slave */
if (slave_name == NULL)
return posix_error();
slave_fd = open(slave_name, O_RDWR | O_NOCTTY); /* open slave */
goto posix_error;
slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */
if (slave_fd < 0)
return posix_error();
goto posix_error;
#if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC)
ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */
ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */
@ -5823,6 +5835,14 @@ posix_openpty(PyObject *self, PyObject *noargs)
return Py_BuildValue("(ii)", master_fd, slave_fd);
posix_error:
posix_error();
error:
if (master_fd != -1)
close(master_fd);
if (slave_fd != -1)
close(slave_fd);
return NULL;
}
#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */
@ -7432,6 +7452,10 @@ posix_tcsetpgrp(PyObject *self, PyObject *args)
/* Functions acting on file descriptors */
#ifdef O_CLOEXEC
extern int _Py_open_cloexec_works;
#endif
PyDoc_STRVAR(posix_open__doc__,
"open(path, flags, mode=0o777, *, dir_fd=None)\n\n\
Open a file for low level IO. Returns a file handle (integer).\n\
@ -7451,6 +7475,11 @@ posix_open(PyObject *self, PyObject *args, PyObject *kwargs)
int fd;
PyObject *return_value = NULL;
static char *keywords[] = {"path", "flags", "mode", "dir_fd", NULL};
#ifdef O_CLOEXEC
int *atomic_flag_works = &_Py_open_cloexec_works;
#elif !defined(MS_WINDOWS)
int *atomic_flag_works = NULL;
#endif
memset(&path, 0, sizeof(path));
path.function_name = "open";
@ -7465,6 +7494,12 @@ posix_open(PyObject *self, PyObject *args, PyObject *kwargs)
))
return NULL;
#ifdef MS_WINDOWS
flags |= O_NOINHERIT;
#elif defined(O_CLOEXEC)
flags |= O_CLOEXEC;
#endif
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS
if (path.wide)
@ -7484,6 +7519,13 @@ posix_open(PyObject *self, PyObject *args, PyObject *kwargs)
goto exit;
}
#ifndef MS_WINDOWS
if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) {
close(fd);
goto exit;
}
#endif
return_value = PyLong_FromLong((long)fd);
exit:
@ -7540,13 +7582,14 @@ static PyObject *
posix_dup(PyObject *self, PyObject *args)
{
int fd;
if (!PyArg_ParseTuple(args, "i:dup", &fd))
return NULL;
if (!_PyVerify_fd(fd))
return posix_error();
fd = dup(fd);
if (fd < 0)
return posix_error();
fd = _Py_dup(fd);
if (fd == -1)
return NULL;
return PyLong_FromLong((long)fd);
}
@ -7556,16 +7599,82 @@ PyDoc_STRVAR(posix_dup2__doc__,
Duplicate file descriptor.");
static PyObject *
posix_dup2(PyObject *self, PyObject *args)
posix_dup2(PyObject *self, PyObject *args, PyObject *kwargs)
{
int fd, fd2, res;
if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2))
static char *keywords[] = {"fd", "fd2", "inheritable", NULL};
int fd, fd2;
int inheritable = 1;
int res;
#if defined(HAVE_DUP3) && \
!(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC))
/* dup3() is available on Linux 2.6.27+ and glibc 2.9 */
int dup3_works = -1;
#endif
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|i:dup2", keywords,
&fd, &fd2, &inheritable))
return NULL;
if (!_PyVerify_fd_dup2(fd, fd2))
return posix_error();
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
res = dup2(fd, fd2);
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error();
/* Character files like console cannot be make non-inheritable */
if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
close(fd2);
return NULL;
}
#elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)
Py_BEGIN_ALLOW_THREADS
if (!inheritable)
res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2);
else
res = dup2(fd, fd2);
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error();
#else
#ifdef HAVE_DUP3
if (!inheritable && dup3_works != 0) {
Py_BEGIN_ALLOW_THREADS
res = dup3(fd, fd2, O_CLOEXEC);
Py_END_ALLOW_THREADS
if (res < 0) {
if (dup3_works == -1)
dup3_works = (errno != ENOSYS);
if (dup3_works)
return posix_error();
}
}
if (inheritable || dup3_works == 0)
{
#endif
Py_BEGIN_ALLOW_THREADS
res = dup2(fd, fd2);
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error();
if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
close(fd2);
return NULL;
}
#ifdef HAVE_DUP3
}
#endif
#endif
Py_INCREF(Py_None);
return Py_None;
}
@ -8052,24 +8161,69 @@ Create a pipe.");
static PyObject *
posix_pipe(PyObject *self, PyObject *noargs)
{
#if !defined(MS_WINDOWS)
int fds[2];
int res;
res = pipe(fds);
if (res != 0)
return posix_error();
return Py_BuildValue("(ii)", fds[0], fds[1]);
#else /* MS_WINDOWS */
#ifdef MS_WINDOWS
HANDLE read, write;
int read_fd, write_fd;
SECURITY_ATTRIBUTES attr;
BOOL ok;
ok = CreatePipe(&read, &write, NULL, 0);
#else
int res;
#endif
#ifdef MS_WINDOWS
attr.nLength = sizeof(attr);
attr.lpSecurityDescriptor = NULL;
attr.bInheritHandle = FALSE;
Py_BEGIN_ALLOW_THREADS
ok = CreatePipe(&read, &write, &attr, 0);
if (ok) {
fds[0] = _open_osfhandle((Py_intptr_t)read, _O_RDONLY);
fds[1] = _open_osfhandle((Py_intptr_t)write, _O_WRONLY);
if (fds[0] == -1 || fds[1] == -1) {
CloseHandle(read);
CloseHandle(write);
ok = 0;
}
}
Py_END_ALLOW_THREADS
if (!ok)
return PyErr_SetFromWindowsErr(0);
read_fd = _open_osfhandle((Py_intptr_t)read, 0);
write_fd = _open_osfhandle((Py_intptr_t)write, 1);
return Py_BuildValue("(ii)", read_fd, write_fd);
#endif /* MS_WINDOWS */
#else
#ifdef HAVE_PIPE2
Py_BEGIN_ALLOW_THREADS
res = pipe2(fds, O_CLOEXEC);
Py_END_ALLOW_THREADS
if (res != 0 && errno == ENOSYS)
{
#endif
Py_BEGIN_ALLOW_THREADS
res = pipe(fds);
Py_END_ALLOW_THREADS
if (res == 0) {
if (_Py_set_inheritable(fds[0], 0, NULL) < 0) {
close(fds[0]);
close(fds[1]);
return NULL;
}
if (_Py_set_inheritable(fds[1], 0, NULL) < 0) {
close(fds[0]);
close(fds[1]);
return NULL;
}
}
#ifdef HAVE_PIPE2
}
#endif
if (res != 0)
return PyErr_SetFromErrno(PyExc_OSError);
#endif /* !MS_WINDOWS */
return Py_BuildValue("(ii)", fds[0], fds[1]);
}
#endif /* HAVE_PIPE */
@ -10659,6 +10813,102 @@ posix_cpu_count(PyObject *self)
Py_RETURN_NONE;
}
PyDoc_STRVAR(get_inheritable__doc__,
"get_inheritable(fd) -> bool\n" \
"\n" \
"Get the close-on-exe flag of the specified file descriptor.");
static PyObject*
posix_get_inheritable(PyObject *self, PyObject *args)
{
int fd;
int inheritable;
if (!PyArg_ParseTuple(args, "i:get_inheritable", &fd))
return NULL;
if (!_PyVerify_fd(fd))
return posix_error();
inheritable = _Py_get_inheritable(fd);
if (inheritable < 0)
return NULL;
return PyBool_FromLong(inheritable);
}
PyDoc_STRVAR(set_inheritable__doc__,
"set_inheritable(fd, inheritable)\n" \
"\n" \
"Set the inheritable flag of the specified file descriptor.");
static PyObject*
posix_set_inheritable(PyObject *self, PyObject *args)
{
int fd, inheritable;
if (!PyArg_ParseTuple(args, "ii:set_inheritable", &fd, &inheritable))
return NULL;
if (!_PyVerify_fd(fd))
return posix_error();
if (_Py_set_inheritable(fd, inheritable, NULL) < 0)
return NULL;
Py_RETURN_NONE;
}
#ifdef MS_WINDOWS
PyDoc_STRVAR(get_handle_inheritable__doc__,
"get_handle_inheritable(fd) -> bool\n" \
"\n" \
"Get the close-on-exe flag of the specified file descriptor.");
static PyObject*
posix_get_handle_inheritable(PyObject *self, PyObject *args)
{
Py_intptr_t handle;
DWORD flags;
if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR ":get_handle_inheritable", &handle))
return NULL;
if (!GetHandleInformation((HANDLE)handle, &flags)) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
return PyBool_FromLong(flags & HANDLE_FLAG_INHERIT);
}
PyDoc_STRVAR(set_handle_inheritable__doc__,
"set_handle_inheritable(fd, inheritable)\n" \
"\n" \
"Set the inheritable flag of the specified handle.");
static PyObject*
posix_set_handle_inheritable(PyObject *self, PyObject *args)
{
int inheritable = 1;
Py_intptr_t handle;
DWORD flags;
if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:set_handle_inheritable",
&handle, &inheritable))
return NULL;
if (inheritable)
flags = HANDLE_FLAG_INHERIT;
else
flags = 0;
if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
Py_RETURN_NONE;
}
#endif /* MS_WINDOWS */
static PyMethodDef posix_methods[] = {
{"access", (PyCFunction)posix_access,
@ -10934,7 +11184,8 @@ static PyMethodDef posix_methods[] = {
{"closerange", posix_closerange, METH_VARARGS, posix_closerange__doc__},
{"device_encoding", device_encoding, METH_VARARGS, device_encoding__doc__},
{"dup", posix_dup, METH_VARARGS, posix_dup__doc__},
{"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__},
{"dup2", (PyCFunction)posix_dup2,
METH_VARARGS | METH_KEYWORDS, posix_dup2__doc__},
#ifdef HAVE_LOCKF
{"lockf", posix_lockf, METH_VARARGS, posix_lockf__doc__},
#endif
@ -11105,6 +11356,14 @@ static PyMethodDef posix_methods[] = {
#endif
{"cpu_count", (PyCFunction)posix_cpu_count,
METH_NOARGS, posix_cpu_count__doc__},
{"get_inheritable", posix_get_inheritable, METH_VARARGS, get_inheritable__doc__},
{"set_inheritable", posix_set_inheritable, METH_VARARGS, set_inheritable__doc__},
#ifdef MS_WINDOWS
{"get_handle_inheritable", posix_get_handle_inheritable,
METH_VARARGS, get_handle_inheritable__doc__},
{"set_handle_inheritable", posix_set_handle_inheritable,
METH_VARARGS, set_handle_inheritable__doc__},
#endif
{NULL, NULL} /* Sentinel */
};

View file

@ -1004,7 +1004,7 @@ newDevPollObject(void)
*/
limit_result = getrlimit(RLIMIT_NOFILE, &limit);
if (limit_result != -1)
fd_devpoll = open("/dev/poll", O_RDWR);
fd_devpoll = _Py_open("/dev/poll", O_RDWR);
Py_END_ALLOW_THREADS
if (limit_result == -1) {
@ -1194,6 +1194,7 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, int flags, SOCKET fd)
if (fd == -1) {
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_EPOLL_CREATE1
flags |= EPOLL_CLOEXEC;
if (flags)
self->epfd = epoll_create1(flags);
else
@ -1209,6 +1210,14 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, int flags, SOCKET fd)
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
#ifndef HAVE_EPOLL_CREATE1
if (_Py_set_inheritable(self->epfd, 0, NULL) < 0) {
Py_DECREF(self);
return NULL;
}
#endif
return (PyObject *)self;
}
@ -1896,13 +1905,19 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd)
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
if (fd == -1) {
if (_Py_set_inheritable(self->kqfd, 0, NULL) < 0) {
Py_DECREF(self);
return NULL;
}
}
return (PyObject *)self;
}
static PyObject *
kqueue_queue_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
if ((args != NULL && PyObject_Size(args)) ||
(kwds != NULL && PyObject_Size(kwds))) {
PyErr_SetString(PyExc_ValueError,

View file

@ -97,13 +97,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) -> socket object\n\
\n\
Open a socket of the given type. The family argument specifies the\n\
address family; it defaults to AF_INET. The type argument specifies\n\
whether this is a stream (SOCK_STREAM, this is the default)\n\
or datagram (SOCK_DGRAM) socket. The protocol argument defaults to 0,\n\
specifying the default protocol. Keyword arguments are accepted.\n\
The socket is created as non-inheritable.\n\
\n\
A socket object represents one endpoint of a network connection.\n\
\n\
@ -114,7 +115,7 @@ bind(addr) -- bind the socket to a local address\n\
close() -- close the socket\n\
connect(addr) -- connect the socket to a remote address\n\
connect_ex(addr) -- connect, return an error code instead of an exception\n\
_dup() -- return a new socket fd duplicated from fileno()\n\
dup() -- return a new socket fd duplicated from fileno()\n\
fileno() -- return underlying file descriptor\n\
getpeername() -- return remote address [*]\n\
getsockname() -- return local address\n\
@ -356,22 +357,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
#endif
#ifdef MS_WINDOWS
/* On Windows a socket is really a handle not an fd */
static SOCKET
dup_socket(SOCKET handle)
{
WSAPROTOCOL_INFO info;
if (WSADuplicateSocket(handle, GetCurrentProcessId(), &info))
return INVALID_SOCKET;
return WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED);
}
#define SOCKETCLOSE closesocket
#else
/* On Unix we can use dup to duplicate the file descriptor of a socket*/
#define dup_socket(fd) dup(fd)
#endif
#ifdef MS_WIN32
@ -499,6 +485,11 @@ select_error(void)
(errno == expected)
#endif
#ifdef MS_WINDOWS
/* Does WSASocket() support the WSA_FLAG_NO_HANDLE_INHERIT flag? */
static int support_wsa_no_inherit = -1;
#endif
/* Convenience function to raise an error according to errno
and return a NULL pointer from a function. */
@ -1955,6 +1946,11 @@ sock_accept(PySocketSockObject *s)
PyObject *addr = NULL;
PyObject *res = NULL;
int timeout;
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
static int accept4_works = -1;
#endif
if (!getsockaddrlen(s, &addrlen))
return NULL;
memset(&addrbuf, 0, addrlen);
@ -1963,10 +1959,24 @@ 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) {
newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
SOCK_CLOEXEC);
if (newfd == INVALID_SOCKET && accept4_works == -1) {
/* On Linux older than 2.6.28, accept4() fails with ENOSYS */
accept4_works = (errno != ENOSYS);
}
}
if (accept4_works == 0)
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#else
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#endif
}
Py_END_ALLOW_THREADS
@ -1979,6 +1989,25 @@ sock_accept(PySocketSockObject *s)
if (newfd == INVALID_SOCKET)
return s->errorhandler();
#ifdef MS_WINDOWS
if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
PyErr_SetFromWindowsErr(0);
SOCKETCLOSE(newfd);
goto finally;
}
#else
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
if (!accept4_works)
#endif
{
if (_Py_set_inheritable(newfd, 0, NULL) < 0) {
SOCKETCLOSE(newfd);
goto finally;
}
}
#endif
sock = PyLong_FromSocket_t(newfd);
if (sock == NULL) {
SOCKETCLOSE(newfd);
@ -3909,6 +3938,12 @@ sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
/* Initialize a new socket object. */
#ifdef SOCK_CLOEXEC
/* socket() and socketpair() fail with EINVAL on Linux kernel older
* than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
static int sock_cloexec_works = -1;
#endif
/*ARGSUSED*/
static int
sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
@ -3918,6 +3953,13 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
SOCKET_T fd = INVALID_SOCKET;
int family = AF_INET, type = SOCK_STREAM, proto = 0;
static char *keywords[] = {"family", "type", "proto", "fileno", 0};
#ifndef MS_WINDOWS
#ifdef SOCK_CLOEXEC
int *atomic_flag_works = &sock_cloexec_works;
#else
int *atomic_flag_works = NULL;
#endif
#endif
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"|iiiO:socket", keywords,
@ -3962,14 +4004,74 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
}
}
else {
#ifdef MS_WINDOWS
/* Windows implementation */
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
Py_BEGIN_ALLOW_THREADS
fd = socket(family, type, proto);
if (support_wsa_no_inherit) {
fd = WSASocket(family, type, proto,
NULL, 0,
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
if (fd == INVALID_SOCKET) {
/* Windows 7 or Windows 2008 R2 without SP1 or the hotfix */
support_wsa_no_inherit = 0;
fd = socket(family, type, proto);
}
}
else {
fd = socket(family, type, proto);
}
Py_END_ALLOW_THREADS
if (fd == INVALID_SOCKET) {
set_error();
return -1;
}
if (!support_wsa_no_inherit) {
if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) {
closesocket(fd);
PyErr_SetFromWindowsErr(0);
return -1;
}
}
#else
/* UNIX */
Py_BEGIN_ALLOW_THREADS
#ifdef SOCK_CLOEXEC
if (sock_cloexec_works != 0) {
fd = socket(family, type | SOCK_CLOEXEC, proto);
if (sock_cloexec_works == -1) {
if (fd >= 0) {
sock_cloexec_works = 1;
}
else if (errno == EINVAL) {
/* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
sock_cloexec_works = 0;
fd = socket(family, type, proto);
}
}
}
else
#endif
{
fd = socket(family, type, proto);
}
Py_END_ALLOW_THREADS
if (fd == INVALID_SOCKET) {
set_error();
return -1;
}
if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) {
SOCKETCLOSE(fd);
return -1;
}
#endif
}
init_sockobject(s, fd, family, type, proto);
@ -4535,16 +4637,36 @@ socket_dup(PyObject *self, PyObject *fdobj)
{
SOCKET_T fd, newfd;
PyObject *newfdobj;
#ifdef MS_WINDOWS
WSAPROTOCOL_INFO info;
#endif
fd = PyLong_AsSocket_t(fdobj);
if (fd == (SOCKET_T)(-1) && PyErr_Occurred())
return NULL;
newfd = dup_socket(fd);
#ifdef MS_WINDOWS
if (WSADuplicateSocket(fd, GetCurrentProcessId(), &info))
return set_error();
newfd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,
&info, 0, WSA_FLAG_OVERLAPPED);
if (newfd == INVALID_SOCKET)
return set_error();
if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
closesocket(newfd);
PyErr_SetFromWindowsErr(0);
return NULL;
}
#else
/* On UNIX, dup can be used to duplicate the file descriptor of a socket */
newfd = _Py_dup(fd);
if (newfd == INVALID_SOCKET)
return NULL;
#endif
newfdobj = PyLong_FromSocket_t(newfd);
if (newfdobj == NULL)
SOCKETCLOSE(newfd);
@ -4572,6 +4694,12 @@ socket_socketpair(PyObject *self, PyObject *args)
SOCKET_T sv[2];
int family, type = SOCK_STREAM, proto = 0;
PyObject *res = NULL;
#ifdef SOCK_CLOEXEC
int *atomic_flag_works = &sock_cloexec_works;
#else
int *atomic_flag_works = NULL;
#endif
int ret;
#if defined(AF_UNIX)
family = AF_UNIX;
@ -4581,9 +4709,38 @@ socket_socketpair(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "|iii:socketpair",
&family, &type, &proto))
return NULL;
/* Create a pair of socket fds */
if (socketpair(family, type, proto, sv) < 0)
Py_BEGIN_ALLOW_THREADS
#ifdef SOCK_CLOEXEC
if (sock_cloexec_works != 0) {
ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv);
if (sock_cloexec_works == -1) {
if (ret >= 0) {
sock_cloexec_works = 1;
}
else if (errno == EINVAL) {
/* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
sock_cloexec_works = 0;
ret = socketpair(family, type, proto, sv);
}
}
}
else
#endif
{
ret = socketpair(family, type, proto, sv);
}
Py_END_ALLOW_THREADS
if (ret < 0)
return set_error();
if (_Py_set_inheritable(sv[0], 0, atomic_flag_works) < 0)
goto finally;
if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0)
goto finally;
s0 = new_sockobject(sv[0], family, type, proto);
if (s0 == NULL)
goto finally;
@ -4605,7 +4762,7 @@ finally:
}
PyDoc_STRVAR(socketpair_doc,
"socketpair([family[, type[, proto]]]) -> (socket object, socket object)\n\
"socketpair([family[, type [, proto]]]) -> (socket object, socket object)\n\
\n\
Create a pair of socket objects from the sockets returned by the platform\n\
socketpair() function.\n\
@ -5539,6 +5696,16 @@ PyInit__socket(void)
if (!os_init())
return NULL;
#ifdef MS_WINDOWS
if (support_wsa_no_inherit == -1) {
DWORD version = GetVersion();
DWORD major = (DWORD)LOBYTE(LOWORD(version));
DWORD minor = (DWORD)HIBYTE(LOWORD(version));
/* need Windows 7 SP1, 2008 R2 SP1 or later */
support_wsa_no_inherit = (major >= 6 && minor >= 1);
}
#endif
Py_TYPE(&sock_type) = &PyType_Type;
m = PyModule_Create(&socketmodule);
if (m == NULL)

View file

@ -870,7 +870,7 @@ read_directory(PyObject *archive)
const char *charset;
int bootstrap;
fp = _Py_fopen(archive, "rb");
fp = _Py_fopen_obj(archive, "rb");
if (fp == NULL) {
if (!PyErr_Occurred())
PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
@ -1064,7 +1064,7 @@ get_data(PyObject *archive, PyObject *toc_entry)
return NULL;
}
fp = _Py_fopen(archive, "rb");
fp = _Py_fopen_obj(archive, "rb");
if (!fp) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_IOError,