Support OpenBSD

Submitted by Leonid Bobrov on Feb. 8, 2019, 12:05 p.m.

Details

Message ID 20190208120536.smw2i6f5btnpxcyk@mazocomp.lan
State New
Series "Support OpenBSD"
Headers show

Commit Message

Leonid Bobrov Feb. 8, 2019, 12:05 p.m.
From 6b2cd23b0681a7ad253201071d243cbefc1bc0aa Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Thu, 7 Feb 2019 18:02:57 +0200
Subject: [PATCH 1/8] tests: fix main symbol duplication

So far I got these errors before patching:

libtool: link: cc -o .libs/headers-test -pthread -Wall -Wextra -Wno-unused-parameter -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden -O2 -pipe tests/headers-test.o tests/headers-protocol-test.o tests/headers-protocol-core-test.o /tmp/obj/wayland-1.16.0/build-amd64/.libs/libtest-runner.a -L.libs -lwayland-client -lffi -lm -lwayland-server -lkvm -Wl,-rpath-link,/usr/local/lib
ld: error: duplicate symbol: main
>>> defined at headers-test.c:53 (/tmp/obj/wayland-1.16.0/wayland-1.16.0/tests/headers-test.c:53)
>>>            tests/headers-test.o:(main)
>>> defined at test-runner.c:377 (/tmp/obj/wayland-1.16.0/wayland-1.16.0/tests/test-runner.c:377)
>>>            test-runner.o:(.text+0x250) in archive /tmp/obj/wayland-1.16.0/build-amd64/.libs/libtest-runner.a

libtool: link: cc -o .libs/exec-fd-leak-checker -pthread -Wall -Wextra -Wno-unused-parameter -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden -O2 -pipe tests/exec-fd-leak-checker.o /tmp/obj/wayland-1.16.0/build-amd64/.libs/libtest-runner.a -L.libs -lwayland-client -lffi -lm -lwayland-server -lkvm -Wl,-rpath-link,/usr/local/lib
ld: error: duplicate symbol: main
>>> defined at exec-fd-leak-checker.c:57 (/tmp/obj/wayland-1.16.0/wayland-1.16.0/tests/exec-fd-leak-checker.c:57)
>>>            tests/exec-fd-leak-checker.o:(main)
>>> defined at test-runner.c:377 (/tmp/obj/wayland-1.16.0/wayland-1.16.0/tests/test-runner.c:377)
>>>            test-runner.o:(.text+0x250) in archive /tmp/obj/wayland-1.16.0/build-amd64/.libs/libtest-runner.a

Makefile.am: error: object 'tests/test-helpers.$(OBJEXT)' created both with libtool and without

libtool: link: cc -o .libs/fixed-benchmark -pthread -Wall -Wextra -Wno-unused-parameter -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden -O2 -pipe tests/fixed-benchmark.o /tmp/obj/wayland-1.16.0/build-amd64/.libs/libtest-runner.a -L.libs -lwayland-client -lffi -lm -lwayland-server -lkvm -Wl,-rpath-link,/usr/local/lib
ld: error: duplicate symbol: main
>>> defined at fixed-benchmark.c:100 (/tmp/obj/wayland-1.16.0/wayland-1.16.0/tests/fixed-benchmark.c:100)
>>>            tests/fixed-benchmark.o:(main)
>>> defined at test-runner.c:377 (/tmp/obj/wayland-1.16.0/wayland-1.16.0/tests/test-runner.c:377)
>>>            test-runner.o:(.text+0x250) in archive /tmp/obj/wayland-1.16.0/build-amd64/.libs/libtest-runner.a

This commit fixes all of that.

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 Makefile.am | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

Patch hide | download patch | download mbox

diff --git a/Makefile.am b/Makefile.am
index 697c517..cce4d73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -210,12 +210,16 @@  noinst_PROGRAMS =				\
 	exec-fd-leak-checker			\
 	fixed-benchmark
 
-noinst_LTLIBRARIES += libtest-runner.la
+noinst_LTLIBRARIES +=				\
+	libtest-runner.la			\
+	libtest-helpers.la
+
+libtest_helpers_la_SOURCES = tests/test-helpers.c
+libtest_helpers_la_LIBADD = -lrt -ldl $(FFI_LIBS)
 
 libtest_runner_la_SOURCES =			\
 	tests/test-runner.c			\
 	tests/test-runner.h			\
-	tests/test-helpers.c			\
 	tests/test-compositor.h			\
 	tests/test-compositor.c
 libtest_runner_la_LIBADD =			\
@@ -223,8 +227,7 @@  libtest_runner_la_LIBADD =			\
 	libwayland-util.la			\
 	libwayland-client.la			\
 	libwayland-server.la			\
-	-lrt -ldl $(FFI_LIBS)
-
+	libtest-helpers.la
 
 array_test_SOURCES = tests/array-test.c
 array_test_LDADD = libtest-runner.la
@@ -270,7 +273,6 @@  protocol_logger_test_LDADD = libtest-runner.la
 headers_test_SOURCES = tests/headers-test.c \
 		       tests/headers-protocol-test.c \
 		       tests/headers-protocol-core-test.c
-headers_test_LDADD = libtest-runner.la
 nodist_headers_test_SOURCES =			\
 	protocol/wayland-server-protocol-core.h	\
 	protocol/wayland-client-protocol-core.h
@@ -280,13 +282,12 @@  cpp_compile_test_SOURCES = tests/cpp-compile-test.cpp
 endif
 
 fixed_benchmark_SOURCES = tests/fixed-benchmark.c
-fixed_benchmark_LDADD = libtest-runner.la
 
 os_wrappers_test_SOURCES = tests/os-wrappers-test.c
 os_wrappers_test_LDADD = libtest-runner.la
 
 exec_fd_leak_checker_SOURCES = tests/exec-fd-leak-checker.c
-exec_fd_leak_checker_LDADD = libtest-runner.la
+exec_fd_leak_checker_LDADD = libtest-helpers.la
 
 EXTRA_DIST += tests/scanner-test.sh			\
 	protocol/tests.xml				\
-- 
2.20.1


From 6c51f608e7eab095310d3c6b0e46ccae05cef82d Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 10:07:56 +0200
Subject: [PATCH 2/8] tests: use *BSD replacement for /proc

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 Makefile.am          |  2 +-
 configure.ac         | 12 ++++++++++++
 tests/test-helpers.c | 33 ++++++++++++++++++++++++++++++++-
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index cce4d73..52c7895 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -215,7 +215,7 @@  noinst_LTLIBRARIES +=				\
 	libtest-helpers.la
 
 libtest_helpers_la_SOURCES = tests/test-helpers.c
-libtest_helpers_la_LIBADD = -lrt -ldl $(FFI_LIBS)
+libtest_helpers_la_LIBADD = -lrt -ldl $(FFI_LIBS) $(KVM_LIBS)
 
 libtest_runner_la_SOURCES =			\
 	tests/test-runner.c			\
diff --git a/configure.ac b/configure.ac
index 18fb649..912330e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,18 @@  AC_SUBST(GCC_CFLAGS)
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl])
 
+# Replacement for /proc on BSD
+AC_CHECK_HEADERS([kvm.h])
+SAVE_LIBS="$LIBS"
+LIBS=
+AC_CHECK_LIB([kvm], [kvm_getfiles])
+KVM_LIBS="$LIBS"
+LIBS="$SAVE_LIBS"
+AC_SUBST(KVM_LIBS)
+if test "x$ac_cv_header_kvm_h" != "x" && test "x$ac_cv_lib_kvm_kvm_getfiles" != "x"; then
+	AC_DEFINE(USE_LIBKVM, 1, [use libkvm on BSD])
+fi
+
 AC_ARG_ENABLE([libraries],
 	      [AC_HELP_STRING([--disable-libraries],
 			      [Disable compilation of wayland libraries])],
diff --git a/tests/test-helpers.c b/tests/test-helpers.c
index b2189d8..1c83e00 100644
--- a/tests/test-helpers.c
+++ b/tests/test-helpers.c
@@ -25,9 +25,20 @@ 
 
 #include "config.h"
 
-#include <assert.h>
+#include "../config.h"
+
+#ifdef USE_LIBKVM
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#else
 #include <errno.h>
 #include <dirent.h>
+#endif
+
+#include <assert.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <time.h>
@@ -43,6 +54,25 @@ 
 int
 count_open_fds(void)
 {
+#ifdef USE_LIBKVM
+	/* Use BSD-specific kernel memory interface */
+
+	struct kinfo_file *kif;
+	kvm_t *kd;
+	int count;
+	char errstr[_POSIX2_LINE_MAX];
+
+	kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY|KVM_NO_FILES, errstr);
+	assert(kd != NULL);
+	kif = kvm_getfiles(kd, KERN_FILE_BYPID, getpid(), sizeof(struct kinfo_file), &count);
+	assert(kif != NULL);
+
+	/* KVM library frees memory on itself */
+	kvm_close(kd);
+	return count;
+#else
+	/* Use /proc filesystem. */
+
 	DIR *dir;
 	struct dirent *ent;
 	int count = 0;
@@ -62,6 +92,7 @@  count_open_fds(void)
 	closedir(dir);
 
 	return count;
+#endif
 }
 
 void
-- 
2.20.1


From 5857190b2b45e05f67972aec87ae486b87e66ef6 Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 10:24:14 +0200
Subject: [PATCH 3/8] server: add *BSD credentials support.

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 configure.ac         |  3 ++
 src/wayland-server.c | 46 ++++++++++++++++++++++
 src/wayland-shm.c    | 92 +++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 912330e..d106a41 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,9 @@  AC_SUBST(GCC_CFLAGS)
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl])
 
+# Credential support on BSD
+AC_CHECK_HEADERS([sys/ucred.h])
+
 # Replacement for /proc on BSD
 AC_CHECK_HEADERS([kvm.h])
 SAVE_LIBS="$LIBS"
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 19f6a76..f4cdbf3 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -25,6 +25,13 @@ 
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/types.h>
+#include <sys/ucred.h>
+#endif
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <stddef.h>
@@ -77,7 +84,13 @@  struct wl_client {
 	struct wl_list link;
 	struct wl_map objects;
 	struct wl_priv_signal destroy_signal;
+#ifdef HAVE_SYS_UCRED_H
+	/* BSD */
+	struct xucred xucred;
+#else
+	/* Linux */
 	struct ucred ucred;
+#endif
 	int error;
 	struct wl_priv_signal resource_created_signal;
 };
@@ -312,7 +325,11 @@  wl_resource_post_error(struct wl_resource *resource,
 static void
 destroy_client_with_error(struct wl_client *client, const char *reason)
 {
+#ifdef HAVE_SYS_UCRED_H
+	wl_log("%s (uid %u)\n", reason, client->xucred.cr_uid);
+#else
 	wl_log("%s (pid %u)\n", reason, client->ucred.pid);
+#endif
 	wl_client_destroy(client);
 }
 
@@ -526,10 +543,29 @@  wl_client_create(struct wl_display *display, int fd)
 	if (!client->source)
 		goto err_client;
 
+#ifndef SO_PEERCRED
+/* FreeBSD */
+# define SO_PEERCRED LOCAL_PEERCRED
+#endif
+
+#ifdef HAVE_SYS_UCRED_H
+	/* BSD */
+	len = sizeof client->xucred;
+	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED,
+		       &client->xucred, &len) < 0
+# ifdef XUCRED_VERSION
+	               /* FreeBSD */
+		       || client->xucred.cr_version != XUCRED_VERSION
+# endif
+	              )
+		goto err_source;
+#else
+	/* Linux */
 	len = sizeof client->ucred;
 	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED,
 		       &client->ucred, &len) < 0)
 		goto err_source;
+#endif
 
 	client->connection = wl_connection_create(fd);
 	if (client->connection == NULL)
@@ -583,12 +619,22 @@  WL_EXPORT void
 wl_client_get_credentials(struct wl_client *client,
 			  pid_t *pid, uid_t *uid, gid_t *gid)
 {
+#ifdef HAVE_SYS_UCRED_H
+	/* BSD */
+	*pid = 0; /* FIXME: pid is not defined on BSD */
+	if (uid)
+		*uid = client->xucred.cr_uid;
+	if (gid)
+		*gid = client->xucred.cr_gid;
+#else
+	/* Linux */
 	if (pid)
 		*pid = client->ucred.pid;
 	if (uid)
 		*uid = client->ucred.uid;
 	if (gid)
 		*gid = client->ucred.gid;
+#endif
 }
 
 /** Get the file descriptor for the client
diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index 4191231..dbaf464 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -30,6 +30,8 @@ 
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -59,6 +61,9 @@  struct wl_shm_pool {
 	char *data;
 	int32_t size;
 	int32_t new_size;
+#ifdef HAVE_SYS_UCRED_H
+	int fd;
+#endif
 };
 
 struct wl_shm_buffer {
@@ -76,15 +81,91 @@  struct wl_shm_sigbus_data {
 	int fallback_mapping_used;
 };
 
+#ifdef HAVE_MREMAP
+static void *
+mremap_compat_maymove(void *old_address, size_t old_size, size_t new_size,
+		      int old_prot, int old_flags, int old_fd)
+{
+	return mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
+}
+#else
+static void *
+mremap_compat_maymove(void *old_address, size_t old_size, size_t new_size,
+		      int old_prot, int old_flags, int old_fd)
+{
+	/* FreeBSD doesn't support mremap() yet, so we have to emulate it.
+	 * This assumes MREMAP_MAYMOVE is the only flag in use. */
+	if (new_size == old_size) {
+		return old_address;
+	} else if (new_size < old_size) {
+		/* Shrinking: munmap() the spare region. */
+		munmap(old_address + old_size, new_size - old_size);
+		return old_address;
+	} else {
+		void *ret;
+
+		/* Growing. Try and mmap() the extra region at the end of
+		 * our existing allocation. If that gets mapped in the
+		 * wrong place, fall back to mmap()ing an entirely new
+		 * region of new_size and copying the data across. */
+		ret = mmap(old_address + old_size, new_size - old_size,
+			   old_prot, old_flags, old_fd, 0);
+
+/* FIXME TODO: msync() before munmap()? */
+		if (ret == MAP_FAILED) {
+			/* Total failure! */
+			return ret;
+		} else if (ret == old_address + old_size) {
+			/* Success. */
+			return old_address;
+		} else if (ret != old_address + old_size) {
+			/* Partial failure. Fall back to mapping an
+			 * entirely new region. Unmap the region we
+			 * just mapped first. */
+			munmap(ret, new_size - old_size);
+
+			/* Map an entirely new region. */
+			ret = mmap(NULL, new_size,
+				   old_prot, old_flags, old_fd, 0);
+			if (ret == MAP_FAILED) {
+				/* Total failure! */
+				return ret;
+			}
+
+			/* Copy the old data across. Implicit assumption
+			 * that the old and new regions don't overlap. */
+			memcpy(ret, old_address, old_size);
+
+			/* Unmap the old region. */
+			munmap(old_address, old_size);
+
+			return ret;
+		}
+	}
+
+	/* Unreachable. */
+	return MAP_FAILED;
+}
+#endif
+
 static void
 shm_pool_finish_resize(struct wl_shm_pool *pool)
 {
 	void *data;
+#ifdef HAVE_SYS_UCRED_H
+	int fd = -1;
+#endif
 
 	if (pool->size == pool->new_size)
 		return;
 
+#ifdef HAVE_SYS_UCRED_H
+	fd = pool->fd;
+	data = mremap_compat_maymove(pool->data, pool->size, pool->new_size,
+	                             PROT_READ|PROT_WRITE, MAP_SHARED, fd);
+#else
 	data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
+#endif
 	if (data == MAP_FAILED) {
 		wl_resource_post_error(pool->resource,
 				       WL_SHM_ERROR_INVALID_FD,
@@ -110,6 +191,10 @@  shm_pool_unref(struct wl_shm_pool *pool, bool external)
 	if (pool->internal_refcount + pool->external_refcount)
 		return;
 
+#ifdef HAVE_SYS_UCRED_H
+	close(pool->fd);
+#endif
+
 	munmap(pool->data, pool->size);
 	free(pool);
 }
@@ -284,7 +369,13 @@  shm_create_pool(struct wl_client *client, struct wl_resource *resource,
 				       "failed mmap fd %d: %m", fd);
 		goto err_free;
 	}
+#ifdef HAVE_SYS_UCRED_H
+	/* We need to keep the FD around on FreeBSD so we can implement
+	 * mremap(). See: mremap_compat_maymove(). */
+	pool->fd = fd;
+#else
 	close(fd);
+#endif
 
 	pool->resource =
 		wl_resource_create(client, &wl_shm_pool_interface, 1, id);
@@ -364,7 +455,6 @@  wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
 	return buffer->stride;
 }
 
-
 /** Get a pointer to the memory for the SHM buffer
  *
  * \param buffer The buffer object
-- 
2.20.1


From 4111a5e2e54d1fe13a6cb813681b9e823a379b89 Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 10:32:58 +0200
Subject: [PATCH 4/8] gitignore: ignore *.orig files generated by patch(1)

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index eadea12..8d2c614 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ 
 *.trs
 *.tar.xz
 *~
+*.orig
 *-test
 .libs
 .dirstamp
-- 
2.20.1


From 8d5f298fb9df46e5856e178a06a24df78e46af68 Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 11:50:57 +0200
Subject: [PATCH 5/8] core: support kqueue

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 Makefile.am                              |   3 +-
 configure.ac                             |  19 +-
 doc/doxygen/Makefile.am                  |   3 +-
 src/{event-loop.c => event-loop-epoll.c} |   4 +
 src/event-loop-kqueue.c                  | 825 +++++++++++++++++++++++
 src/wayland-os.c                         |  47 +-
 src/wayland-os.h                         |   2 +-
 tests/os-wrappers-test.c                 |  55 +-
 8 files changed, 940 insertions(+), 18 deletions(-)
 rename src/{event-loop.c => event-loop-epoll.c} (99%)
 create mode 100644 src/event-loop-kqueue.c

diff --git a/Makefile.am b/Makefile.am
index 52c7895..426e20a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,7 +76,8 @@  libwayland_server_la_LDFLAGS = -version-info 1:0:1
 libwayland_server_la_SOURCES =			\
 	src/wayland-server.c			\
 	src/wayland-shm.c			\
-	src/event-loop.c
+	src/event-loop-epoll.c			\
+	src/event-loop-kqueue.c
 
 nodist_libwayland_server_la_SOURCES =		\
 	protocol/wayland-server-protocol.h	\
diff --git a/configure.ac b/configure.ac
index d106a41..941749d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,11 @@  AC_SUBST(GCC_CFLAGS)
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl])
 
+AC_CHECK_HEADERS([sys/epoll.h sys/event.h])
+if test "x$ac_cv_header_sys_epoll_h" != "xyes" && test "x$ac_cv_header_sys_event_h" != "xyes"; then
+	AC_MSG_ERROR([Can't find sys/epoll.h or sys/event.h. Please ensure either epoll or kqueue is available.])
+fi
+
 # Credential support on BSD
 AC_CHECK_HEADERS([sys/ucred.h])
 
@@ -115,12 +120,14 @@  AC_SUBST([ICONDIR])
 
 if test "x$enable_libraries" = "xyes"; then
 	PKG_CHECK_MODULES(FFI, [libffi])
-	AC_CHECK_DECL(SFD_CLOEXEC,[],
-		      [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland libraries")],
-		      [[#include <sys/signalfd.h>]])
-	AC_CHECK_DECL(TFD_CLOEXEC,[],
-		      [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland libraries")],
-		      [[#include <sys/timerfd.h>]])
+	if test "x$ac_cv_header_sys_epoll_h" == "xyes"; then
+		AC_CHECK_DECL(SFD_CLOEXEC,[],
+			      [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland libraries")],
+			      [[#include <sys/signalfd.h>]])
+		AC_CHECK_DECL(TFD_CLOEXEC,[],
+			      [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland libraries")],
+			      [[#include <sys/timerfd.h>]])
+	fi
 	AC_CHECK_DECL(CLOCK_MONOTONIC,[],
 		      [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland libraries")],
 		      [[#include <time.h>]])
diff --git a/doc/doxygen/Makefile.am b/doc/doxygen/Makefile.am
index f8b0b3a..60bed53 100644
--- a/doc/doxygen/Makefile.am
+++ b/doc/doxygen/Makefile.am
@@ -19,7 +19,8 @@  scanned_src_files_Client = 				\
 
 scanned_src_files_Server = 				\
 	$(scanned_src_files_shared)			\
-	$(top_srcdir)/src/event-loop.c		\
+	$(top_srcdir)/src/event-loop-epoll.c	\
+	$(top_srcdir)/src/event-loop-kqueue.c	\
 	$(top_srcdir)/src/wayland-server.c	\
 	$(top_srcdir)/src/wayland-server.h	\
 	$(top_srcdir)/src/wayland-server-core.h	\
diff --git a/src/event-loop.c b/src/event-loop-epoll.c
similarity index 99%
rename from src/event-loop.c
rename to src/event-loop-epoll.c
index eb2dce6..76cbfae 100644
--- a/src/event-loop.c
+++ b/src/event-loop-epoll.c
@@ -23,6 +23,9 @@ 
  * SOFTWARE.
  */
 
+#include "../config.h"
+
+#ifdef HAVE_SYS_EPOLL_H
 #include <stddef.h>
 #include <stdio.h>
 #include <errno.h>
@@ -702,3 +705,4 @@  wl_event_loop_get_destroy_listener(struct wl_event_loop *loop,
 {
 	return wl_signal_get(&loop->destroy_signal, notify);
 }
+#endif
diff --git a/src/event-loop-kqueue.c b/src/event-loop-kqueue.c
new file mode 100644
index 0000000..2713416
--- /dev/null
+++ b/src/event-loop-kqueue.c
@@ -0,0 +1,825 @@ 
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "../config.h"
+
+#ifdef HAVE_SYS_EVENT_H
+#include <stddef.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <unistd.h>
+#include "wayland-util.h"
+#include "wayland-private.h"
+#include "wayland-server-core.h"
+#include "wayland-os.h"
+
+/** \cond INTERNAL */
+
+struct wl_event_loop {
+	int event_fd;
+	struct wl_list check_list;
+	struct wl_list idle_list;
+	struct wl_list destroy_list;
+
+	struct wl_signal destroy_signal;
+
+	/*
+	 * kqueue doesn't allow to setup multiple handlers of the same signal.
+	 * Also, with kqueue we need to keep list of registered timers,
+	 * to avoid accidental timer ID reuse.
+	 * Thus we have to maintain lists of signal & timer handlers separately.
+	 *
+	 * TODO: locking.
+	 */
+	struct wl_list signals[NSIG];
+	struct wl_list timers;
+};
+
+struct wl_event_source_interface {
+	int (*dispatch)(struct wl_event_source *source,
+	                struct kevent *ep);
+};
+
+struct wl_event_source {
+	struct wl_event_source_interface *interface;
+	struct wl_event_loop *loop;
+	struct wl_list link;
+	void *data;
+	int fd;
+};
+
+struct wl_event_source_fd {
+	struct wl_event_source base;
+	wl_event_loop_fd_func_t func;
+};
+
+/** \endcond */
+
+static int
+wl_event_source_fd_dispatch(struct wl_event_source *source,
+                            struct kevent *ev)
+{
+	struct wl_event_source_fd *fd_source = (struct wl_event_source_fd *) source;
+	uint32_t mask;
+
+	mask = 0;
+	if (ev->filter == EVFILT_READ)
+		mask |= WL_EVENT_READABLE;
+	if (ev->filter == EVFILT_WRITE)
+		mask |= WL_EVENT_WRITABLE;
+	if (ev->flags & EV_ERROR)
+		mask |= WL_EVENT_ERROR;
+	if (ev->flags & EV_EOF)
+		mask |= WL_EVENT_HANGUP;
+
+	return fd_source->func(source->fd, mask, source->data);
+}
+
+struct wl_event_source_interface fd_source_interface = {
+	wl_event_source_fd_dispatch,
+};
+
+/** Create a file descriptor event source
+ *
+ * \param loop The event loop that will process the new source.
+ * \param fd The file descriptor to watch.
+ * \param mask A bitwise-or of which events to watch for: \c WL_EVENT_READABLE,
+ * \c WL_EVENT_WRITABLE.
+ * \param func The file descriptor dispatch function.
+ * \param data User data.
+ * \return A new file descriptor event source.
+ *
+ * The given file descriptor is initially watched for the events given in
+ * \c mask. This can be changed as needed with wl_event_source_fd_update().
+ *
+ * If it is possible that program execution causes the file descriptor to be
+ * read while leaving the data in a buffer without actually processing it,
+ * it may be necessary to register the file descriptor source to be re-checked,
+ * see wl_event_source_check(). This will ensure that the dispatch function
+ * gets called even if the file descriptor is not readable or writable
+ * anymore. This is especially useful with IPC libraries that automatically
+ * buffer incoming data, possibly as a side-effect of other operations.
+ *
+ * \sa wl_event_loop_fd_func_t
+ * \memberof wl_event_source
+ */
+WL_EXPORT struct wl_event_source *
+wl_event_loop_add_fd(struct wl_event_loop *loop,
+		     int fd, uint32_t mask,
+		     wl_event_loop_fd_func_t func,
+		     void *data)
+{
+	struct wl_event_source_fd *source;
+
+	source = malloc(sizeof *source);
+	if (source == NULL)
+		return NULL;
+
+	/* save original fd, but monitor a duplicate */
+	source->base.interface = &fd_source_interface;
+	source->base.fd = fd;
+	source->func = func;
+	source->base.loop = loop;
+	source->base.data = data;
+	wl_list_init(&source->base.link);
+	if (wl_event_source_fd_update(&source->base, mask) == -1) {
+		free(source);
+		return NULL;
+	}
+
+	return &source->base;
+}
+
+/** Update a file descriptor source's event mask
+ *
+ * \param source The file descriptor event source to update.
+ * \param mask The new mask, a bitwise-or of: \c WL_EVENT_READABLE,
+ * \c WL_EVENT_WRITABLE.
+ * \return 0 on success, -1 on failure.
+ *
+ * This changes which events, readable and/or writable, cause the dispatch
+ * callback to be called on.
+ *
+ * File descriptors are usually writable to begin with, so they do not need to
+ * be polled for writable until a write actually fails. When a write fails,
+ * the event mask can be changed to poll for readable and writable, delivering
+ * a dispatch callback when it is possible to write more. Once all data has
+ * been written, the mask can be changed to poll only for readable to avoid
+ * busy-looping on dispatch.
+ *
+ * \sa wl_event_loop_add_fd()
+ * \memberof wl_event_source
+ */
+WL_EXPORT int
+wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask)
+{
+	struct wl_event_loop *loop = source->loop;
+	struct kevent events[2];
+
+	if (mask & WL_EVENT_READABLE)
+		EV_SET(&events[0], source->fd, EVFILT_READ,
+		       EV_ADD | EV_ENABLE, 0, 0, source);
+	else
+		EV_SET(&events[0], source->fd, EVFILT_READ,
+		       EV_ADD | EV_DISABLE, 0, 0, source);
+
+	if (mask & WL_EVENT_WRITABLE)
+		EV_SET(&events[1], source->fd, EVFILT_WRITE,
+		       EV_ADD | EV_ENABLE, 0, 0, source);
+	else
+		EV_SET(&events[1], source->fd, EVFILT_WRITE,
+		       EV_ADD | EV_DISABLE, 0, 0, source);
+
+	return kevent(loop->event_fd, events, 2, NULL, 0, NULL);
+}
+
+/** \cond INTERNAL */
+
+struct wl_event_source_timer {
+	struct wl_event_source base;
+	wl_event_loop_timer_func_t func;
+	struct wl_list timer_link;
+};
+
+/** \endcond */
+
+static int
+wl_event_source_timer_dispatch(struct wl_event_source *source,
+                               struct kevent *ev)
+{
+	struct wl_event_source_timer *timer_source =
+		(struct wl_event_source_timer *) source;
+	uint64_t expires;
+
+	expires = ev->data;    /* XXX unused?! */
+	return timer_source->func(timer_source->base.data);
+}
+
+struct wl_event_source_interface timer_source_interface = {
+	wl_event_source_timer_dispatch,
+};
+
+/** Create a timer event source
+ *
+ * \param loop The event loop that will process the new source.
+ * \param func The timer dispatch function.
+ * \param data User data.
+ * \return A new timer event source.
+ *
+ * The timer is initially disarmed. It needs to be armed with a call to
+ * wl_event_source_timer_update() before it can trigger a dispatch call.
+ *
+ * \sa wl_event_loop_timer_func_t
+ * \memberof wl_event_source
+ */
+WL_EXPORT struct wl_event_source *
+wl_event_loop_add_timer(struct wl_event_loop *loop,
+			wl_event_loop_timer_func_t func,
+			void *data)
+{
+	/*
+	 * BSD. We use kqueue() timers directly, without additional FD.
+	 * See: wl_event_source_timer_update().
+	 *
+	 * FIXME: timer ID wrapping: need to keep list of used timers.
+	 */
+	static int next_timer_id = 0;
+	struct wl_event_source_timer *source;
+
+	source = malloc(sizeof *source);
+	if (source == NULL)
+		return NULL;
+	source->base.interface = &timer_source_interface;
+	source->base.fd = next_timer_id++;    /* TODO: locking/atomic ops */
+	source->base.loop = loop;
+	source->base.data = data;
+	source->func = func;
+	wl_list_insert(&loop->timers, &source->timer_link);
+	wl_list_init(&source->base.link);
+
+	return &source->base;
+}
+
+/** Arm or disarm a timer
+ *
+ * \param source The timer event source to modify.
+ * \param ms_delay The timeout in milliseconds.
+ * \return 0 on success, -1 on failure.
+ *
+ * If the timeout is zero, the timer is disarmed.
+ *
+ * If the timeout is non-zero, the timer is set to expire after the given
+ * timeout in milliseconds. When the timer expires, the dispatch function
+ * set with wl_event_loop_add_timer() is called once from
+ * wl_event_loop_dispatch(). If another dispatch is desired after another
+ * expiry, wl_event_source_timer_update() needs to be called again.
+ *
+ * \memberof wl_event_source
+ */
+WL_EXPORT int
+wl_event_source_timer_update(struct wl_event_source *source, int ms_delay)
+{
+	struct kevent ev;
+
+	EV_SET(&ev, source->fd, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0,
+	       ms_delay, source);
+	if (kevent(source->loop->event_fd, &ev, 1, NULL, 0, NULL) < 0) {
+		fprintf(stderr, "could not set kqueue timer: %s",
+			strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+/** \cond INTERNAL */
+
+struct wl_event_source_signal {
+	struct wl_event_source base;
+	wl_event_loop_signal_func_t func;
+	struct wl_list signal_link;
+};
+
+/** \endcond */
+
+static int
+wl_event_source_signal_dispatch(struct wl_event_source *source,
+				struct kevent *ev)
+{
+	struct wl_event_source_signal *signal_source;
+
+	signal_source = (struct wl_event_source_signal *) source;
+	return signal_source->func(signal_source->base.fd,
+				   signal_source->base.data);
+}
+
+struct wl_event_source_interface signal_source_interface = {
+	wl_event_source_signal_dispatch,
+};
+
+/** Create a POSIX signal event source
+ *
+ * \param loop The event loop that will process the new source.
+ * \param signal_number Number of the signal to watch for.
+ * \param func The signal dispatch function.
+ * \param data User data.
+ * \return A new signal event source.
+ *
+ * This function blocks the normal delivery of the given signal in the calling
+ * thread, and creates a "watch" for it. Signal delivery no longer happens
+ * asynchronously, but by wl_event_loop_dispatch() calling the dispatch
+ * callback function \c func.
+ *
+ * It is the caller's responsibility to ensure that all other threads have
+ * also blocked the signal.
+ *
+ * \sa wl_event_loop_signal_func_t
+ * \memberof wl_event_source
+ */
+static struct wl_event_source_signal *
+wl_event_loop_create_signal_source(int signal_number,
+				   wl_event_loop_signal_func_t func)
+{
+	struct wl_event_source_signal *source;
+	sigset_t mask;
+
+	source = calloc(1, sizeof(struct wl_event_source_signal));
+	if (source == NULL)
+		return NULL;
+
+	source->base.interface = &signal_source_interface;
+	wl_list_init(&source->base.link);
+	source->base.fd = signal_number;
+	source->func = func;
+
+	/* Block delivery of signal_number to this process. */
+	sigemptyset(&mask);
+	sigaddset(&mask, signal_number);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	source->func = func;
+	return source;
+}
+
+static int
+dispatch_signal_event(struct wl_event_loop *loop, int signal_number)
+{
+	struct wl_event_source_signal *ksh, *next;
+	int n;
+
+	n = 0;
+	warnx("SIGNAL %d DISPATCH BEGIN", signal_number);
+	wl_list_for_each_safe(ksh, next, &loop->signals[signal_number], signal_link) {
+		warnx("\t0x%p 0x%p %d", ksh, next, n);
+		n += ksh->func(signal_number, ksh->base.data);
+	}
+	return n;
+}
+
+/* BSD. Use kqueue. Multiple handlers need special treatment. */
+WL_EXPORT struct wl_event_source *
+wl_event_loop_add_signal(struct wl_event_loop *loop,
+			 int signal_number,
+			 wl_event_loop_signal_func_t func,
+			 void *data)
+{
+	struct wl_event_source_signal *source = NULL;;
+	struct kevent ev;
+
+	source = wl_event_loop_create_signal_source(signal_number, func);
+	if (source == NULL)
+		return NULL;
+	source->base.loop = loop;
+	source->base.data = data;
+	if (wl_list_empty(&loop->signals[signal_number])) {
+		/* set up kevent object */
+		EV_SET(&ev, signal_number, EVFILT_SIGNAL, EV_ADD | EV_ENABLE,
+		       0, 0, source);
+		if (kevent(loop->event_fd, &ev, 1, NULL, 0, NULL) < 0) {
+			fprintf(stderr,
+				"error adding handler for signal %i (%p) "
+				"in event loop %p: %s",
+				signal_number, source, loop, strerror(errno));
+			free(source);
+			return NULL;
+		}
+	}
+
+	wl_list_insert(&loop->signals[signal_number], &source->signal_link);
+	return &source->base;
+}
+
+/** \cond INTERNAL */
+
+struct wl_event_source_idle {
+	struct wl_event_source base;
+	wl_event_loop_idle_func_t func;
+};
+
+/** \endcond */
+
+struct wl_event_source_interface idle_source_interface = {
+	NULL,
+};
+
+/** Create an idle task
+ *
+ * \param loop The event loop that will process the new task.
+ * \param func The idle task dispatch function.
+ * \param data User data.
+ * \return A new idle task (an event source).
+ *
+ * Idle tasks are dispatched before wl_event_loop_dispatch() goes to sleep.
+ * See wl_event_loop_dispatch() for more details.
+ *
+ * Idle tasks fire once, and are automatically destroyed right after the
+ * callback function has been called.
+ *
+ * An idle task can be cancelled before the callback has been called by
+ * wl_event_source_remove(). Calling wl_event_source_remove() after or from
+ * within the callback results in undefined behaviour.
+ *
+ * \sa wl_event_loop_idle_func_t
+ * \memberof wl_event_source
+ */
+WL_EXPORT struct wl_event_source *
+wl_event_loop_add_idle(struct wl_event_loop *loop,
+		       wl_event_loop_idle_func_t func,
+		       void *data)
+{
+	struct wl_event_source_idle *source;
+
+	source = malloc(sizeof *source);
+	if (source == NULL)
+		return NULL;
+
+	source->base.interface = &idle_source_interface;
+	source->base.loop = loop;
+	source->base.fd = -1;
+
+	source->func = func;
+	source->base.data = data;
+
+	wl_list_insert(loop->idle_list.prev, &source->base.link);
+
+	return &source->base;
+}
+
+/** Mark event source to be re-checked
+ *
+ * \param source The event source to be re-checked.
+ *
+ * This function permanently marks the event source to be re-checked after
+ * the normal dispatch of sources in wl_event_loop_dispatch(). Re-checking
+ * will keep iterating over all such event sources until the dispatch
+ * function for them all returns zero.
+ *
+ * Re-checking is used on sources that may become ready to dispatch as a
+ * side-effect of dispatching themselves or other event sources, including idle
+ * sources. Re-checking ensures all the incoming events have been fully drained
+ * before wl_event_loop_dispatch() returns.
+ *
+ * \memberof wl_event_source
+ */
+WL_EXPORT void
+wl_event_source_check(struct wl_event_source *source)
+{
+	wl_list_insert(source->loop->check_list.prev, &source->link);
+}
+
+/** Remove an event source from its event loop
+ *
+ * \param source The event source to be removed.
+ * \return Zero.
+ *
+ * The event source is removed from the event loop it was created for,
+ * and is effectively destroyed. This invalidates \c source .
+ * The dispatch function of the source will no longer be called through this
+ * source.
+ *
+ * \memberof wl_event_source
+ */
+WL_EXPORT int
+wl_event_source_remove(struct wl_event_source *source)
+{
+	struct wl_event_loop *loop = source->loop;
+	int ret = 0, saved_errno = 0;
+
+	/*
+	 * Since BSD doesn't treat all event sources as FDs, we need to
+	 * differentiate by source interface.
+	 */
+	if (source->interface == &fd_source_interface && source->fd >= 0) {
+		struct kevent ev[2];
+		int _ret[2], _saved_errno[2];
+
+		/*
+		 * We haven't stored state about the mask used when adding the
+		 * source, so we have to try and remove both READ and WRITE
+		 * filters. One may fail, which is OK. Removal of the source has
+		 * only failed if _both_ kevent() calls fail. We have to do two
+		 * kevent() calls so that we can get independent return values
+		 * for the two kevents.
+		 */
+		EV_SET(&ev[0], source->fd, EVFILT_READ, EV_DELETE, 0, 0,
+		      source);
+		EV_SET(&ev[1], source->fd, EVFILT_WRITE, EV_DELETE, 0, 0,
+		      source);
+
+		_ret[0] = kevent(loop->event_fd, &ev[0], 1, NULL, 0, NULL);
+		_saved_errno[0] = errno;
+		_ret[1] = kevent(loop->event_fd, &ev[1], 1, NULL, 0, NULL);
+		_saved_errno[1] = errno;
+
+		if (_ret[0] >= _ret[1]) {
+			ret = _ret[0];
+			saved_errno = _saved_errno[0];
+		} else {
+			ret = _ret[1];
+			saved_errno = _saved_errno[1];
+		}
+	} else if (source->interface == &timer_source_interface) {
+		struct kevent ev;
+		struct wl_event_source_timer *source_t;
+
+		EV_SET(&ev, source->fd, EVFILT_TIMER, EV_DELETE, 0, 0, source);
+		ret = kevent(loop->event_fd, &ev, 1, NULL, 0, NULL);
+		saved_errno = errno;
+		source_t = (struct wl_event_source_timer*)source;
+		wl_list_remove(&source_t->timer_link);
+	} else if (source->interface == &signal_source_interface) {
+		struct kevent ev;
+		struct wl_event_source_signal *source_s;
+
+		source_s = (struct wl_event_source_signal *) source;
+		wl_list_remove(&source_s->signal_link);
+
+		if (wl_list_empty(&loop->signals[source->fd])) {
+			EV_SET(&ev, source->fd, EVFILT_SIGNAL, EV_DELETE, 0, 0,
+			       source);
+			ret = kevent(loop->event_fd, &ev, 1, NULL, 0, NULL);
+			saved_errno = errno;
+		}
+	}
+
+	/* Handle any errors from kevent() calls. */
+	if (ret < 0) {
+		fprintf(stderr,
+		        "error removing event (%i) from kqueue: %s\n",
+		        source->fd, strerror(saved_errno));
+	}
+
+	/* Tidy up the source. */
+	source->fd = -1;
+
+	wl_list_remove(&source->link);
+	wl_list_insert(&loop->destroy_list, &source->link);
+
+	return 0;
+}
+
+static void
+wl_event_loop_process_destroy_list(struct wl_event_loop *loop)
+{
+	struct wl_event_source *source, *next;
+
+	wl_list_for_each_safe(source, next, &loop->destroy_list, link)
+		free(source);
+
+	wl_list_init(&loop->destroy_list);
+}
+
+/** Create a new event loop context
+ *
+ * \return A new event loop context object.
+ *
+ * This creates a new event loop context. Initially this context is empty.
+ * Event sources need to be explicitly added to it.
+ *
+ * Normally the event loop is run by calling wl_event_loop_dispatch() in
+ * a loop until the program terminates. Alternatively, an event loop can be
+ * embedded in another event loop by its file descriptor, see
+ * wl_event_loop_get_fd().
+ *
+ * \memberof wl_event_loop
+ */
+WL_EXPORT struct wl_event_loop *
+wl_event_loop_create(void)
+{
+	struct wl_event_loop *loop;
+	int i;
+
+	loop = malloc(sizeof *loop);
+	if (loop == NULL)
+		return NULL;
+	loop->event_fd = wl_os_queue_create_cloexec();
+	if (loop->event_fd < 0) {
+		free(loop);
+		return NULL;
+	}
+
+	wl_list_init(&loop->check_list);
+	wl_list_init(&loop->idle_list);
+	wl_list_init(&loop->destroy_list);
+	wl_list_init(&loop->timers);
+	for (i = 0; i < NSIG; i++)
+		wl_list_init(&loop->signals[i]);
+
+	wl_signal_init(&loop->destroy_signal);
+
+	return loop;
+}
+
+/** Destroy an event loop context
+ *
+ * \param loop The event loop to be destroyed.
+ *
+ * This emits the event loop destroy signal, closes the event loop file
+ * descriptor, and frees \c loop.
+ *
+ * If the event loop has existing sources, those cannot be safely removed
+ * afterwards. Therefore one must call wl_event_source_remove() on all
+ * event sources before destroying the event loop context.
+ *
+ * \memberof wl_event_loop
+ */
+WL_EXPORT void
+wl_event_loop_destroy(struct wl_event_loop *loop)
+{
+	wl_signal_emit(&loop->destroy_signal, loop);
+
+	wl_event_loop_process_destroy_list(loop);
+	close(loop->event_fd);
+	free(loop);
+}
+
+static bool
+post_dispatch_check(struct wl_event_loop *loop)
+{
+	struct wl_event_source *source, *next;
+	bool needs_recheck = false;
+
+	struct kevent ev;
+	memset(&ev, 0, sizeof(struct kevent));
+	wl_list_for_each_safe(source, next, &loop->check_list, link) {
+		int dispatch_result;
+
+		dispatch_result = source->interface->dispatch(source, &ev);
+		if (dispatch_result < 0) {
+			wl_log("Source dispatch function returned negative value!");
+			wl_log("This would previously accidentally suppress a follow-up dispatch");
+		}
+		needs_recheck |= dispatch_result != 0;
+	}
+
+	return needs_recheck;
+}
+
+/** Dispatch the idle sources
+ *
+ * \param loop The event loop whose idle sources are dispatched.
+ *
+ * \sa wl_event_loop_add_idle()
+ * \memberof wl_event_loop
+ */
+WL_EXPORT void
+wl_event_loop_dispatch_idle(struct wl_event_loop *loop)
+{
+	struct wl_event_source_idle *source;
+
+	while (!wl_list_empty(&loop->idle_list)) {
+		source = container_of(loop->idle_list.next,
+				      struct wl_event_source_idle, base.link);
+		source->func(source->base.data);
+		wl_event_source_remove(&source->base);
+	}
+}
+
+/** Wait for events and dispatch them
+ *
+ * \param loop The event loop whose sources to wait for.
+ * \param timeout The polling timeout in milliseconds.
+ * \return 0 for success, -1 for polling error.
+ *
+ * All the associated event sources are polled. This function blocks until
+ * any event source delivers an event (idle sources excluded), or the timeout
+ * expires. A timeout of -1 disables the timeout, causing the function to block
+ * indefinitely. A timeout of zero causes the poll to always return immediately.
+ *
+ * All idle sources are dispatched before blocking. An idle source is destroyed
+ * when it is dispatched. After blocking, all other ready sources are
+ * dispatched. Then, idle sources are dispatched again, in case the dispatched
+ * events created idle sources. Finally, all sources marked with
+ * wl_event_source_check() are dispatched in a loop until their dispatch
+ * functions all return zero.
+ *
+ * \memberof wl_event_loop
+ */
+WL_EXPORT int
+wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
+{
+	/* kqueue sends separate events for r/w */
+	struct kevent ev[64];
+	struct wl_event_source *source;
+	int i, count, n;
+	struct timespec timeout_spec;
+
+	wl_event_loop_dispatch_idle(loop);
+
+	/* timeout is provided in milliseconds */
+	timeout_spec.tv_sec = timeout / 1000;
+	timeout_spec.tv_nsec = (timeout % 1000) * 1000000;
+
+	count = kevent(loop->event_fd, NULL, 0, ev, ARRAY_LENGTH(ev),
+		       (timeout != -1) ? &timeout_spec : NULL);
+	if (count < 0)
+		return -1;
+
+	for (i = 0; i < count; i++) {
+		if (ev[i].filter == EVFILT_SIGNAL) {
+			warnx("dispatching signal %d", ev[i].ident);
+			dispatch_signal_event(loop, ev[i].ident);
+		} else {
+			source = ev[i].udata;
+			if (source->fd != -1)
+				source->interface->dispatch(source, &ev[i]);
+		}
+	}
+
+	wl_event_loop_process_destroy_list(loop);
+
+	do {
+		n = post_dispatch_check(loop);
+	} while (n > 0);
+
+	return 0;
+}
+
+/** Get the event loop file descriptor
+ *
+ * \param loop The event loop context.
+ * \return The aggregate file descriptor.
+ *
+ * This function returns the aggregate file descriptor, that represents all
+ * the event sources (idle sources excluded) associated with the given event
+ * loop context. When any event source makes an event available, it will be
+ * reflected in the aggregate file descriptor.
+ *
+ * When the aggregate file descriptor delivers an event, one can call
+ * wl_event_loop_dispatch() on the event loop context to dispatch all the
+ * available events.
+ *
+ * \memberof wl_event_loop
+ */
+WL_EXPORT int
+wl_event_loop_get_fd(struct wl_event_loop *loop)
+{
+	return loop->event_fd;
+}
+
+/** Register a destroy listener for an event loop context
+ *
+ * \param loop The event loop context whose destruction to listen for.
+ * \param listener The listener with the callback to be called.
+ *
+ * \sa wl_listener
+ * \memberof wl_event_loop
+ */
+WL_EXPORT void
+wl_event_loop_add_destroy_listener(struct wl_event_loop *loop,
+				   struct wl_listener *listener)
+{
+	wl_signal_add(&loop->destroy_signal, listener);
+}
+
+/** Get the listener struct for the specified callback
+ *
+ * \param loop The event loop context to inspect.
+ * \param notify The destroy callback to find.
+ * \return The wl_listener registered to the event loop context with
+ * the given callback pointer.
+ *
+ * \memberof wl_event_loop
+ */
+WL_EXPORT struct wl_listener *
+wl_event_loop_get_destroy_listener(struct wl_event_loop *loop,
+				   wl_notify_func_t notify)
+{
+	return wl_signal_get(&loop->destroy_signal, notify);
+}
+#endif
diff --git a/src/wayland-os.c b/src/wayland-os.c
index 93b6f5f..1614c94 100644
--- a/src/wayland-os.c
+++ b/src/wayland-os.c
@@ -25,14 +25,20 @@ 
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#ifdef HAVE_SYS_EPOLL_H
 #include <sys/epoll.h>
+#endif
+#ifdef HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#endif
 
-#include "../config.h"
 #include "wayland-os.h"
 
 static int
@@ -62,26 +68,50 @@  wl_os_socket_cloexec(int domain, int type, int protocol)
 {
 	int fd;
 
+#ifdef SOCK_CLOEXEC
 	fd = socket(domain, type | SOCK_CLOEXEC, protocol);
 	if (fd >= 0)
 		return fd;
 	if (errno != EINVAL)
 		return -1;
+#endif
 
 	fd = socket(domain, type, protocol);
 	return set_cloexec_or_close(fd);
 }
 
+int
+wl_os_socketpair_cloexec(int domain, int type, int protocol, int sv[2])
+{
+       int retval;
+
+#ifdef SOCK_CLOEXEC
+       retval = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv);
+       if (retval >= 0)
+               return retval;
+       if (errno != EINVAL)
+               return -1;
+#endif
+
+       retval = socketpair(domain, type, protocol, sv);
+       if (set_cloexec_or_close(sv[0]) < 0 || set_cloexec_or_close(sv[1]) < 0)
+               retval = -1;
+
+       return retval;
+}
+
 int
 wl_os_dupfd_cloexec(int fd, long minfd)
 {
 	int newfd;
 
+#ifdef F_DUPFD_CLOEXEC
 	newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
 	if (newfd >= 0)
 		return newfd;
 	if (errno != EINVAL)
 		return -1;
+#endif
 
 	newfd = fcntl(fd, F_DUPFD, minfd);
 	return set_cloexec_or_close(newfd);
@@ -123,17 +153,20 @@  wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags)
 {
 	ssize_t len;
 
+#ifdef MSG_CMSG_CLOEXEC
 	len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
 	if (len >= 0)
 		return len;
 	if (errno != EINVAL)
 		return -1;
+#endif
 
 	return recvmsg_cloexec_fallback(sockfd, msg, flags);
 }
 
+#if defined(HAVE_SYS_EPOLL_H)
 int
-wl_os_epoll_create_cloexec(void)
+wl_os_queue_create_cloexec(void)
 {
 	int fd;
 
@@ -148,6 +181,16 @@  wl_os_epoll_create_cloexec(void)
 	fd = epoll_create(1);
 	return set_cloexec_or_close(fd);
 }
+#elif defined(HAVE_SYS_EVENT_H)
+int
+wl_os_queue_create_cloexec(void)
+{
+	int fd;
+
+	fd = kqueue();
+	return set_cloexec_or_close(fd);
+}
+#endif
 
 int
 wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
diff --git a/src/wayland-os.h b/src/wayland-os.h
index f51efaa..321e34d 100644
--- a/src/wayland-os.h
+++ b/src/wayland-os.h
@@ -36,7 +36,7 @@  ssize_t
 wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags);
 
 int
-wl_os_epoll_create_cloexec(void);
+wl_os_queue_create_cloexec(void);
 
 int
 wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c
index 102622c..64e04b0 100644
--- a/tests/os-wrappers-test.c
+++ b/tests/os-wrappers-test.c
@@ -26,6 +26,7 @@ 
 
 #define _GNU_SOURCE
 
+#include <err.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <assert.h>
@@ -38,7 +39,11 @@ 
 #include <stdarg.h>
 #include <fcntl.h>
 #include <stdio.h>
-#include <sys/epoll.h>
+#ifdef HAVE_SYS_EPOLL_H
+# include <sys/epoll.h>
+#else
+# include <sys/event.h>
+#endif
 
 #include "wayland-private.h"
 #include "test-runner.h"
@@ -55,8 +60,15 @@  static int wrapped_calls_fcntl;
 static ssize_t (*real_recvmsg)(int, struct msghdr *, int);
 static int wrapped_calls_recvmsg;
 
+#ifdef HAVE_SYS_EPOLL_H
 static int (*real_epoll_create1)(int);
 static int wrapped_calls_epoll_create1;
+#endif
+
+#if HAVE_SYS_EVENT_H
+static int (*real_kqueue)(void);
+static int wrapped_calls_kqueue;
+#endif
 
 static void
 init_fallbacks(int do_fallbacks)
@@ -65,7 +77,12 @@  init_fallbacks(int do_fallbacks)
 	real_socket = dlsym(RTLD_NEXT, "socket");
 	real_fcntl = dlsym(RTLD_NEXT, "fcntl");
 	real_recvmsg = dlsym(RTLD_NEXT, "recvmsg");
+#ifdef HAVE_SYS_EPOLL_H
 	real_epoll_create1 = dlsym(RTLD_NEXT, "epoll_create1");
+#endif
+#ifdef HAVE_SYS_EVENT_H
+	real_kqueue = dlsym(RTLD_NEXT, "kqueue");
+#endif
 }
 
 __attribute__ ((visibility("default"))) int
@@ -81,6 +98,8 @@  socket(int domain, int type, int protocol)
 	return real_socket(domain, type, protocol);
 }
 
+/* won't work on OpenBSD, since real fcntl have to be called early */
+#if !defined(__OpenBSD__)
 __attribute__ ((visibility("default"))) int
 fcntl(int fd, int cmd, ...)
 {
@@ -100,6 +119,7 @@  fcntl(int fd, int cmd, ...)
 
 	return real_fcntl(fd, cmd, arg);
 }
+#endif
 
 __attribute__ ((visibility("default"))) ssize_t
 recvmsg(int sockfd, struct msghdr *msg, int flags)
@@ -114,6 +134,7 @@  recvmsg(int sockfd, struct msghdr *msg, int flags)
 	return real_recvmsg(sockfd, msg, flags);
 }
 
+#ifdef HAVE_SYS_EPOLL_H
 __attribute__ ((visibility("default"))) int
 epoll_create1(int flags)
 {
@@ -127,6 +148,23 @@  epoll_create1(int flags)
 
 	return real_epoll_create1(flags);
 }
+#endif
+
+#ifdef HAVE_SYS_EVENT_H
+__attribute__ ((visibility("default"))) int
+kqueue(void)
+{
+	wrapped_calls_kqueue++;
+
+	if (fall_back) {
+		wrapped_calls_kqueue++; /* kqueue() not wrapped */
+		errno = EINVAL;
+		return -1;
+	}
+
+	return real_kqueue();
+}
+#endif
 
 static void
 do_os_wrappers_socket_cloexec(int n)
@@ -184,6 +222,7 @@  do_os_wrappers_dupfd_cloexec(int n)
 	 * Must have 4 calls if falling back, but must also allow
 	 * falling back without a forced fallback.
 	 */
+	warnx("wrapped_calls_fcntl is %d, should be larger than %d", wrapped_calls_fcntl, n);
 	assert(wrapped_calls_fcntl > n);
 
 	exec_fd_leak_check(nr_fds);
@@ -335,14 +374,14 @@  TEST(os_wrappers_recvmsg_cloexec_fallback)
 }
 
 static void
-do_os_wrappers_epoll_create_cloexec(int n)
+do_os_wrappers_queue_create_cloexec(int n)
 {
 	int fd;
 	int nr_fds;
 
 	nr_fds = count_open_fds();
 
-	fd = wl_os_epoll_create_cloexec();
+	fd = wl_os_queue_create_cloexec();
 	assert(fd >= 0);
 
 #ifdef EPOLL_CLOEXEC
@@ -354,16 +393,18 @@  do_os_wrappers_epoll_create_cloexec(int n)
 	exec_fd_leak_check(nr_fds);
 }
 
-TEST(os_wrappers_epoll_create_cloexec)
+TEST(os_wrappers_queue_create_cloexec)
 {
 	init_fallbacks(0);
-	do_os_wrappers_epoll_create_cloexec(1);
+	do_os_wrappers_queue_create_cloexec(1);
 }
 
-TEST(os_wrappers_epoll_create_cloexec_fallback)
+TEST(os_wrappers_queue_create_cloexec_fallback)
 {
 	init_fallbacks(1);
-	do_os_wrappers_epoll_create_cloexec(2);
+	do_os_wrappers_queue_create_cloexec(2);
 }
 
 /* FIXME: add tests for wl_os_accept_cloexec() */
+
+/* FIXME: add tests for kqueue() */
-- 
2.20.1


From 60741cd3ba92d6bcb784e4c4fe15e48750029d15 Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 11:53:20 +0200
Subject: [PATCH 6/8] client: define EPROTO if it's not defined

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 src/wayland-client.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/wayland-client.h b/src/wayland-client.h
index 9f70fa3..524f79d 100644
--- a/src/wayland-client.h
+++ b/src/wayland-client.h
@@ -36,6 +36,10 @@ 
 #ifndef WAYLAND_CLIENT_H
 #define WAYLAND_CLIENT_H
 
+#ifndef EPROTO
+# define EPROTO ENOPROTOOPT
+#endif
+
 #include "wayland-client-core.h"
 #include "wayland-client-protocol.h"
 
-- 
2.20.1


From 809256b3a8b3c237f0d2f65575ee80862ac71d26 Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 12:11:49 +0200
Subject: [PATCH 7/8] tests: support waitpid()

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 configure.ac            |  7 ++++++
 tests/test-compositor.c | 23 +++++++++++++++--
 tests/test-runner.c     | 55 ++++++++++++++++++++++++++++++++++++++---
 3 files changed, 80 insertions(+), 5 deletions(-)

diff --git a/configure.ac b/configure.ac
index 941749d..1d1a25b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -85,6 +85,13 @@  if test "x$ac_cv_header_kvm_h" != "x" && test "x$ac_cv_lib_kvm_kvm_getfiles" !=
 	AC_DEFINE(USE_LIBKVM, 1, [use libkvm on BSD])
 fi
 
+# Defines __FreeBSD__ if we're on FreeBSD, same for other *BSD
+AC_CHECK_HEADERS([sys/param.h])
+
+# waitid() and signal.h are needed for the test suite.
+AC_CHECK_FUNCS([waitid])
+AC_CHECK_HEADERS([signal.h])
+
 AC_ARG_ENABLE([libraries],
 	      [AC_HELP_STRING([--disable-libraries],
 			      [Disable compilation of wayland libraries])],
diff --git a/tests/test-compositor.c b/tests/test-compositor.c
index 72f6351..27285ee 100644
--- a/tests/test-compositor.c
+++ b/tests/test-compositor.c
@@ -86,8 +86,8 @@  get_socket_name(void)
 	static char retval[64];
 
 	gettimeofday(&tv, NULL);
-	snprintf(retval, sizeof retval, "wayland-test-%d-%ld%ld",
-		 getpid(), tv.tv_sec, tv.tv_usec);
+	snprintf(retval, sizeof retval, "wayland-test-%d-%lld%lld",
+		 getpid(), (long long)tv.tv_sec, (long long)tv.tv_usec);
 
 	return retval;
 }
@@ -97,10 +97,15 @@  handle_client_destroy(void *data)
 {
 	struct client_info *ci = data;
 	struct display *d;
+#ifdef HAVE_WAITID
 	siginfo_t status;
+#else
+	int istatus;
+#endif
 
 	d = ci->display;
 
+#ifdef HAVE_WAITID
 	assert(waitid(P_PID, ci->pid, &status, WEXITED) != -1);
 
 	switch (status.si_code) {
@@ -118,6 +123,20 @@  handle_client_destroy(void *data)
 		ci->exit_code = status.si_status;
 		break;
 	}
+#else
+	assert(waitpid(ci->pid, &istatus, WNOHANG) != -1);
+
+	if (WIFSIGNALED(istatus)) {
+		fprintf(stderr, "Client '%s' was killed by signal %d\n",
+			ci->name, WTERMSIG(istatus));
+		ci->exit_code = WEXITSTATUS(istatus);
+	} else if (WIFEXITED(istatus)) {
+		if (WEXITSTATUS(istatus) != EXIT_SUCCESS)
+			fprintf(stderr, "Client '%s' exited with code %d\n",
+				ci->name, WEXITSTATUS(istatus));
+		ci->exit_code = WEXITSTATUS(istatus);
+	}
+#endif
 
 	++d->clients_terminated_no;
 	if (d->clients_no == d->clients_terminated_no) {
diff --git a/tests/test-runner.c b/tests/test-runner.c
index 1487dc4..2936a59 100644
--- a/tests/test-runner.c
+++ b/tests/test-runner.c
@@ -25,6 +25,12 @@ 
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,18 +38,30 @@ 
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <string.h>
+#include <signal.h>
 #include <assert.h>
 #include <dlfcn.h>
 #include <errno.h>
 #include <limits.h>
 #include <sys/ptrace.h>
+#ifdef HAVE_SYS_PRCTL_H
 #include <sys/prctl.h>
+#endif
 #ifndef PR_SET_PTRACER
 # define PR_SET_PTRACER 0x59616d61
 #endif
 
 #include "test-runner.h"
 
+extern const struct test __start_test_section, __stop_test_section;
+
+#ifndef __linux__
+/* XXX review ptrace() usage */
+#define PTRACE_ATTACH PT_ATTACH
+#define PTRACE_CONT PT_CONTINUE
+#define PTRACE_DETACH PT_DETACH
+#endif
+
 /* when set to 1, check if tests are not leaking opened files.
  * It is turned on by default. It can be turned off by
  * WAYLAND_TEST_NO_LEAK_CHECK environment variable. */
@@ -51,7 +69,7 @@  int fd_leak_check_enabled;
 
 /* when this var is set to 0, every call to test_set_timeout() is
  * suppressed - handy when debugging the test. Can be set by
- * WAYLAND_TEST_NO_TIMEOUTS environment variable. */
+ * WAYLAND_TESTS_NO_TIMEOUTS evnironment var */
 static int timeouts_enabled = 1;
 
 /* set to one if the output goes to the terminal */
@@ -229,6 +247,10 @@  stderr_reset_color(void)
 static int
 is_debugger_attached(void)
 {
+#ifdef __OpenBSD__
+	/* OpenBSD doesn't allow to trace parent process */
+	return 0;
+#else
 	int status;
 	int rc;
 	pid_t pid;
@@ -239,6 +261,8 @@  is_debugger_attached(void)
 		return 0;
 	}
 
+
+// xxx start here
 	pid = fork();
 	if (pid == -1) {
 		perror("fork");
@@ -259,7 +283,7 @@  is_debugger_attached(void)
 			_exit(1);
 		if (!waitpid(-1, NULL, 0))
 			_exit(1);
-		ptrace(PTRACE_CONT, NULL, NULL);
+		ptrace(PTRACE_CONT, ppid, NULL, NULL);
 		ptrace(PTRACE_DETACH, ppid, NULL, NULL);
 		_exit(0);
 	} else {
@@ -286,6 +310,7 @@  is_debugger_attached(void)
 	}
 
 	return rc;
+#endif
 }
 
 int main(int argc, char *argv[])
@@ -293,7 +318,11 @@  int main(int argc, char *argv[])
 	const struct test *t;
 	pid_t pid;
 	int total, pass;
+#ifdef HAVE_WAITID
 	siginfo_t info;
+#else
+	int status;
+#endif
 
 	if (isatty(fileno(stderr)))
 		is_atty = 1;
@@ -336,7 +365,8 @@  int main(int argc, char *argv[])
 		if (pid == 0)
 			run_test(t); /* never returns */
 
-		if (waitid(P_PID, pid, &info, WEXITED)) {
+#ifdef HAVE_WAITID
+		if (waitid(P_PID, 0, &info, WEXITED)) {
 			stderr_set_color(RED);
 			fprintf(stderr, "waitid failed: %m\n");
 			stderr_reset_color();
@@ -367,6 +397,25 @@  int main(int argc, char *argv[])
 
 			break;
 		}
+#else
+               if (waitpid(-1, &status, 0) == -1) {
+                       fprintf(stderr, "waitpid failed: %s\n",
+                               strerror(errno));
+                       abort();
+               }
+
+               fprintf(stderr, "test \"%s\":\t", t->name);
+               if (WIFEXITED(status)) {
+                       fprintf(stderr, "exit status %d", WEXITSTATUS(status));
+                       if (WEXITSTATUS(status) == EXIT_SUCCESS)
+                               success = 1;
+               } else if (WIFSIGNALED(status)) {
+                       fprintf(stderr, "signal %d", WTERMSIG(status));
+               }
+#endif
+
+		if (t->must_fail)
+			success = !success;
 
 		if (success) {
 			pass++;
-- 
2.20.1


From 264d6a361e867f5c96e7ac36b487a81e71ece456 Mon Sep 17 00:00:00 2001
From: Leonid Bobrov <mazocomp@disroot.org>
Date: Fri, 8 Feb 2019 13:02:42 +0200
Subject: [PATCH 8/8] configure: detect libdl and librt

Signed-off-by: Leonid Bobrov <mazocomp@disroot.org>
---
 Makefile.am  |  6 +++---
 configure.ac | 16 ++++++++++++++++
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 426e20a..b973cbe 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -71,7 +71,7 @@  nodist_include_HEADERS =			\
 	protocol/wayland-client-protocol.h
 
 libwayland_server_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -pthread
-libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la -lrt -lm
+libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la $(RT_LIBS) -lm
 libwayland_server_la_LDFLAGS = -version-info 1:0:1
 libwayland_server_la_SOURCES =			\
 	src/wayland-server.c			\
@@ -84,7 +84,7 @@  nodist_libwayland_server_la_SOURCES =		\
 	protocol/wayland-protocol.c
 
 libwayland_client_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -pthread
-libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la -lrt -lm
+libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la $(RT_LIBS) -lm
 libwayland_client_la_LDFLAGS = -version-info 3:0:3
 libwayland_client_la_SOURCES =			\
 	src/wayland-client.c
@@ -216,7 +216,7 @@  noinst_LTLIBRARIES +=				\
 	libtest-helpers.la
 
 libtest_helpers_la_SOURCES = tests/test-helpers.c
-libtest_helpers_la_LIBADD = -lrt -ldl $(FFI_LIBS) $(KVM_LIBS)
+libtest_helpers_la_LIBADD = $(RT_LIBS) $(DL_LIBS) $(FFI_LIBS) $(KVM_LIBS)
 
 libtest_runner_la_SOURCES =			\
 	tests/test-runner.c			\
diff --git a/configure.ac b/configure.ac
index 1d1a25b..40cbd08 100644
--- a/configure.ac
+++ b/configure.ac
@@ -85,6 +85,22 @@  if test "x$ac_cv_header_kvm_h" != "x" && test "x$ac_cv_lib_kvm_kvm_getfiles" !=
 	AC_DEFINE(USE_LIBKVM, 1, [use libkvm on BSD])
 fi
 
+# *BSD don't have libdl, but they have its functions
+SAVE_LIBS="$LIBS"
+LIBS=
+AC_CHECK_LIB([dl], [dlsym])
+DL_LIBS="$LIBS"
+LIBS="$SAVE_LIBS"
+AC_SUBST(DL_LIBS)
+
+# *BSD don't have librt, but they have its functions
+SAVE_LIBS="$LIBS"
+LIBS=
+AC_CHECK_LIB([rt], [clock_gettime])
+RT_LIBS="$LIBS"
+LIBS="$SAVE_LIBS"
+AC_SUBST(RT_LIBS)
+
 # Defines __FreeBSD__ if we're on FreeBSD, same for other *BSD
 AC_CHECK_HEADERS([sys/param.h])