From 36f34cbb9ff29a8966dede4ab34b626df510a2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= Date: Mon, 11 Jul 2022 20:37:53 +0200 Subject: [PATCH] port to macOS --- external/tree-macros/CMakeLists.txt | 3 + src/CMakeLists.txt | 32 +++-- src/compat_itimerspec.c | 1 + src/compat_itimerspec.h | 14 +++ src/compat_kqueue1.c | 9 ++ src/compat_pipe2.c | 68 +++++++++++ src/compat_pipe2.h | 12 ++ src/compat_ppoll.c | 181 ++++++++++++++++++++++++++++ src/compat_ppoll.h | 14 +++ src/compat_sem.c | 1 + src/compat_sem.h | 18 +++ src/compat_sigops.c | 2 +- src/compat_socket.c | 60 +++++++++ src/compat_socket.h | 17 +++ src/compat_socketpair.c | 67 ++++++++++ src/compat_socketpair.h | 17 +++ src/epoll_shim_interpose.c | 2 + src/epollfd_ctx.c | 68 ++++++++++- src/signalfd_ctx.c | 9 +- src/wrap.c | 13 +- test/CMakeLists.txt | 26 ++-- test/epoll-test.c | 38 ++++-- test/pipe-test.c | 46 ++++++- test/real_close.c | 9 +- test/signalfd-test.c | 11 +- test/socketpair-test.c | 4 + test/tst-epoll.c | 4 + 27 files changed, 700 insertions(+), 46 deletions(-) create mode 100644 src/compat_itimerspec.c create mode 100644 src/compat_itimerspec.h create mode 100644 src/compat_pipe2.c create mode 100644 src/compat_pipe2.h create mode 100644 src/compat_ppoll.c create mode 100644 src/compat_ppoll.h create mode 100644 src/compat_sem.c create mode 100644 src/compat_sem.h create mode 100644 src/compat_socket.c create mode 100644 src/compat_socket.h create mode 100644 src/compat_socketpair.c create mode 100644 src/compat_socketpair.h diff --git a/external/tree-macros/CMakeLists.txt b/external/tree-macros/CMakeLists.txt index c390560..2893303 100644 --- a/external/tree-macros/CMakeLists.txt +++ b/external/tree-macros/CMakeLists.txt @@ -4,6 +4,9 @@ project(tree-macros LANGUAGES C) add_library(tree-macros INTERFACE) target_include_directories(tree-macros INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include") +if(APPLE) + target_compile_definitions(tree-macros INTERFACE __uintptr_t=uintptr_t) +endif() # diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb15808..1b2fb20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,9 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) +find_package(tree-macros REQUIRED) +find_package(queue-macros REQUIRED) + add_library(rwlock OBJECT rwlock.c) set_property(TARGET rwlock PROPERTY POSITION_INDEPENDENT_CODE ON) target_link_libraries(rwlock PUBLIC Threads::Threads) @@ -18,11 +21,15 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") return() endif() -find_package(tree-macros REQUIRED) -find_package(queue-macros REQUIRED) +add_library(wrap OBJECT wrap.c) +set_property(TARGET wrap PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_libraries(wrap PUBLIC Threads::Threads) +target_include_directories(wrap + PUBLIC $) macro(add_compat_target _name _condition) add_library(compat_${_name} OBJECT compat_${_name}.c) + target_link_libraries(compat_${_name} PUBLIC wrap) set_property(TARGET compat_${_name} PROPERTY POSITION_INDEPENDENT_CODE ON) target_compile_options( compat_${_name} @@ -39,12 +46,13 @@ macro(add_compat_target _name _condition) endmacro() include(CheckSymbolExists) + # FreeBSD 13 and NetBSD 10 support native eventfd descriptors. NetBSD 10 # supports native timerfd descriptors. Prefer them if available. check_symbol_exists(eventfd "sys/eventfd.h" HAVE_EVENTFD) check_symbol_exists(timerfd_create "sys/timerfd.h" HAVE_TIMERFD) -check_symbol_exists(kqueue1 "sys/event.h" HAVE_KQUEUE1) +check_symbol_exists(kqueue1 "sys/event.h;sys/time.h" HAVE_KQUEUE1) add_compat_target(kqueue1 "NOT;HAVE_KQUEUE1") check_symbol_exists(sigandset "signal.h" HAVE_SIGANDSET) check_symbol_exists(sigorset "signal.h" HAVE_SIGORSET) @@ -71,6 +79,14 @@ if(NOT ALLOWS_ONESHOT_TIMERS_WITH_TIMEOUT_ZERO) evfilt_timer_quirks INTERFACE QUIRK_EVFILT_TIMER_DISALLOWS_ONESHOT_TIMEOUT_ZERO) endif() +add_compat_target(pipe2 "APPLE") +add_compat_target(socket "APPLE") +add_compat_target(socketpair "APPLE") +add_compat_target(itimerspec "APPLE") +add_compat_target(sem "APPLE") +add_compat_target(ppoll "APPLE") + +target_link_libraries(rwlock PUBLIC $) add_library( epoll-shim @@ -80,8 +96,7 @@ add_library( kqueue_event.c signalfd.c signalfd_ctx.c - timespec_util.c - wrap.c) + timespec_util.c) if(NOT HAVE_EVENTFD) target_sources(epoll-shim PRIVATE eventfd.c eventfd_ctx.c) endif() @@ -94,11 +109,14 @@ target_link_libraries( epoll-shim PRIVATE Threads::Threads # $ - $ + $ # $ $ + $ + $ $ - $) + $ + $) if(HAVE_TIMERFD) target_compile_definitions(epoll-shim PRIVATE HAVE_TIMERFD) endif() diff --git a/src/compat_itimerspec.c b/src/compat_itimerspec.c new file mode 100644 index 0000000..022e55e --- /dev/null +++ b/src/compat_itimerspec.c @@ -0,0 +1 @@ +#include "compat_itimerspec.h" diff --git a/src/compat_itimerspec.h b/src/compat_itimerspec.h new file mode 100644 index 0000000..67662bd --- /dev/null +++ b/src/compat_itimerspec.h @@ -0,0 +1,14 @@ +#ifndef COMPAT_ITIMERSPEC_H +#define COMPAT_ITIMERSPEC_H + +#ifdef COMPAT_ENABLE_ITIMERSPEC + +#include + +struct itimerspec { + struct timespec it_interval; + struct timespec it_value; +}; +#endif + +#endif diff --git a/src/compat_kqueue1.c b/src/compat_kqueue1.c index 6d44e47..40788f6 100644 --- a/src/compat_kqueue1.c +++ b/src/compat_kqueue1.c @@ -38,6 +38,15 @@ compat_kqueue1_impl(int *fd_out, int flags) goto out; } } +#ifdef __APPLE__ + else { + if ((r = fcntl(fd, F_GETFD, 0)) < 0 || + fcntl(fd, F_SETFD, r & ~FD_CLOEXEC) < 0) { + ec = errno; + goto out; + } + } +#endif if (flags & O_NONBLOCK) { if ((r = real_fcntl(fd, F_GETFL)) < 0) { diff --git a/src/compat_pipe2.c b/src/compat_pipe2.c new file mode 100644 index 0000000..adb56a2 --- /dev/null +++ b/src/compat_pipe2.c @@ -0,0 +1,68 @@ +#include "compat_pipe2.h" + +#include +#include + +#include + +#include "wrap.h" + +static errno_t +compat_pipe2_impl(int pipefd[2], int flags) +{ + errno_t ec; + + if (flags & ~(O_CLOEXEC | O_NONBLOCK)) { + return EINVAL; + } + + int p[2]; + if (pipe(p) < 0) { + return errno; + } + + { + int r; + + if (flags & O_NONBLOCK) { + if ((r = fcntl(p[0], F_GETFL, 0)) < 0 || + fcntl(p[0], F_SETFL, r | O_NONBLOCK) < 0 || + (r = fcntl(p[1], F_GETFL, 0)) < 0 || + fcntl(p[1], F_SETFL, r | O_NONBLOCK) < 0) { + ec = errno; + goto out; + } + } + + if (flags & O_CLOEXEC) { + if ((r = fcntl(p[0], F_GETFD, 0)) < 0 || + fcntl(p[0], F_SETFD, r | FD_CLOEXEC) < 0 || + (r = fcntl(p[1], F_GETFD, 0)) < 0 || + fcntl(p[1], F_SETFD, r | FD_CLOEXEC) < 0) { + ec = errno; + goto out; + } + } + } + + pipefd[0] = p[0]; + pipefd[1] = p[1]; + + return 0; + +out: + (void)real_close(p[0]); + (void)real_close(p[1]); + return ec; +} + +int +compat_pipe2(int pipefd[2], int flags) +{ + errno_t ec = compat_pipe2_impl(pipefd, flags); + if (ec != 0) { + errno = ec; + return -1; + } + return 0; +} diff --git a/src/compat_pipe2.h b/src/compat_pipe2.h new file mode 100644 index 0000000..33599bb --- /dev/null +++ b/src/compat_pipe2.h @@ -0,0 +1,12 @@ +#ifndef COMPAT_PIPE2_H +#define COMPAT_PIPE2_H + +#include +#include + +int compat_pipe2(int pipefd[2], int flags); +#ifdef COMPAT_ENABLE_PIPE2 +#define pipe2 compat_pipe2 +#endif + +#endif diff --git a/src/compat_ppoll.c b/src/compat_ppoll.c new file mode 100644 index 0000000..44a579d --- /dev/null +++ b/src/compat_ppoll.c @@ -0,0 +1,181 @@ +#include "compat_ppoll.h" + +#include + +#include + +#include +#include +#include + +#include + +#include "wrap.h" + +static errno_t +compat_ppoll_impl(struct pollfd fds[], nfds_t nfds, + struct timespec const *restrict timeout, sigset_t const *restrict sigmask, + int *n) +{ + errno_t ec; + + int timeout_ms = -1; + if (timeout != NULL) { + if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 || + timeout->tv_nsec >= 1000000000) { + return EINVAL; + } + + timeout_ms = timeout->tv_nsec / 1000000; + if (timeout->tv_nsec % 1000000) { + ++timeout_ms; + } + + int sec_ms; + if (__builtin_mul_overflow(timeout->tv_sec, 1000, &sec_ms) || + __builtin_add_overflow(sec_ms, timeout_ms, &timeout_ms)) { + timeout_ms = -1; + } + } + + struct kevent kevs[NSIG]; + int kevs_length = 0; + + sigset_t origmask; + + if (timeout_ms != 0 && sigmask != NULL) { + if ((ec = pthread_sigmask(SIG_SETMASK, /**/ + NULL, &origmask)) != 0) { + return ec; + } + + for (int i = 1; i < NSIG; ++i) { + if (i == SIGKILL || i == SIGSTOP) { + continue; + } + + if (!sigismember(&origmask, i) || + sigismember(sigmask, i)) { + continue; + } + + struct sigaction act; + if (sigaction(i, NULL, &act) < 0) { + return errno; + } + + if (!(act.sa_flags & SA_SIGINFO) && + (act.sa_handler == SIG_DFL || + act.sa_handler == SIG_IGN)) { + continue; + } + + if (act.sa_flags & SA_RESTART) { + continue; + } + + EV_SET(&kevs[kevs_length++], i, EVFILT_SIGNAL, + EV_ADD | EV_ONESHOT, 0, 0, 0); + } + } + + int kq = -1; + struct pollfd *fds2 = NULL; + + if (kevs_length > 0) { + kq = kqueue(); + if (kq < 0) { + return errno; + } + + if (kevent(kq, kevs, kevs_length, NULL, 0, NULL) < 0) { + ec = errno; + goto out; + } + +#ifdef EVFILT_USER + sigset_t pending; + if (sigpending(&pending) < 0) { + ec = errno; + goto out; + } + for (int i = 0; i < kevs_length; ++i) { + if (sigismember(&pending, (int)kevs[i].ident)) { + EV_SET(&kevs[0], 0, EVFILT_USER, /**/ + EV_ADD | EV_ONESHOT, 0, 0, 0); + EV_SET(&kevs[1], 0, EVFILT_USER, /**/ + 0, NOTE_TRIGGER, 0, 0); + if (kevent(kq, kevs, 2, NULL, 0, NULL) < 0) { + ec = errno; + goto out; + } + break; + } + } +#endif + + size_t pfds2_bytes; + if (__builtin_add_overflow(nfds, 1, &pfds2_bytes) || + __builtin_mul_overflow(pfds2_bytes, sizeof(struct pollfd), + &pfds2_bytes)) { + ec = EINVAL; + goto out; + } + + fds2 = malloc(pfds2_bytes); + if (!fds2) { + ec = errno; + goto out; + } + memcpy(fds2, fds, nfds * sizeof(struct pollfd)); + fds2[nfds] = (struct pollfd) { + .fd = kq, + .events = POLLIN, + }; + } + + if (sigmask != NULL) { + (void)pthread_sigmask(SIG_SETMASK, sigmask, &origmask); + } + + int ready = real_poll(fds2 ? fds2 : fds, nfds + !!(fds2), timeout_ms); + ec = ready < 0 ? errno : 0; + + if (sigmask != NULL) { + (void)pthread_sigmask(SIG_SETMASK, &origmask, NULL); + } + + if (ec == 0 && fds2 && fds2[nfds].revents) { + --ready; + if (ready == 0) { + ec = EINTR; + } + } + + if (ec == 0) { + *n = ready; + if (fds2) { + memcpy(fds, fds2, nfds * sizeof(struct pollfd)); + } + } + +out: + free(fds2); + if (kq >= 0) { + real_close(kq); + } + return ec; +} + +int +compat_ppoll(struct pollfd fds[], nfds_t nfds, + struct timespec const *restrict timeout, sigset_t const *restrict sigmask) +{ + int n; + errno_t ec = compat_ppoll_impl(fds, nfds, timeout, sigmask, &n); + if (ec != 0) { + errno = ec; + return -1; + } + return n; +} diff --git a/src/compat_ppoll.h b/src/compat_ppoll.h new file mode 100644 index 0000000..8a8d173 --- /dev/null +++ b/src/compat_ppoll.h @@ -0,0 +1,14 @@ +#ifndef COMPAT_PPOLL_H +#define COMPAT_PPOLL_H + +#include +#include +#include + +int compat_ppoll(struct pollfd fds[], nfds_t nfds, + struct timespec const *restrict timeout, sigset_t const *restrict sigmask); +#ifdef COMPAT_ENABLE_PPOLL +#define ppoll compat_ppoll +#endif + +#endif diff --git a/src/compat_sem.c b/src/compat_sem.c new file mode 100644 index 0000000..9e72271 --- /dev/null +++ b/src/compat_sem.c @@ -0,0 +1 @@ +#include "compat_sem.h" diff --git a/src/compat_sem.h b/src/compat_sem.h new file mode 100644 index 0000000..f32503d --- /dev/null +++ b/src/compat_sem.h @@ -0,0 +1,18 @@ +#ifndef COMPAT_SEM_H +#define COMPAT_SEM_H + +#ifdef COMPAT_ENABLE_SEM +#include + +#include + +#define sem_t dispatch_semaphore_t + +#define sem_init(sem, pshared, value) \ + (*(sem) = dispatch_semaphore_create(value)) +#define sem_post(sem) dispatch_semaphore_signal(*(sem)) +#define sem_wait(sem) dispatch_semaphore_wait(*(sem), DISPATCH_TIME_FOREVER) +#define sem_destroy(sem) dispatch_release(*(sem)) +#endif + +#endif diff --git a/src/compat_sigops.c b/src/compat_sigops.c index 1b2516d..a846628 100644 --- a/src/compat_sigops.c +++ b/src/compat_sigops.c @@ -2,7 +2,7 @@ #include -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) || defined(__APPLE__) int compat_sigisemptyset(sigset_t const *set) { diff --git a/src/compat_socket.c b/src/compat_socket.c new file mode 100644 index 0000000..8f6a218 --- /dev/null +++ b/src/compat_socket.c @@ -0,0 +1,60 @@ +#include "compat_socket.h" + +#include +#include + +#include +#include + +#include "wrap.h" + +static errno_t +compat_socket_impl(int domain, int type, int protocol, int *s) +{ + errno_t ec; + + int sock = socket(domain, + type & ~(COMPAT_SOCK_CLOEXEC | COMPAT_SOCK_NONBLOCK), protocol); + if (sock < 0) { + return errno; + } + + { + int r; + + if (type & COMPAT_SOCK_NONBLOCK) { + if ((r = fcntl(sock, F_GETFL, 0)) < 0 || + fcntl(sock, F_SETFL, r | O_NONBLOCK) < 0) { + ec = errno; + goto out; + } + } + + if (type & COMPAT_SOCK_CLOEXEC) { + if ((r = fcntl(sock, F_GETFD, 0)) < 0 || + fcntl(sock, F_SETFD, r | FD_CLOEXEC) < 0) { + ec = errno; + goto out; + } + } + } + + *s = sock; + return 0; + +out: + (void)real_close(sock); + return ec; +} + +int +compat_socket(int domain, int type, int protocol) +{ + int s; + errno_t ec = compat_socket_impl(domain, type, protocol, &s); + if (ec != 0) { + errno = ec; + return -1; + } + return s; +} diff --git a/src/compat_socket.h b/src/compat_socket.h new file mode 100644 index 0000000..74a732b --- /dev/null +++ b/src/compat_socket.h @@ -0,0 +1,17 @@ +#ifndef COMPAT_SOCKET_H +#define COMPAT_SOCKET_H + +#include + +#include + +#define COMPAT_SOCK_NONBLOCK (O_NONBLOCK) +#define COMPAT_SOCK_CLOEXEC (O_CLOEXEC) +int compat_socket(int domain, int type, int protocol); +#ifdef COMPAT_ENABLE_SOCKET +#define socket compat_socket +#define SOCK_NONBLOCK COMPAT_SOCK_NONBLOCK +#define SOCK_CLOEXEC COMPAT_SOCK_CLOEXEC +#endif + +#endif diff --git a/src/compat_socketpair.c b/src/compat_socketpair.c new file mode 100644 index 0000000..de8190e --- /dev/null +++ b/src/compat_socketpair.c @@ -0,0 +1,67 @@ +#include "compat_socketpair.h" + +#include +#include + +#include +#include + +#include "wrap.h" + +static errno_t +compat_socketpair_impl(int domain, int type, int protocol, int sv[2]) +{ + errno_t ec; + + int p[2]; + if (socketpair(domain, + type & ~(COMPAT_SOCK_CLOEXEC | COMPAT_SOCK_NONBLOCK), protocol, + p) < 0) { + return errno; + } + + { + int r; + + if (type & COMPAT_SOCK_NONBLOCK) { + if ((r = fcntl(p[0], F_GETFL, 0)) < 0 || + fcntl(p[0], F_SETFL, r | O_NONBLOCK) < 0 || + (r = fcntl(p[1], F_GETFL, 0)) < 0 || + fcntl(p[1], F_SETFL, r | O_NONBLOCK) < 0) { + ec = errno; + goto out; + } + } + + if (type & COMPAT_SOCK_CLOEXEC) { + if ((r = fcntl(p[0], F_GETFD, 0)) < 0 || + fcntl(p[0], F_SETFD, r | FD_CLOEXEC) < 0 || + (r = fcntl(p[1], F_GETFD, 0)) < 0 || + fcntl(p[1], F_SETFD, r | FD_CLOEXEC) < 0) { + ec = errno; + goto out; + } + } + } + + sv[0] = p[0]; + sv[1] = p[1]; + + return 0; + +out: + (void)real_close(p[0]); + (void)real_close(p[1]); + return ec; +} + +int +compat_socketpair(int domain, int type, int protocol, int sv[2]) +{ + errno_t ec = compat_socketpair_impl(domain, type, protocol, sv); + if (ec != 0) { + errno = ec; + return -1; + } + return 0; +} diff --git a/src/compat_socketpair.h b/src/compat_socketpair.h new file mode 100644 index 0000000..a39f5b4 --- /dev/null +++ b/src/compat_socketpair.h @@ -0,0 +1,17 @@ +#ifndef COMPAT_SOCKETPAIR_H +#define COMPAT_SOCKETPAIR_H + +#include + +#include + +#define COMPAT_SOCK_NONBLOCK (O_NONBLOCK) +#define COMPAT_SOCK_CLOEXEC (O_CLOEXEC) +int compat_socketpair(int domain, int type, int protocol, int sv[2]); +#ifdef COMPAT_ENABLE_SOCKETPAIR +#define socketpair compat_socketpair +#define SOCK_NONBLOCK COMPAT_SOCK_NONBLOCK +#define SOCK_CLOEXEC COMPAT_SOCK_CLOEXEC +#endif + +#endif diff --git a/src/epoll_shim_interpose.c b/src/epoll_shim_interpose.c index 76bcac2..ba1a49e 100644 --- a/src/epoll_shim_interpose.c +++ b/src/epoll_shim_interpose.c @@ -43,6 +43,7 @@ poll(struct pollfd fds[], nfds_t nfds, int timeout) return epoll_shim_poll(fds, nfds, timeout); } +#if !defined(__APPLE__) EPOLL_SHIM_INTERPOSE_EXPORT int #ifdef __NetBSD__ @@ -55,6 +56,7 @@ ppoll { return epoll_shim_ppoll(fds, nfds, timeout, newsigmask); } +#endif EPOLL_SHIM_INTERPOSE_EXPORT int diff --git a/src/epollfd_ctx.c b/src/epollfd_ctx.c index b6528ae..f0c6ecc 100644 --- a/src/epollfd_ctx.c +++ b/src/epollfd_ctx.c @@ -374,9 +374,61 @@ registered_fds_node_feed_event(RegisteredFDsNode *fd2_node, int kq, } #ifdef EVFILT_EXCEPT else if (kev->filter == EVFILT_EXCEPT) { - assert((kev->fflags & NOTE_OOB) != 0); +#ifdef __APPLE__ + if ((kev->fflags & NOTE_OOB) != 0) { + /* + * macOS might still report NOTE_OOB + * even if there is no longer OOB data pending. + * Therefore we must recheck and reset + * the EVFILT_EXCEPT filter if needed. + */ + + int tmp_kq = kqueue(); + if (tmp_kq >= 0) { + struct kevent kev; + EV_SET(&kev, fd2_node->fd, EVFILT_EXCEPT, + EV_ADD | EV_ONESHOT, NOTE_OOB, 0, fd2_node); + + bool need_reset = false; + + if (kevent(tmp_kq, &kev, 1, NULL, 0, NULL) == + 0 && + kevent(tmp_kq, NULL, 0, &kev, 1, + &(struct timespec) { 0, 0 }) == 1 && + (kev.fflags & NOTE_OOB)) { + revents |= EPOLLPRI; + + NeededFilters needed_filters = + get_needed_filters(fd2_node); + if (needed_filters.evfilt_except && + !(needed_filters.evfilt_except & + EV_CLEAR)) { + need_reset = true; + } + } else { + need_reset = true; + } + real_close(tmp_kq); + + if (need_reset) { + EV_SET(&kev, fd2_node->fd, + EVFILT_EXCEPT, EV_DELETE, 0, 0, 0); + (void)kevent(kq, &kev, 1, NULL, 0, + NULL); + EV_SET(&kev, fd2_node->fd, + EVFILT_EXCEPT, EV_ADD | EV_CLEAR, + NOTE_OOB, 0, fd2_node); + (void)kevent(kq, &kev, 1, NULL, 0, + NULL); + } + } + } +#else + assert((kev->fflags & NOTE_OOB) != 0); revents |= EPOLLPRI; +#endif + goto out; } #endif @@ -877,7 +929,19 @@ epollfd_ctx__register_events(EpollFDCtx *epollfd, int kq, #ifdef EVFILT_EXCEPT fd2_node->has_evfilt_except = true; EV_SET(&kev[n++], (unsigned int)fd2, EVFILT_EXCEPT, - EV_ADD | (needed_filters.evfilt_except & EV_CLEAR), + EV_ADD | +#ifdef __APPLE__ + /* + * On macOS EVFILT_EXCEPT also triggers on + * normal data, so we must set the filter to + * edge triggered in all cases. Otherwise we + * will get swamped by events. + */ + EV_CLEAR +#else + (needed_filters.evfilt_except & EV_CLEAR) +#endif + , NOTE_OOB, 0, fd2_node); #else assert(0); diff --git a/src/signalfd_ctx.c b/src/signalfd_ctx.c index f9635a8..f302d9d 100644 --- a/src/signalfd_ctx.c +++ b/src/signalfd_ctx.c @@ -162,7 +162,7 @@ signalfd_ctx_read_impl(SignalFDCtx *signalfd, siginfo_t siginfo; memset(&siginfo, 0, sizeof(siginfo)); -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) || defined(__APPLE__) for (;;) { bool has_pending; sigset_t pending_sigs; @@ -174,6 +174,7 @@ signalfd_ctx_read_impl(SignalFDCtx *signalfd, return EAGAIN; } +#if defined(__OpenBSD__) /* * sigwait does not behave nicely when multiple signals * are pending (as of OpenBSD 6.8). So, only try to @@ -196,6 +197,12 @@ signalfd_ctx_read_impl(SignalFDCtx *signalfd, * one signal. There may be others pending. */ continue; } +#elif defined(__APPLE__) + int s; + ec = sigwait(&signalfd->sigs, &s); +#else +#error "" +#endif if (ec != 0) { break; } diff --git a/src/wrap.c b/src/wrap.c index b7653e5..f9c9d9a 100644 --- a/src/wrap.c +++ b/src/wrap.c @@ -1,7 +1,9 @@ #include "wrap.h" +#include #include #include +#include #include #include @@ -9,8 +11,7 @@ #include #include -#include "epoll_shim_ctx.h" -#include "epoll_shim_export.h" +#include "compat_ppoll.h" static struct { pthread_once_t wrap_init; @@ -19,10 +20,12 @@ static struct { typeof(write) *real_write; typeof(close) *real_close; typeof(poll) *real_poll; +#if !defined(__APPLE__) #ifdef __NetBSD__ typeof(pollts) *real___pollts50; #else typeof(ppoll) *real_ppoll; +#endif #endif typeof(fcntl) *real_fcntl; } wrap = { .wrap_init = PTHREAD_ONCE_INIT }; @@ -55,10 +58,12 @@ wrap_initialize_impl(void) WRAP(write); WRAP(close); WRAP(poll); +#if !defined(__APPLE__) #ifdef __NetBSD__ WRAP(__pollts50); #else WRAP(ppoll); +#endif #endif WRAP(fcntl); @@ -106,12 +111,16 @@ real_ppoll(struct pollfd fds[], nfds_t nfds, struct timespec const *restrict timeout, sigset_t const *restrict newsigmask) { +#ifdef __APPLE__ + return compat_ppoll(fds, nfds, timeout, newsigmask); +#else wrap_initialize(); #ifdef __NetBSD__ return wrap.real___pollts50(fds, nfds, timeout, newsigmask); #else return wrap.real_ppoll(fds, nfds, timeout, newsigmask); #endif +#endif } int diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 98d5364..1685d76 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,19 +17,25 @@ find_package(Threads REQUIRED) # -macro(atf_test _testname) - add_executable("${_testname}" "${_testname}.c") +macro(atf_test_impl _testname _suffix) + add_executable("${_testname}${_suffix}" "${_testname}.c") target_link_libraries( - "${_testname}" PRIVATE epoll-shim::epoll-shim Threads::Threads - microatf::microatf-c) - atf_discover_tests("${_testname}" ${ARGN}) + "${_testname}${_suffix}" PRIVATE epoll-shim::epoll-shim${_suffix} + Threads::Threads microatf::microatf-c) + if(APPLE) + target_link_libraries( + "${_testname}${_suffix}" + PRIVATE wrap compat_enable_pipe2 compat_enable_ppoll + compat_enable_socketpair compat_enable_socket + compat_enable_itimerspec) + endif() + atf_discover_tests("${_testname}${_suffix}" ${ARGN}) +endmacro() +macro(atf_test _testname) + atf_test_impl("${_testname}" "" ${ARGN}) if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") - add_executable("${_testname}-interpose" "${_testname}.c") - target_link_libraries( - "${_testname}-interpose" PRIVATE epoll-shim::epoll-shim-interpose - Threads::Threads microatf::microatf-c) - atf_discover_tests("${_testname}-interpose" ${ARGN}) + atf_test_impl("${_testname}" "-interpose" ${ARGN}) endif() endmacro() diff --git a/test/epoll-test.c b/test/epoll-test.c index fc7b86f..54cd3d6 100644 --- a/test/epoll-test.c +++ b/test/epoll-test.c @@ -31,6 +31,10 @@ #include #include +#ifndef __linux__ +#include +#endif + #include "atf-c-leakcheck.h" static void @@ -122,7 +126,11 @@ fd_tcp_socket(int fds[3]) ATF_REQUIRE( pthread_create(&client_thread, NULL, connector_client, NULL) == 0); +#ifdef __APPLE__ + int conn = accept(sock, NULL, NULL); +#else int conn = accept4(sock, NULL, NULL, SOCK_CLOEXEC); +#endif ATF_REQUIRE(conn >= 0); void *client_socket = NULL; @@ -139,7 +147,8 @@ ATF_TC_BODY_FD_LEAKCHECK(epoll__simple, tc) { int fd; - ATF_REQUIRE((fd = epoll_create1(EPOLL_CLOEXEC)) >= 0); + ATF_REQUIRE_MSG((fd = epoll_create1(EPOLL_CLOEXEC)) >= 0, + "errno: %d/%s", errno, strerror(errno)); ATF_REQUIRE(close(fd) == 0); ATF_REQUIRE_ERRNO(EINVAL, epoll_create(0) < 0); @@ -610,6 +619,8 @@ ATF_TC_BODY_FD_LEAKCHECK(epoll__poll_only_fd, tc) { #ifdef __linux__ atf_tc_skip("Test hangs on Linux"); +#elif defined(__APPLE__) + atf_tc_skip("/dev/random not pollable under macOS"); #endif int ep = epoll_create1(EPOLL_CLOEXEC); @@ -937,10 +948,10 @@ signalfd_in_thread_test(int which) { struct pollfd pfd = { .fd = ep, .events = POLLIN }; -#if defined(__DragonFly__) +#if defined(__DragonFly__) || defined(__APPLE__) ATF_REQUIRE(poll(&pfd, 1, 0) == 0); atf_tc_skip("signals sent to threads won't trigger " - "EVFILT_SIGNAL on DragonFly"); + "EVFILT_SIGNAL on DragonFly/macOS"); #endif ATF_REQUIRE(poll(&pfd, 1, -1) == 1); ATF_REQUIRE(pfd.revents == POLLIN); @@ -1981,6 +1992,11 @@ ATF_TC_BODY_FD_LEAKCHECK(epoll__epoll_pwait, tcptr) &emptyset); ATF_REQUIRE(n == 0 || (n < 0 && errno == EINTR)); ATF_REQUIRE(epoll_pwait_got_signal == 1); + +#ifdef __APPLE__ + n = ppoll(&pfd, 1, &(struct timespec) { 0, 500000000 }, NULL); + ATF_REQUIRE(n == 0); +#endif } ATF_REQUIRE(close(ep) == 0); @@ -1992,14 +2008,14 @@ ATF_TC_BODY_FD_LEAKCHECK(epoll__cloexec, tcptr) int fd; int r; -#define CLOEXEC_TEST(fun, cmp, ...) \ - do { \ - fd = fun(__VA_ARGS__); \ - ATF_REQUIRE(fd >= 0); \ - r = fcntl(fd, F_GETFD); \ - ATF_REQUIRE(r >= 0); \ - ATF_REQUIRE((r & FD_CLOEXEC) cmp 0); \ - ATF_REQUIRE(close(fd) == 0); \ +#define CLOEXEC_TEST(fun, cmp, ...) \ + do { \ + fd = fun(__VA_ARGS__); \ + ATF_REQUIRE(fd >= 0); \ + r = fcntl(fd, F_GETFD); \ + ATF_REQUIRE(r >= 0); \ + ATF_REQUIRE_MSG((r & FD_CLOEXEC) cmp 0, "%d", r); \ + ATF_REQUIRE(close(fd) == 0); \ } while (0) CLOEXEC_TEST(epoll_create1, !=, EPOLL_CLOEXEC); diff --git a/test/pipe-test.c b/test/pipe-test.c index 4721558..69b99cd 100644 --- a/test/pipe-test.c +++ b/test/pipe-test.c @@ -214,7 +214,7 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__poll_full_write_end_after_read_end_close_hup, tc) { struct pollfd pfd = { .fd = p[1] }; int rv = poll(&pfd, 1, 1000); -#if defined(__DragonFly__) +#if defined(__DragonFly__) || defined(__APPLE__) if (rv == 0) { atf_tc_skip("polling of pipes broken"); } @@ -437,7 +437,8 @@ print_statbuf(struct stat *sb) } static int const SPURIOUS_EV_ADD = 0 -#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || \ + defined(__APPLE__) | EV_ADD #endif ; @@ -664,6 +665,7 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_writes, tc) ATF_REQUIRE_MSG(kev[0].data == 16384 || kev[0].data == 4096 /* On OpenBSD < 7.0 */ || kev[0].data == 8192 /* On OpenBSD 7.0 */ || + kev[0].data == 8192 /* On macOS */ || kev[0].data == 65536 /* On DragonFly */, "%d", (int)kev[0].data); ATF_REQUIRE(kev[0].udata == 0); @@ -689,7 +691,7 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_writes, tc) #if !defined(__linux__) && !defined(FORCE_EPOLL) r = kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 }); -#if defined(__DragonFly__) || defined(__NetBSD__) +#if defined(__DragonFly__) || defined(__NetBSD__) || defined(__APPLE__) ATF_REQUIRE(r == 1); ATF_REQUIRE_MSG(kev[0].filter == EVFILT_READ, "%d", kev[0].filter); #else @@ -702,12 +704,17 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_writes, tc) ATF_REQUIRE(read(p[0], &c, 1) == 1); } -#if defined(__DragonFly__) +#if defined(__DragonFly__) || defined(__APPLE__) try_again: #endif #if !defined(__linux__) && !defined(FORCE_EPOLL) r = kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 }); +#if defined(__APPLE__) + if (r == 2) { + atf_tc_skip("Something is broken here."); + } +#endif #if defined(__DragonFly__) while (r == 1) { if (kev[0].filter == EVFILT_READ) { @@ -905,7 +912,7 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_connecting_reader, tc) ATF_REQUIRE(close(p[0]) == 0); #if !defined(__linux__) && !defined(FORCE_EPOLL) -#if defined(__DragonFly__) || defined(__NetBSD__) +#if defined(__DragonFly__) || defined(__NetBSD__) || defined(__APPLE__) ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), NULL) == 2); int index = 1; #else @@ -913,7 +920,9 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_connecting_reader, tc) int index = 0; #endif ATF_REQUIRE(kev[index].filter == EVFILT_WRITE); +#if !defined(__APPLE__) ATF_REQUIRE((kev[index].flags & EV_EOF) != 0); +#endif ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 }) == 0); #endif @@ -1085,6 +1094,11 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_read_eof_wakeups, tc) ATF_REQUIRE(close(p[1]) == 0); #if !defined(__linux__) && !defined(FORCE_EPOLL) +#if defined(__APPLE__) + ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), + &(struct timespec) { 0, 0 }) == 0); + atf_tc_skip("This doesn't work on macOS"); +#endif ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 }) == 1); ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]); @@ -1167,6 +1181,11 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__fifo_read_eof_state_when_reconnecting, tc) ATF_REQUIRE(close(p[1]) == 0); #if !defined(__linux__) && !defined(FORCE_EPOLL) +#if defined(__APPLE__) + ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), + &(struct timespec) { 0, 0 }) == 0); + atf_tc_skip("This doesn't work on macOS"); +#endif ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 }) == 1); ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]); @@ -1260,6 +1279,9 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__closed_read_end, tc) ATF_REQUIRE((kev[1].flags & EV_ERROR) != 0); #ifdef __NetBSD__ ATF_REQUIRE(kev[1].data == EBADF); +#elif defined(__APPLE__) + ATF_REQUIRE(kev[1].data == 0); + atf_tc_skip("This doesn't work on macOS"); #else ATF_REQUIRE(kev[1].data == EPIPE); #endif @@ -1579,6 +1601,9 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__closed_write_end, tc) ATF_REQUIRE((kev[1].flags & EV_ERROR) != 0); #ifdef __NetBSD__ ATF_REQUIRE(kev[1].data == EBADF); +#elif defined(__APPLE__) + ATF_REQUIRE(kev[1].data == 0); + atf_tc_skip("This doesn't work on macOS"); #else ATF_REQUIRE(kev[1].data == EPIPE); #endif @@ -1707,9 +1732,18 @@ ATF_TC_BODY_FD_LEAKCHECK(pipe__closed_write_end_register_before_close, tc) ATF_REQUIRE(close(p[1]) == 0); #if !defined(__linux__) && !defined(FORCE_EPOLL) -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || \ + defined(__APPLE__) ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 }) == 2); +#if defined(__APPLE__) + { + struct kevent tmp; + memcpy(&tmp, &kev[0], sizeof(tmp)); + memcpy(&kev[0], &kev[1], sizeof(tmp)); + memcpy(&kev[1], &tmp, sizeof(tmp)); + } +#endif { ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]); ATF_REQUIRE(kev[0].filter == EVFILT_WRITE); diff --git a/test/real_close.c b/test/real_close.c index e50c781..7a1ae1b 100644 --- a/test/real_close.c +++ b/test/real_close.c @@ -4,8 +4,11 @@ #include #include +#if !defined(__APPLE__) #include #include +#endif + #include extern int real_close_for_test(int fd); @@ -13,9 +16,12 @@ extern int real_close_for_test(int fd); int real_close_for_test(int fd) { +#if defined(__APPLE__) + return close(fd); +#else void *libc_handle; -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) struct r_debug *r_debug = NULL; for (Elf_Dyn *dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) { if (dyn->d_tag == DT_DEBUG) { @@ -62,4 +68,5 @@ real_close_for_test(int fd) } return real_close(fd); +#endif } diff --git a/test/signalfd-test.c b/test/signalfd-test.c index a29eb61..8328191 100644 --- a/test/signalfd-test.c +++ b/test/signalfd-test.c @@ -46,7 +46,8 @@ ATF_TC_BODY_FD_LEAKCHECK(signalfd__simple_signalfd, tcptr) { struct pollfd pfd = { .fd = sfd, .events = POLLIN }; - ATF_REQUIRE(poll(&pfd, 1, -1) == 1); + int n; + ATF_REQUIRE_MSG((n = poll(&pfd, 1, -1)) == 1, "%d", n); ATF_REQUIRE(pfd.revents == POLLIN); } @@ -351,7 +352,7 @@ ATF_TC_BODY_FD_LEAKCHECK(signalfd__sigwaitinfo, tcptr) } { -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) || defined(__APPLE__) sigset_t mask2; sigemptyset(&mask2); sigaddset(&mask2, SIGINT); @@ -378,7 +379,7 @@ ATF_TC_BODY_FD_LEAKCHECK(signalfd__sigwaitinfo, tcptr) } { -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) || defined(__APPLE__) sigset_t mask2; sigemptyset(&mask2); sigaddset(&mask2, SIGUSR1); @@ -526,10 +527,10 @@ ATF_TC_BODY_FD_LEAKCHECK(signalfd__sigchld, tcptr) ATF_REQUIRE(close(sfd) == 0); ATF_REQUIRE(fdsi.ssi_signo == SIGCHLD); -#if defined(__OpenBSD__) || defined(__DragonFly__) +#if defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) ATF_REQUIRE(fdsi.ssi_pid == 0); ATF_REQUIRE(fdsi.ssi_code == 0); - atf_tc_skip("OpenBSD/DragonFlyBSD do not fill si_pid on SIGCHLD"); + atf_tc_skip("OpenBSD/DragonFlyBSD/macOS do not fill si_pid on SIGCHLD"); #endif ATF_REQUIRE_MSG(fdsi.ssi_pid == (uint32_t)pid, "%d %d", (int)fdsi.ssi_pid, (int)pid); diff --git a/test/socketpair-test.c b/test/socketpair-test.c index e2d8b14..a405bb9 100644 --- a/test/socketpair-test.c +++ b/test/socketpair-test.c @@ -120,11 +120,15 @@ ATF_TC_BODY_FD_LEAKCHECK(socketpair__simple_edge_triggering, tc) ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLOUT)); ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); +#if defined(__APPLE__) + ATF_REQUIRE_ERRNO(ENOTCONN, shutdown(p[0], SHUT_RD) < 0); +#else ATF_REQUIRE(shutdown(p[0], SHUT_RD) == 0); ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLOUT)); ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); +#endif ATF_REQUIRE(shutdown(p[0], SHUT_WR) == 0); diff --git a/test/tst-epoll.c b/test/tst-epoll.c index dfdfd31..a0a8fdf 100644 --- a/test/tst-epoll.c +++ b/test/tst-epoll.c @@ -155,6 +155,10 @@ ATF_TC_BODY_FD_LEAKCHECK(epollfd_osv__epolloneshot, tc) ATF_TC_WITHOUT_HEAD(epollfd_osv__epoll_file); ATF_TC_BODY_FD_LEAKCHECK(epollfd_osv__epoll_file, tc) { +#if defined(__APPLE__) + atf_tc_skip("/dev/random not pollable under macOS"); +#endif + struct epoll_event events[MAXEVENTS]; int ep = epoll_create(1);