diff --git a/src/event-loop.c b/src/event-loop.c index 3bab3df5..ee2eae5d 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -34,6 +34,7 @@ #include #include #include "wayland-server.h" +#include "wayland-os.h" struct wl_event_loop { int epoll_fd; @@ -392,7 +393,7 @@ wl_event_loop_create(void) if (loop == NULL) return NULL; - loop->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + loop->epoll_fd = wl_os_epoll_create_cloexec(); if (loop->epoll_fd < 0) { free(loop); return NULL; diff --git a/src/wayland-os.c b/src/wayland-os.c index eb53eec4..a8756b6d 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "wayland-os.h" @@ -124,3 +125,20 @@ wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) return recvmsg_cloexec_fallback(sockfd, msg, flags); } + +int +wl_os_epoll_create_cloexec(void) +{ + int fd; + +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} diff --git a/src/wayland-os.h b/src/wayland-os.h index 43c317b9..f6827f1c 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -32,6 +32,9 @@ wl_os_dupfd_cloexec(int fd, long minfd); ssize_t wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); +int +wl_os_epoll_create_cloexec(void); + /* * The following are for wayland-os.c and the unit tests. diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 657f1feb..967eb83e 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "../src/wayland-private.h" #include "test-runner.h" @@ -50,6 +51,9 @@ static int wrapped_calls_fcntl; static ssize_t (*real_recvmsg)(int, struct msghdr *, int); static int wrapped_calls_recvmsg; +static int (*real_epoll_create1)(int); +static int wrapped_calls_epoll_create1; + static void init_fallbacks(int do_fallbacks) { @@ -57,6 +61,7 @@ init_fallbacks(int do_fallbacks) real_socket = dlsym(RTLD_NEXT, "socket"); real_fcntl = dlsym(RTLD_NEXT, "fcntl"); real_recvmsg = dlsym(RTLD_NEXT, "recvmsg"); + real_epoll_create1 = dlsym(RTLD_NEXT, "epoll_create1"); } __attribute__ ((visibility("default"))) int @@ -105,6 +110,20 @@ recvmsg(int sockfd, struct msghdr *msg, int flags) return real_recvmsg(sockfd, msg, flags); } +__attribute__ ((visibility("default"))) int +epoll_create1(int flags) +{ + wrapped_calls_epoll_create1++; + + if (fall_back) { + wrapped_calls_epoll_create1++; /* epoll_create() not wrapped */ + errno = EINVAL; + return -1; + } + + return real_epoll_create1(flags); +} + static void do_os_wrappers_socket_cloexec(int n) { @@ -331,3 +350,35 @@ TEST(os_wrappers_recvmsg_cloexec_fallback) init_fallbacks(1); do_os_wrappers_recvmsg_cloexec(1); } + +static void +do_os_wrappers_epoll_create_cloexec(int n) +{ + int fd; + int nr_fds; + + nr_fds = count_open_fds(); + + fd = wl_os_epoll_create_cloexec(); + assert(fd >= 0); + +#ifdef EPOLL_CLOEXEC + assert(wrapped_calls_epoll_create1 == n); +#else + printf("No epoll_create1.\n"); +#endif + + exec_fd_leak_check(nr_fds); +} + +TEST(os_wrappers_epoll_create_cloexec) +{ + init_fallbacks(0); + do_os_wrappers_epoll_create_cloexec(1); +} + +TEST(os_wrappers_epoll_create_cloexec_fallback) +{ + init_fallbacks(1); + do_os_wrappers_epoll_create_cloexec(2); +}