diff --git a/CMakeLists.txt b/CMakeLists.txt index ed9ecfab..c42c3241 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,10 @@ if (POLLER STREQUAL "") set (CMAKE_REQUIRED_INCLUDES) if (HAVE_EPOLL) set (POLLER "epoll") + check_function_exists (epoll_create1 HAVE_EPOLL_CLOEXEC) + if (HAVE_EPOLL_CLOEXEC) + set (ZMQ_USE_EPOLL_CLOEXEC 1) + endif () endif () endif () @@ -179,6 +183,9 @@ check_include_files (ifaddrs.h ZMQ_HAVE_IFADDRS) check_include_files (windows.h ZMQ_HAVE_WINDOWS) check_include_files (sys/uio.h ZMQ_HAVE_UIO) check_include_files (sys/eventfd.h ZMQ_HAVE_EVENTFD) +if (ZMQ_HAVE_EVENTFD) + zmq_check_efd_cloexec () +endif () check_library_exists (ws2_32 fopen "" HAVE_WS2_32) # TODO: Why doesn't something logical like WSAStartup work? check_library_exists (ws2 fopen "" HAVE_WS2) diff --git a/acinclude.m4 b/acinclude.m4 index f6c8a48c..0eddc2b9 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -614,6 +614,29 @@ int main (int argc, char *argv []) AS_IF([test "x$libzmq_cv_sock_cloexec" = "xyes"], [$1], [$2]) }]) +dnl ################################################################################ +dnl # LIBZMQ_CHECK_EVENTFD_CLOEXEC([action-if-found], [action-if-not-found]) # +dnl # Check if EFD_CLOEXEC is supported # +dnl ################################################################################ +AC_DEFUN([LIBZMQ_CHECK_EVENTFD_CLOEXEC], [{ + AC_CACHE_CHECK([whether EFD_CLOEXEC is supported], [libzmq_cv_efd_cloexec], + [AC_TRY_RUN([/* EFD_CLOEXEC test */ +#include + +int main (int argc, char *argv []) +{ + int s = eventfd (0, EFD_CLOEXEC); + return (s == -1); +} + ], + [libzmq_cv_efd_cloexec="yes"], + [libzmq_cv_efd_cloexec="no"], + [libzmq_cv_efd_cloexec="not during cross-compile"] + )] + ) + AS_IF([test "x$libzmq_cv_efd_cloexec" = "xyes"], [$1], [$2]) +}]) + dnl ################################################################################ dnl # LIBZMQ_CHECK_ATOMIC_INSTRINSICS([action-if-found], [action-if-not-found]) # dnl # Check if compiler supoorts __atomic_Xxx intrinsics # @@ -801,6 +824,7 @@ kqueue(); dnl ################################################################################ dnl # LIBZMQ_CHECK_POLLER_EPOLL_RUN([action-if-found], [action-if-not-found]) # +dnl # LIBZMQ_CHECK_POLLER_EPOLL_CLOEXEC([action-if-found], [action-if-not-found]) # dnl # Checks epoll polling system can actually run # dnl # For cross-compile, only requires that epoll can link # dnl ################################################################################ @@ -828,6 +852,30 @@ epoll_create(10); ) }]) +AC_DEFUN([LIBZMQ_CHECK_POLLER_EPOLL_CLOEXEC], [{ + AC_RUN_IFELSE([ + AC_LANG_PROGRAM([ +#include + ],[[ +struct epoll_event t_ev; +int r; +r = epoll_create1(EPOLL_CLOEXEC); +return(r < 0); + ]])], + [$1],[$2],[ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +#include + ],[[ +struct epoll_event t_ev; +epoll_create1(EPOLL_CLOEXEC); + ]])], + [$1], [$2] + ) + ] + ) +}]) + dnl ################################################################################ dnl # LIBZMQ_CHECK_POLLER_DEVPOLL([action-if-found], [action-if-not-found]) # dnl # Checks devpoll polling system # @@ -938,10 +986,17 @@ AC_DEFUN([LIBZMQ_CHECK_POLLER], [{ ]) ;; epoll) - LIBZMQ_CHECK_POLLER_EPOLL([ - AC_MSG_NOTICE([Using 'epoll' polling system]) + LIBZMQ_CHECK_POLLER_EPOLL_CLOEXEC([ + AC_MSG_NOTICE([Using 'epoll' polling system with CLOEXEC]) AC_DEFINE(ZMQ_USE_EPOLL, 1, [Use 'epoll' polling system]) + AC_DEFINE(ZMQ_USE_EPOLL_CLOEXEC, 1, [Use 'epoll' polling system with CLOEXEC]) poller_found=1 + ],[ + LIBZMQ_CHECK_POLLER_EPOLL([ + AC_MSG_NOTICE([Using 'epoll' polling system with CLOEXEC]) + AC_DEFINE(ZMQ_USE_EPOLL, 1, [Use 'epoll' polling system]) + poller_found=1 + ]) ]) ;; devpoll) diff --git a/builds/cmake/Modules/ZMQSourceRunChecks.cmake b/builds/cmake/Modules/ZMQSourceRunChecks.cmake index 10d46d3d..c4644919 100644 --- a/builds/cmake/Modules/ZMQSourceRunChecks.cmake +++ b/builds/cmake/Modules/ZMQSourceRunChecks.cmake @@ -16,6 +16,21 @@ int main(int argc, char *argv []) ZMQ_HAVE_SOCK_CLOEXEC) endmacro() +macro(zmq_check_efd_cloexec) + message(STATUS "Checking whether EFD_CLOEXEC is supported") + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + int s = eventfd (0, EFD_CLOEXEC); + return(s == -1); +} +" + ZMQ_HAVE_EVENTFD_CLOEXEC) +endmacro() + # TCP keep-alives Checks. macro(zmq_check_so_keepalive) diff --git a/builds/cmake/platform.hpp.in b/builds/cmake/platform.hpp.in index 0235dc7e..14f1898e 100644 --- a/builds/cmake/platform.hpp.in +++ b/builds/cmake/platform.hpp.in @@ -3,6 +3,7 @@ #cmakedefine ZMQ_USE_KQUEUE #cmakedefine ZMQ_USE_EPOLL +#cmakedefine ZMQ_USE_EPOLL_CLOEXEC #cmakedefine ZMQ_USE_DEVPOLL #cmakedefine ZMQ_USE_POLL #cmakedefine ZMQ_USE_SELECT @@ -16,6 +17,7 @@ #cmakedefine ZMQ_HAVE_UIO #cmakedefine ZMQ_HAVE_EVENTFD +#cmakedefine ZMQ_HAVE_EVENTFD_CLOEXEC #cmakedefine ZMQ_HAVE_IFADDRS #cmakedefine ZMQ_HAVE_SO_PEERCRED diff --git a/configure.ac b/configure.ac index d548f9fa..a8c9c25d 100644 --- a/configure.ac +++ b/configure.ac @@ -358,8 +358,14 @@ AC_ARG_ENABLE([eventfd], if test "x$zmq_enable_eventfd" = "xyes"; then # Check if we have eventfd.h header file. - AC_CHECK_HEADERS(sys/eventfd.h, - [AC_DEFINE(ZMQ_HAVE_EVENTFD, 1, [Have eventfd extension])]) + AC_CHECK_HEADERS(sys/eventfd.h, [ + AC_DEFINE(ZMQ_HAVE_EVENTFD, 1, [Have eventfd extension]) + LIBZMQ_CHECK_EVENTFD_CLOEXEC([ + AC_DEFINE([ZMQ_HAVE_EVENTFD_CLOEXEC], + [1], + [Whether EFD_CLOEXEC is defined and functioning.]) + ]) + ]) fi # Conditionally build performance measurement tools diff --git a/src/epoll.cpp b/src/epoll.cpp index ab792164..be7ad61d 100644 --- a/src/epoll.cpp +++ b/src/epoll.cpp @@ -48,7 +48,14 @@ zmq::epoll_t::epoll_t (const zmq::ctx_t &ctx_) : ctx(ctx_), stopping (false) { +#ifdef ZMQ_USE_EPOLL_CLOEXEC + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports, avoid + // leaks, etc. + epoll_fd = epoll_create1 (EPOLL_CLOEXEC); +#else epoll_fd = epoll_create (1); +#endif errno_assert (epoll_fd != -1); } diff --git a/src/signaler.cpp b/src/signaler.cpp index 77c7ff54..5a0b8ddc 100644 --- a/src/signaler.cpp +++ b/src/signaler.cpp @@ -381,7 +381,14 @@ void zmq::signaler_t::forked () int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_) { #if defined ZMQ_HAVE_EVENTFD - fd_t fd = eventfd (0, 0); + int flags = 0; +#if defined ZMQ_HAVE_EVENTFD_CLOEXEC + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports, avoid + // leaks, etc. + flags |= EFD_CLOEXEC; +#endif + fd_t fd = eventfd (0, flags); if (fd == -1) { errno_assert (errno == ENFILE || errno == EMFILE); *w_ = *r_ = -1; @@ -637,13 +644,29 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_) #else // All other implementations support socketpair() int sv [2]; - int rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sv); + int type = SOCK_STREAM; + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports, avoid + // leaks, etc. +#if defined ZMQ_HAVE_SOCK_CLOEXEC + type |= SOCK_CLOEXEC; +#endif + int rc = socketpair (AF_UNIX, type, 0, sv); if (rc == -1) { errno_assert (errno == ENFILE || errno == EMFILE); *w_ = *r_ = -1; return -1; } else { + // If there's no SOCK_CLOEXEC, let's try the second best option. Note that + // race condition can cause socket not to be closed (if fork happens + // between socket creation and this point). +#if !defined ZMQ_HAVE_SOCK_CLOEXEC && defined FD_CLOEXEC + rc = fcntl (sv [0], F_SETFD, FD_CLOEXEC); + errno_assert (rc != -1); + rc = fcntl (sv [1], F_SETFD, FD_CLOEXEC); + errno_assert (rc != -1); +#endif *w_ = sv [0]; *r_ = sv [1]; return 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9fe5ded8..1e60ba10 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -84,6 +84,7 @@ if(NOT WIN32) test_router_mandatory_hwm test_use_fd_ipc test_use_fd_tcp + test_zmq_poll_fd ) if(HAVE_FORK) list(APPEND tests test_fork)