Revamp the CLOEXEC support in Qt

The pipe2/dup3/accept4 functions and SOCK_CLOEXEC are quite old nowadays
on Linux. They were introduced on Linux 2.6.28 and glibc 2.10, all from
2008. They were also picked up by uClibc in 2011 and FreeBSD as of
version 10.0. So we no longer need the runtime detection of whether the
feature is available.

Instead, if the libc has support for it, use it unconditionally and fail
at runtime if the syscall isn't implemented.

Change-Id: Ib056b47dde3341ef9a52ffff13efcc39ef8dff7d
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
This commit is contained in:
Thiago Macieira 2015-07-10 22:09:15 -07:00
parent 61e621def3
commit 85ff351266
5 changed files with 87 additions and 53 deletions

View File

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2015 Intel Corporation.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the configuration module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int pipes[2];
(void) pipe2(pipes, O_CLOEXEC | O_NONBLOCK);
(void) fcntl(0, F_DUPFD_CLOEXEC, 0);
(void) dup3(0, 3, O_CLOEXEC);
(void) accept4(0, 0, 0, SOCK_CLOEXEC | SOCK_NONBLOCK);
return 0;
}

View File

@ -0,0 +1,3 @@
SOURCES = cloexec.cpp
CONFIG -= qt
QT =

10
configure vendored
View File

@ -728,6 +728,7 @@ CFG_IPV6IFNAME=auto
CFG_GETIFADDRS=auto CFG_GETIFADDRS=auto
CFG_INOTIFY=auto CFG_INOTIFY=auto
CFG_EVENTFD=auto CFG_EVENTFD=auto
CFG_CLOEXEC=no
CFG_RPATH=yes CFG_RPATH=yes
CFG_FRAMEWORK=auto CFG_FRAMEWORK=auto
CFG_USE_GOLD_LINKER=auto CFG_USE_GOLD_LINKER=auto
@ -5869,6 +5870,11 @@ if [ "$CFG_GETIFADDRS" != "no" ]; then
fi fi
fi fi
# find if the platform provides thread-safe CLOEXEC support
if compileTest unix/cloexec; then
CFG_CLOEXEC=yes
fi
# detect OpenSSL # detect OpenSSL
if [ "$CFG_OPENSSL" != "no" ]; then if [ "$CFG_OPENSSL" != "no" ]; then
if compileTest unix/openssl "OpenSSL"; then if compileTest unix/openssl "OpenSSL"; then
@ -6147,6 +6153,9 @@ fi
if [ "$CFG_EVENTFD" = "yes" ]; then if [ "$CFG_EVENTFD" = "yes" ]; then
QT_CONFIG="$QT_CONFIG eventfd" QT_CONFIG="$QT_CONFIG eventfd"
fi fi
if [ "$CFG_CLOEXEC" = "yes" ]; then
QT_CONFIG="$QT_CONFIG threadsafe-cloexec"
fi
if [ "$CFG_LIBJPEG" = "no" ]; then if [ "$CFG_LIBJPEG" = "no" ]; then
CFG_JPEG="no" CFG_JPEG="no"
elif [ "$CFG_LIBJPEG" = "system" ]; then elif [ "$CFG_LIBJPEG" = "system" ]; then
@ -6627,6 +6636,7 @@ QMakeVar set sql-plugins "$SQL_PLUGINS"
[ "$CFG_GETIFADDRS" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_GETIFADDRS" [ "$CFG_GETIFADDRS" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_GETIFADDRS"
[ "$CFG_INOTIFY" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_INOTIFY" [ "$CFG_INOTIFY" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_INOTIFY"
[ "$CFG_EVENTFD" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_EVENTFD" [ "$CFG_EVENTFD" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_EVENTFD"
[ "$CFG_CLOEXEC" = "yes" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_THREADSAFE_CLOEXEC=1"
[ "$CFG_NIS" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_NIS" [ "$CFG_NIS" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_NIS"
[ "$CFG_OPENSSL" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_OPENSSL" [ "$CFG_OPENSSL" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_OPENSSL"
[ "$CFG_OPENSSL" = "linked" ]&& QCONFIG_FLAGS="$QCONFIG_FLAGS QT_LINKED_OPENSSL" [ "$CFG_OPENSSL" = "linked" ]&& QCONFIG_FLAGS="$QCONFIG_FLAGS QT_LINKED_OPENSSL"

View File

@ -67,24 +67,6 @@
struct sockaddr; struct sockaddr;
#if defined(Q_OS_LINUX) && defined(O_CLOEXEC)
# define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 1
QT_BEGIN_NAMESPACE
namespace QtLibcSupplement {
inline int accept4(int, sockaddr *, QT_SOCKLEN_T *, int)
{ errno = ENOSYS; return -1; }
inline int dup3(int, int, int)
{ errno = ENOSYS; return -1; }
inline int pipe2(int [], int )
{ errno = ENOSYS; return -1; }
}
QT_END_NAMESPACE
using namespace QT_PREPEND_NAMESPACE(QtLibcSupplement);
#else
# define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 0
#endif
#define EINTR_LOOP(var, cmd) \ #define EINTR_LOOP(var, cmd) \
do { \ do { \
var = cmd; \ var = cmd; \
@ -181,16 +163,12 @@ static inline int qt_safe_pipe(int pipefd[2], int flags = 0)
{ {
Q_ASSERT((flags & ~O_NONBLOCK) == 0); Q_ASSERT((flags & ~O_NONBLOCK) == 0);
int ret; #ifdef QT_THREADSAFE_CLOEXEC
#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC)
// use pipe2 // use pipe2
flags |= O_CLOEXEC; flags |= O_CLOEXEC;
ret = ::pipe2(pipefd, flags); // pipe2 is Linux-specific and is documented not to return EINTR return ::pipe2(pipefd, flags); // pipe2 is documented not to return EINTR
if (ret == 0 || errno != ENOSYS) #else
return ret; int ret = ::pipe(pipefd);
#endif
ret = ::pipe(pipefd);
if (ret == -1) if (ret == -1)
return -1; return -1;
@ -204,6 +182,7 @@ static inline int qt_safe_pipe(int pipefd[2], int flags = 0)
} }
return 0; return 0;
#endif
} }
#endif // Q_OS_VXWORKS #endif // Q_OS_VXWORKS
@ -213,22 +192,19 @@ static inline int qt_safe_dup(int oldfd, int atleast = 0, int flags = FD_CLOEXEC
{ {
Q_ASSERT(flags == FD_CLOEXEC || flags == 0); Q_ASSERT(flags == FD_CLOEXEC || flags == 0);
int ret;
#ifdef F_DUPFD_CLOEXEC #ifdef F_DUPFD_CLOEXEC
// use this fcntl int cmd = F_DUPFD;
if (flags & FD_CLOEXEC) { if (flags & FD_CLOEXEC)
ret = ::fcntl(oldfd, F_DUPFD_CLOEXEC, atleast); cmd = F_DUPFD_CLOEXEC;
if (ret != -1 || errno != EINVAL) return ::fcntl(oldfd, cmd, atleast);
return ret; #else
}
#endif
// use F_DUPFD // use F_DUPFD
ret = ::fcntl(oldfd, F_DUPFD, atleast); int ret = ::fcntl(oldfd, F_DUPFD, atleast);
if (flags && ret != -1) if (flags && ret != -1)
::fcntl(ret, F_SETFD, flags); ::fcntl(ret, F_SETFD, flags);
return ret; return ret;
#endif
} }
// don't call dup2 // don't call dup2
@ -238,14 +214,11 @@ static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC)
Q_ASSERT(flags == FD_CLOEXEC || flags == 0); Q_ASSERT(flags == FD_CLOEXEC || flags == 0);
int ret; int ret;
#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) #ifdef QT_THREADSAFE_CLOEXEC
// use dup3 // use dup3
if (flags & FD_CLOEXEC) { EINTR_LOOP(ret, ::dup3(oldfd, newfd, flags ? O_CLOEXEC : 0));
EINTR_LOOP(ret, ::dup3(oldfd, newfd, O_CLOEXEC)); return ret;
if (ret == 0 || errno != ENOSYS) #else
return ret;
}
#endif
EINTR_LOOP(ret, ::dup2(oldfd, newfd)); EINTR_LOOP(ret, ::dup2(oldfd, newfd));
if (ret == -1) if (ret == -1)
return -1; return -1;
@ -253,6 +226,7 @@ static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC)
if (flags) if (flags)
::fcntl(newfd, F_SETFD, flags); ::fcntl(newfd, F_SETFD, flags);
return 0; return 0;
#endif
} }
static inline qint64 qt_safe_read(int fd, void *data, qint64 maxlen) static inline qint64 qt_safe_read(int fd, void *data, qint64 maxlen)

View File

@ -77,15 +77,13 @@ static inline int qt_safe_socket(int domain, int type, int protocol, int flags =
Q_ASSERT((flags & ~O_NONBLOCK) == 0); Q_ASSERT((flags & ~O_NONBLOCK) == 0);
int fd; int fd;
#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) #ifdef QT_THREADSAFE_CLOEXEC
int newtype = type | SOCK_CLOEXEC; int newtype = type | SOCK_CLOEXEC;
if (flags & O_NONBLOCK) if (flags & O_NONBLOCK)
newtype |= SOCK_NONBLOCK; newtype |= SOCK_NONBLOCK;
fd = ::socket(domain, newtype, protocol); fd = ::socket(domain, newtype, protocol);
if (fd != -1 || errno != EINVAL) return fd;
return fd; #else
#endif
fd = ::socket(domain, type, protocol); fd = ::socket(domain, type, protocol);
if (fd == -1) if (fd == -1)
return -1; return -1;
@ -97,6 +95,7 @@ static inline int qt_safe_socket(int domain, int type, int protocol, int flags =
::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK); ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK);
return fd; return fd;
#endif
} }
// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED // Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED
@ -105,16 +104,14 @@ static inline int qt_safe_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *add
Q_ASSERT((flags & ~O_NONBLOCK) == 0); Q_ASSERT((flags & ~O_NONBLOCK) == 0);
int fd; int fd;
#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) #ifdef QT_THREADSAFE_CLOEXEC
// use accept4 // use accept4
int sockflags = SOCK_CLOEXEC; int sockflags = SOCK_CLOEXEC;
if (flags & O_NONBLOCK) if (flags & O_NONBLOCK)
sockflags |= SOCK_NONBLOCK; sockflags |= SOCK_NONBLOCK;
fd = ::accept4(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen), sockflags); fd = ::accept4(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen), sockflags);
if (fd != -1 || !(errno == ENOSYS || errno == EINVAL)) return fd;
return fd; #else
#endif
fd = ::accept(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen)); fd = ::accept(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen));
if (fd == -1) if (fd == -1)
return -1; return -1;
@ -126,6 +123,7 @@ static inline int qt_safe_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *add
::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK); ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK);
return fd; return fd;
#endif
} }
// UnixWare 7 redefines listen -> _listen // UnixWare 7 redefines listen -> _listen