diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2013-08-27 22:53:59 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2013-08-27 22:53:59 (GMT) |
commit | daf455554bc21b6b5df0a016ab5fa639d36cc595 (patch) | |
tree | 216f52f9f6d9aed0406b2ce2574e5a02aa93e327 /Modules | |
parent | 46e1ce214b5711e8dae63a1b5a0a7aafb371baf0 (diff) | |
download | cpython-daf455554bc21b6b5df0a016ab5fa639d36cc595.zip cpython-daf455554bc21b6b5df0a016ab5fa639d36cc595.tar.gz cpython-daf455554bc21b6b5df0a016ab5fa639d36cc595.tar.bz2 |
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().
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_cursesmodule.c | 71 | ||||
-rw-r--r-- | Modules/_io/fileio.c | 32 | ||||
-rw-r--r-- | Modules/_posixsubprocess.c | 140 | ||||
-rw-r--r-- | Modules/_ssl.c | 2 | ||||
-rw-r--r-- | Modules/main.c | 2 | ||||
-rw-r--r-- | Modules/mmapmodule.c | 8 | ||||
-rw-r--r-- | Modules/ossaudiodev.c | 7 | ||||
-rw-r--r-- | Modules/posixmodule.c | 335 | ||||
-rw-r--r-- | Modules/selectmodule.c | 19 | ||||
-rw-r--r-- | Modules/socketmodule.c | 211 | ||||
-rw-r--r-- | Modules/zipimport.c | 4 |
11 files changed, 635 insertions, 196 deletions
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 27e5579..b5af867 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -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 * diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index e88ae87..e757c82 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -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; diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 8d70107..a2d7022 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -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) { - POSIX_CALL(dup2(p2cread, 0)); /* stdin */ + 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) { - POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ + 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 */ }; diff --git a/Modules/_ssl.c b/Modules/_ssl.c index a5086ca..48804ef 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -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); diff --git a/Modules/main.c b/Modules/main.c index 25190b8..9171070 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -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(); diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 4b4be61..366dac1 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -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; } } diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c index 5e45abd..b0a16db 100644 --- a/Modules/ossaudiodev.c +++ b/Modules/ossaudiodev.c @@ -115,7 +115,9 @@ newossobject(PyObject *arg) one open at a time. This does *not* affect later I/O; OSS provides a special ioctl() for non-blocking read/write, which is exposed via oss_nonblock() below. */ - if ((fd = open(devicename, imode|O_NONBLOCK)) == -1) { + fd = _Py_open(devicename, imode|O_NONBLOCK); + + if (fd == -1) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } @@ -177,7 +179,8 @@ newossmixerobject(PyObject *arg) devicename = "/dev/mixer"; } - if ((fd = open(devicename, O_RDWR)) == -1) { + fd = _Py_open(devicename, O_RDWR); + if (fd == -1) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index fde6752..6570142 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -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 */ }; diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 5da0d92..0ff09b4 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -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, diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c5efbc1..ca8dade 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -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) diff --git a/Modules/zipimport.c b/Modules/zipimport.c index 603efd6..633732f 100644 --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -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, |