From 91484e380f67ee5a58a9d4277b66b02afda621bb Mon Sep 17 00:00:00 2001 From: Torrekie Gen Date: Sat, 6 Apr 2024 08:26:09 +0000 Subject: [PATCH 1/6] Enable epoll-shim for Darwin in favor of upstream support Signed-off-by: Torrekie Gen --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index f6f7dd84..9108c0da 100644 --- a/meson.build +++ b/meson.build @@ -16,7 +16,7 @@ config_h.set_quoted('PACKAGE', meson.project_name()) config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) cc_args = [] -if host_machine.system() not in ['freebsd', 'openbsd'] +if host_machine.system() not in ['freebsd', 'openbsd', 'darwin'] cc_args += ['-D_POSIX_C_SOURCE=200809L'] endif add_project_arguments(cc_args, language: 'c') @@ -69,8 +69,8 @@ endif config_h.set10('HAVE_BROKEN_MSG_CMSG_CLOEXEC', have_broken_msg_cmsg_cloexec) if get_option('libraries') - if host_machine.system() in ['freebsd', 'openbsd'] - # When building for FreeBSD, epoll(7) is provided by a userspace + if host_machine.system() in ['freebsd', 'openbsd', 'darwin'] + # When building for BSDs, epoll(7) is provided by a userspace # wrapper around kqueue(2). epoll_dep = dependency('epoll-shim') else From 3c6a88253cb93f2e1b200641e71136b5a2e58ce7 Mon Sep 17 00:00:00 2001 From: Torrekie Date: Sat, 20 Apr 2024 18:27:23 +0800 Subject: [PATCH 2/6] Avoid `SOCK_CLOEXEC` on Darwin Signed-off-by: Torrekie Gen --- src/wayland-os.c | 23 ++++++++++++++++++----- tests/client-test.c | 12 ++++++++++++ tests/connection-test.c | 18 ++++++++++++++++++ tests/os-wrappers-test.c | 4 +++- tests/resources-test.c | 25 +++++++++++++++++++++++++ tests/test-helpers.c | 24 ++++++++++++++++++++++++ tests/test-runner.c | 22 ++++++++++++++++++++++ tests/test-runner.h | 35 +++++++++++++++++++++++++++++++++-- 8 files changed, 155 insertions(+), 8 deletions(-) diff --git a/src/wayland-os.c b/src/wayland-os.c index f00ead4b..aa44f091 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -74,18 +74,20 @@ int wl_os_socket_cloexec(int domain, int type, int protocol) { int fd; - +#if !defined(__APPLE__) + /* It is ok to bypass this logic on Darwin, + FD_CLOEXEC will be set by set_cloexec_or_close() */ fd = wl_socket(domain, type | SOCK_CLOEXEC, protocol); if (fd >= 0) return fd; if (errno != EINVAL) return -1; - +#endif fd = wl_socket(domain, type, protocol); return set_cloexec_or_close(fd); } -#if defined(__FreeBSD__) +#if defined(LOCAL_PEERCRED) int wl_os_socket_peercred(int sockfd, uid_t *uid, gid_t *gid, pid_t *pid) { @@ -101,6 +103,14 @@ wl_os_socket_peercred(int sockfd, uid_t *uid, gid_t *gid, pid_t *pid) #if HAVE_XUCRED_CR_PID /* Since https://cgit.freebsd.org/src/commit/?id=c5afec6e895a */ *pid = ucred.cr_pid; +#elif defined(LOCAL_PEERPID) + /* Try LOCAL_PEERPID if no cr_pid in xucred */ + size_t pid_size; + pid_t peerpid; + if (getsockopt(sockfd, SOL_LOCAL, LOCAL_PEERPID, &peerpid, &pid_size)) + *pid = peerpid; + else + *pid = 0; #else *pid = 0; #endif @@ -178,13 +188,16 @@ recvmsg_cloexec_fallback(int sockfd, struct msghdr *msg, int flags) ssize_t wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) { -#if HAVE_BROKEN_MSG_CMSG_CLOEXEC +#if HAVE_BROKEN_MSG_CMSG_CLOEXEC || defined(__APPLE__) /* * FreeBSD had a broken implementation of MSG_CMSG_CLOEXEC between 2015 * and 2021, so we have to use the non-MSG_CMSG_CLOEXEC fallback * directly when compiling against a version that does not include the * fix (https://cgit.freebsd.org/src/commit/?id=6ceacebdf52211). */ + /* + * Darwin has no MSG_CMSG_CLOEXEC, so use fallback too. + */ #pragma message("Using fallback directly since MSG_CMSG_CLOEXEC is broken.") #else ssize_t len; @@ -220,7 +233,7 @@ wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int fd; -#ifdef HAVE_ACCEPT4 +#if defined(HAVE_ACCEPT4) && !defined(__APPLE__) fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC); if (fd >= 0) return fd; diff --git a/tests/client-test.c b/tests/client-test.c index 5585c0cd..b5c3f924 100644 --- a/tests/client-test.c +++ b/tests/client-test.c @@ -97,7 +97,13 @@ TEST(client_destroy_listener) bool user_data_destroyed = false; int s[2]; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); @@ -184,7 +190,13 @@ TEST(client_destroy_removes_link) struct client_destroy_listener destroy_listener; int s[2]; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); diff --git a/tests/connection-test.c b/tests/connection-test.c index dde5d89c..f79642e2 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -48,7 +48,13 @@ setup(int *s) { struct wl_connection *connection; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif connection = wl_connection_create(s[0], WL_BUFFER_DEFAULT_MAX_SIZE); assert(connection); @@ -181,8 +187,14 @@ struct marshal_data { static void setup_marshal_data(struct marshal_data *data) { +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, data->s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM , 0, data->s) == 0); + assert(set_cloexec_or_close(data->s[0]) != -1); + assert(set_cloexec_or_close(data->s[1]) != -1); +#endif data->read_connection = wl_connection_create(data->s[0], WL_BUFFER_DEFAULT_MAX_SIZE); assert(data->read_connection); @@ -885,7 +897,13 @@ TEST(request_bogus_size) for (bogus_size = 11; bogus_size >= 0; bogus_size--) { fprintf(stderr, "* bogus size %d\n", bogus_size); +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 061d29e6..55067485 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -23,6 +23,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + /* This test should be skipped on Darwin (as of 2024) */ +#ifndef __APPLE__ #include "../config.h" #define _GNU_SOURCE @@ -387,5 +389,5 @@ TEST(os_wrappers_epoll_create_cloexec_fallback) init_fallbacks(1); do_os_wrappers_epoll_create_cloexec(2); } - +#endif /* __APPLE__ */ /* FIXME: add tests for wl_os_accept_cloexec() */ diff --git a/tests/resources-test.c b/tests/resources-test.c index 92707297..33f6e21d 100644 --- a/tests/resources-test.c +++ b/tests/resources-test.c @@ -40,7 +40,14 @@ TEST(create_resource_tst) int s[2]; uint32_t id; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif + display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); @@ -111,7 +118,13 @@ TEST(destroy_res_tst) .notify = &destroy_notify }; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); @@ -159,7 +172,13 @@ TEST(create_resource_with_same_id) int s[2]; uint32_t id; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); @@ -243,7 +262,13 @@ TEST(resource_destroy_iteration) .notify = &resource_destroy_notify }; +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0); + assert(set_cloexec_or_close(s[0]) != -1); + assert(set_cloexec_or_close(s[1]) != -1); +#endif display = wl_display_create(); assert(display); client = wl_client_create(display, s[0]); diff --git a/tests/test-helpers.c b/tests/test-helpers.c index 1af813bb..9ae96b1b 100644 --- a/tests/test-helpers.c +++ b/tests/test-helpers.c @@ -88,6 +88,30 @@ count_open_fds(void) /* return the current number of entries */ return size / sizeof(struct kinfo_file); } +#elif defined(__APPLE__) +#include + +/* + * On Darwin, use libproc API to get fds of a PID + */ +int +count_open_fds(void) +{ + int buffer_size, buffer_used; + pid_t pid = getpid(); + int nfds; + struct proc_fdinfo *fdinfo; + + buffer_size = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, 0, 0); + fdinfo = malloc(buffer_size); + + buffer_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo, buffer_size); + assert(buffer_used > 0 && "proc_pidinfo PROC_PIDLISTFDS failed."); + + nfds = buffer_used / PROC_PIDLISTFD_SIZE; + free(fdinfo); + return nfds; +} #else int count_open_fds(void) diff --git a/tests/test-runner.c b/tests/test-runner.c index 9a50d1dd..5eafe3ea 100644 --- a/tests/test-runner.c +++ b/tests/test-runner.c @@ -63,7 +63,12 @@ static int timeouts_enabled = 1; /* set to one if the output goes to the terminal */ static int is_atty = 0; +#if __APPLE__ +extern const struct test __start_test_section __asm("section$start$__DATA$test_section"); +extern const struct test __stop_test_section __asm("section$end$__DATA$test_section"); +#else extern const struct test __start_test_section, __stop_test_section; +#endif static const struct test * find_test(const char *name) @@ -308,6 +313,23 @@ is_debugger_attached(void) return rc; } +#elif defined(__APPLE__) +#include +/* https://stackoverflow.com/a/2200786 */ +static int +is_debugger_attached(void) +{ + int ret; + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + struct kinfo_proc info; + size_t size; + + info.kp_proc.p_flag = 0; + ret = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(ret == 0); + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); +} #else static int is_debugger_attached(void) diff --git a/tests/test-runner.h b/tests/test-runner.h index d0734009..8936865b 100644 --- a/tests/test-runner.h +++ b/tests/test-runner.h @@ -37,11 +37,17 @@ struct test { int must_fail; } __attribute__ ((aligned (16))); +#if __APPLE__ +#define TEST_SECTION "__DATA,test_section" +#else +#define TEST_SECTION "test_section" +#endif + #define TEST(name) \ static void name(void); \ \ const struct test test##name \ - __attribute__ ((used, section ("test_section"))) = { \ + __attribute__ ((used, section (TEST_SECTION))) = { \ #name, name, 0 \ }; \ \ @@ -51,7 +57,7 @@ struct test { static void name(void); \ \ const struct test test##name \ - __attribute__ ((used, section ("test_section"))) = { \ + __attribute__ ((used, section (TEST_SECTION))) = { \ #name, name, 1 \ }; \ \ @@ -93,3 +99,28 @@ test_disable_coredumps(void); } while (0); #endif + +/* For systems without SOCK_CLOEXEC */ +#include +__attribute__((used)) +static int +set_cloexec_or_close(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + goto err; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; + + return fd; + +err: + close(fd); + return -1; +} \ No newline at end of file From 991b8d24f6b047913f846fa9786c585e594a720a Mon Sep 17 00:00:00 2001 From: Torrekie Gen Date: Mon, 8 Apr 2024 20:57:34 +0800 Subject: [PATCH 3/6] Fix symbol check on Darwin Signed-off-by: Torrekie Gen --- egl/wayland-egl-symbols-check | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/egl/wayland-egl-symbols-check b/egl/wayland-egl-symbols-check index d04fd042..39f80cd1 100755 --- a/egl/wayland-egl-symbols-check +++ b/egl/wayland-egl-symbols-check @@ -14,7 +14,15 @@ if ! test -n "$NM"; then exit 99 fi -AVAIL_FUNCS="$($NM -D --format=bsd --defined-only $LIB | awk '{print $3}')" +if [ "$(uname)" == "Darwin" ]; then + NM_DYNSYM_TABLE="" + SYMBOL_PREFIX="_" +else + NM_DYNSYM_TABLE="-D" + SYMBOL_PREFIX="" +endif + +AVAIL_FUNCS="$($NM ${NM_DYNSYM_TABLE} --format=bsd --defined-only $LIB | awk '{print $3}')" # Official ABI, taken from the header. REQ_FUNCS="wl_egl_window_resize @@ -37,7 +45,7 @@ if test -n "$NEW_ABI"; then fi REMOVED_ABI=$(echo "$REQ_FUNCS" | while read func; do - echo "$AVAIL_FUNCS" | grep -q "^$func$" && continue + echo "$AVAIL_FUNCS" | grep -q "^${SYMBOL_PREFIX}$func$" && continue echo $func done) From 4ee5ade866c674ccc43a21f277c0bdb0f807beb7 Mon Sep 17 00:00:00 2001 From: Torrekie Gen Date: Mon, 8 Apr 2024 21:10:25 +0800 Subject: [PATCH 4/6] Fix Darwin sockets nonblocking Co-authored-by: Weijia Wang Signed-off-by: Torrekie Gen --- tests/display-test.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/display-test.c b/tests/display-test.c index c2def444..1e0b389f 100644 --- a/tests/display-test.c +++ b/tests/display-test.c @@ -39,6 +39,10 @@ #include #include +#ifdef __APPLE__ +#include +#endif + #include #include @@ -1499,6 +1503,10 @@ send_overflow_client(void *data) /* Limit the send buffer size for the display socket to guarantee * that the test will cause an overflow. */ sock = wl_display_get_fd(c->wl_display); +#if __APPLE__ + /* Darwin sockets may by non-blocked after accept() */ + assert(fcntl(sock, F_SETFL, ~O_NONBLOCK) != -1); +#endif assert(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == 0); /* Request to break out of 'display_run' in the main process */ From d0d2a43e330542b303b2fb7b15275c6020960144 Mon Sep 17 00:00:00 2001 From: Torrekie Gen Date: Mon, 8 Apr 2024 22:29:54 +0800 Subject: [PATCH 5/6] Use Read-Only DATA (__RODATA) instead of __DATA Co-authored-by: Weijia Wang Signed-off-by: Torrekie Gen --- tests/test-runner.c | 4 ++-- tests/test-runner.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test-runner.c b/tests/test-runner.c index 5eafe3ea..639f5a2e 100644 --- a/tests/test-runner.c +++ b/tests/test-runner.c @@ -64,8 +64,8 @@ static int timeouts_enabled = 1; static int is_atty = 0; #if __APPLE__ -extern const struct test __start_test_section __asm("section$start$__DATA$test_section"); -extern const struct test __stop_test_section __asm("section$end$__DATA$test_section"); +extern const struct test __start_test_section __asm("section$start$__RODATA$test_section"); +extern const struct test __stop_test_section __asm("section$end$__RODATA$test_section"); #else extern const struct test __start_test_section, __stop_test_section; #endif diff --git a/tests/test-runner.h b/tests/test-runner.h index 8936865b..50eff6af 100644 --- a/tests/test-runner.h +++ b/tests/test-runner.h @@ -38,7 +38,7 @@ struct test { } __attribute__ ((aligned (16))); #if __APPLE__ -#define TEST_SECTION "__DATA,test_section" +#define TEST_SECTION "__RODATA,test_section" #else #define TEST_SECTION "test_section" #endif @@ -123,4 +123,4 @@ set_cloexec_or_close(int fd) err: close(fd); return -1; -} \ No newline at end of file +} From 2376a31c20a74947dba0ede4680579421d2f30e6 Mon Sep 17 00:00:00 2001 From: Torrekie Date: Sat, 20 Apr 2024 18:28:46 +0800 Subject: [PATCH 6/6] Re-enable os-wrappers-test.c on Darwin, fix errors Signed-off-by: Torrekie Gen --- egl/wayland-egl-symbols-check | 2 +- src/event-loop.c | 8 ++++++++ tests/os-wrappers-test.c | 23 +++++++++++++++-------- tests/queue-test.c | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/egl/wayland-egl-symbols-check b/egl/wayland-egl-symbols-check index 39f80cd1..82b9499d 100755 --- a/egl/wayland-egl-symbols-check +++ b/egl/wayland-egl-symbols-check @@ -20,7 +20,7 @@ if [ "$(uname)" == "Darwin" ]; then else NM_DYNSYM_TABLE="-D" SYMBOL_PREFIX="" -endif +fi AVAIL_FUNCS="$($NM ${NM_DYNSYM_TABLE} --format=bsd --defined-only $LIB | awk '{print $3}')" diff --git a/src/event-loop.c b/src/event-loop.c index 45222f71..1986939a 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -45,6 +45,14 @@ #include "wayland-server-private.h" #include "wayland-os.h" +#ifdef __APPLE__ +/* epoll-shim should provide this by design */ +struct itimerspec { + struct timespec it_interval; + struct timespec it_value; +}; +#endif + /** \cond INTERNAL */ #define TIMER_REMOVED -2 diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 55067485..d9cd7c40 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -23,8 +23,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - /* This test should be skipped on Darwin (as of 2024) */ -#ifndef __APPLE__ #include "../config.h" #define _GNU_SOURCE @@ -62,12 +60,12 @@ static int socket_wrapper(int domain, int type, int protocol) { wrapped_calls_socket++; - +#ifdef SOCK_CLOEXEC if (fall_back && (type & SOCK_CLOEXEC)) { errno = EINVAL; return -1; } - +#endif return socket(domain, type, protocol); } @@ -111,11 +109,12 @@ static ssize_t recvmsg_wrapper(int sockfd, struct msghdr *msg, int flags) { wrapped_calls_recvmsg++; - +#ifdef MSG_CMSG_CLOEXEC if (fall_back && (flags & MSG_CMSG_CLOEXEC)) { errno = EINVAL; return -1; } +#endif return recvmsg(sockfd, msg, flags); } @@ -160,8 +159,11 @@ do_os_wrappers_socket_cloexec(int n) * Must have 2 calls if falling back, but must also allow * falling back without a forced fallback. */ +#ifdef SOCK_CLOEXEC assert(wrapped_calls_socket > n); - +#else + assert(wrapped_calls_socket == 1); +#endif exec_fd_leak_check(nr_fds); } @@ -234,8 +236,14 @@ struct marshal_data { static void setup_marshal_data(struct marshal_data *data) { +#ifdef SOCK_CLOEXEC assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, data->s) == 0); +#else + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, data->s) == 0); + assert(set_cloexec_or_close(data->s[0]) != -1); + assert(set_cloexec_or_close(data->s[1]) != -1); +#endif data->read_connection = wl_connection_create(data->s[0], WL_BUFFER_DEFAULT_MAX_SIZE); @@ -325,7 +333,7 @@ do_os_wrappers_recvmsg_cloexec(int n) struct marshal_data data; data.nr_fds_begin = count_open_fds(); -#if HAVE_BROKEN_MSG_CMSG_CLOEXEC +#if HAVE_BROKEN_MSG_CMSG_CLOEXEC || !defined(MSG_CMSG_CLOEXEC) /* We call the fallback directly on FreeBSD versions with a broken * MSG_CMSG_CLOEXEC, so we don't call the local recvmsg() wrapper. */ data.wrapped_calls = 0; @@ -389,5 +397,4 @@ TEST(os_wrappers_epoll_create_cloexec_fallback) init_fallbacks(1); do_os_wrappers_epoll_create_cloexec(2); } -#endif /* __APPLE__ */ /* FIXME: add tests for wl_os_accept_cloexec() */ diff --git a/tests/queue-test.c b/tests/queue-test.c index cb61a85b..014c5de1 100644 --- a/tests/queue-test.c +++ b/tests/queue-test.c @@ -23,7 +23,26 @@ * SOFTWARE. */ +#ifndef __APPLE__ #define _GNU_SOURCE /* For memrchr */ +#else +#include +/* No memrchr() on Darwin, borrow one from OpenBSD */ +static void * +memrchr(const void *s, int c, size_t n) +{ + const unsigned char *cp; + + if (n != 0) { + cp = (unsigned char *)s + n; + do { + if (*(--cp) == (unsigned char)c) + return((void *)cp); + } while (--n != 0); + } + return(NULL); +} +#endif #include #include #include