changeset 2981:17ce5dad11dc draft

Support kqueue(2).
author Roy Marples <roy@marples.name>
date Wed, 04 Mar 2015 15:47:04 +0000
parents c66ed22ed0d6
children b5e381584efe
files configure dhcpcd.c dhcpcd.h eloop.c eloop.h
diffstat 5 files changed, 172 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Wed Mar 04 11:51:55 2015 +0000
+++ b/configure	Wed Mar 04 15:47:04 2015 +0000
@@ -790,6 +790,22 @@
 fi
 
 if [ -z "$POLL" ]; then
+	printf "Testing for kqueue ... "
+	cat <<EOF >_kqueue.c
+#include <sys/event.h>
+int main(void) {
+	return kqueue();
+}
+EOF
+	if $XCC _kqueue.c -o _kqueue 2>&3; then
+		POLL=kqueue
+ 		echo "yes"
+ 	else
+ 		echo "no"
+ 	fi
+	rm -f _kqueue.c _kqueue
+fi
+if [ -z "$POLL" ]; then
 	printf "Testing for epoll ... "
 	cat <<EOF >_epoll.c
 #ifdef __linux__
@@ -853,6 +869,9 @@
 	rm -f _pselect.c _pselect
 fi
 case "$POLL" in
+kqueue)
+	echo "#define HAVE_KQUEUE" >>$CONFIG_H
+	;;
 epoll)
 	echo "#define HAVE_EPOLL" >>$CONFIG_H
 	;;
--- a/dhcpcd.c	Wed Mar 04 11:51:55 2015 +0000
+++ b/dhcpcd.c	Wed Mar 04 15:47:04 2015 +0000
@@ -68,12 +68,12 @@
 
 #ifdef USE_SIGNALS
 const int dhcpcd_handlesigs[] = {
+	SIGTERM,
+	SIGINT,
 	SIGALRM,
 	SIGHUP,
-	SIGINT,
+	SIGUSR1,
 	SIGPIPE,
-	SIGTERM,
-	SIGUSR1,
 	0
 };
 
@@ -1060,14 +1060,10 @@
 }
 
 #ifdef USE_SIGNALS
-struct dhcpcd_siginfo {
-	int signo;
-	pid_t pid;
-} dhcpcd_siginfo;
-
-#define sigmsg "received signal %s from PID %d, %s"
-static void
-handle_signal1(void *arg)
+struct dhcpcd_siginfo dhcpcd_siginfo;
+#define sigmsg "received signal %s, %s"
+void
+dhcpcd_handle_signal(void *arg)
 {
 	struct dhcpcd_ctx *ctx;
 	struct dhcpcd_siginfo *si;
@@ -1080,19 +1076,19 @@
 	exit_code = EXIT_FAILURE;
 	switch (si->signo) {
 	case SIGINT:
-		syslog(LOG_INFO, sigmsg, "INT", (int)si->pid, "stopping");
+		syslog(LOG_INFO, sigmsg, "INT", "stopping");
 		break;
 	case SIGTERM:
-		syslog(LOG_INFO, sigmsg, "TERM", (int)si->pid, "stopping");
+		syslog(LOG_INFO, sigmsg, "TERM", "stopping");
 		exit_code = EXIT_SUCCESS;
 		break;
 	case SIGALRM:
-		syslog(LOG_INFO, sigmsg, "ALRM", (int)si->pid, "releasing");
+		syslog(LOG_INFO, sigmsg, "ALRM", "releasing");
 		do_release = 1;
 		exit_code = EXIT_SUCCESS;
 		break;
 	case SIGHUP:
-		syslog(LOG_INFO, sigmsg, "HUP", (int)si->pid, "rebinding");
+		syslog(LOG_INFO, sigmsg, "HUP", "rebinding");
 		reload_config(ctx);
 		/* Preserve any options passed on the commandline
 		 * when we were started. */
@@ -1100,7 +1096,7 @@
 		    ctx->argc - ctx->ifc);
 		return;
 	case SIGUSR1:
-		syslog(LOG_INFO, sigmsg, "USR1", (int)si->pid, "reconfiguring");
+		syslog(LOG_INFO, sigmsg, "USR1", "reconfiguring");
 		TAILQ_FOREACH(ifp, ctx->ifaces, next) {
 			ipv4_applyaddr(ifp);
 		}
@@ -1110,9 +1106,9 @@
 		return;
 	default:
 		syslog(LOG_ERR,
-		    "received signal %d from PID %d, "
+		    "received signal %d, "
 		    "but don't know what to do with it",
-		    si->signo, (int)si->pid);
+		    si->signo);
 		return;
 	}
 
@@ -1121,39 +1117,44 @@
 	eloop_exit(ctx->eloop, exit_code);
 }
 
+#ifndef HAVE_KQUEUE
 static void
-handle_signal(int sig, siginfo_t *siginfo, __unused void *context)
+handle_signal(int sig, __unused siginfo_t *siginfo, __unused void *context)
 {
 
 	/* So that we can operate safely under a signal we instruct
 	 * eloop to pass a copy of the siginfo structure to handle_signal1
 	 * as the very first thing to do. */
 	dhcpcd_siginfo.signo = sig;
-	dhcpcd_siginfo.pid = siginfo ? siginfo->si_pid : 0;
 	eloop_timeout_add_now(dhcpcd_ctx->eloop,
-	    handle_signal1, &dhcpcd_siginfo);
+	    dhcpcd_handle_signal, &dhcpcd_siginfo);
 }
+#endif
 
 static int
 signal_init(sigset_t *oldset)
 {
-	unsigned int i;
+	sigset_t newset;
+#ifndef HAVE_KQUEUE
+	int i;
 	struct sigaction sa;
-	sigset_t newset;
+#endif
 
 	sigfillset(&newset);
 	if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
 		return -1;
 
+#ifndef HAVE_KQUEUE
 	memset(&sa, 0, sizeof(sa));
 	sa.sa_sigaction = handle_signal;
 	sa.sa_flags = SA_SIGINFO;
 	sigemptyset(&sa.sa_mask);
 
-	for (i = 0; dhcpcd_handlesigs[i]; i++) {
+	for (i = 0; i < dhcpcd_handlesigs[i]; i++) {
 		if (sigaction(dhcpcd_handlesigs[i], &sa, NULL) == -1)
 			return -1;
 	}
+#endif
 	return 0;
 }
 #endif
--- a/dhcpcd.h	Wed Mar 04 11:51:55 2015 +0000
+++ b/dhcpcd.h	Wed Mar 04 15:47:04 2015 +0000
@@ -150,7 +150,12 @@
 };
 
 #ifdef USE_SIGNALS
+struct dhcpcd_siginfo {
+	int signo;
+};
+
 extern const int dhcpcd_handlesigs[];
+void dhcpcd_handle_signal(void *);
 #endif
 
 int dhcpcd_oneup(struct dhcpcd_ctx *);
--- a/eloop.c	Wed Mar 04 11:51:55 2015 +0000
+++ b/eloop.c	Wed Mar 04 15:47:04 2015 +0000
@@ -42,7 +42,16 @@
 #include "dhcpcd.h"
 #include "eloop.h"
 
-#if defined(HAVE_EPOLL)
+#if defined(HAVE_KQUEUE)
+#include <sys/event.h>
+#ifdef __NetBSD__
+/* udata is void * except on NetBSD */
+#define UPTR(x) ((intptr_t)(x))
+#else
+#define UPTR(x) (x)
+#endif
+#define eloop_event_setup_fds(ctx)
+#elif defined(HAVE_EPOLL)
 #include <sys/epoll.h>
 #define eloop_event_setup_fds(ctx)
 #else
@@ -74,7 +83,9 @@
     void (*write_cb)(void *), void *write_cb_arg)
 {
 	struct eloop_event *e;
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+	struct kevent ke[2], *nfds;
+#elif HAVE_EPOLL
 	struct epoll_event epe, *nfds;
 #else
 	struct pollfd *nfds;
@@ -99,7 +110,16 @@
 				e->write_cb = write_cb;
 				e->write_cb_arg = write_cb_arg;
 			}
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+			EV_SET(&ke[0], fd, EVFILT_READ, EV_ADD, 0, 0, UPTR(e));
+			if (write_cb)
+				EV_SET(&ke[1], fd, EVFILT_WRITE, EV_ADD,
+				    0, 0, UPTR(e));
+			if (kevent(ctx->kqueue_fd, ke, write_cb ? 2 : 1,
+			    NULL, 0, NULL) == -1)
+				goto err;
+			return 0;
+#elif HAVE_EPOLL
 			epe.data.ptr = e;
 			return epoll_ctl(ctx->epoll_fd, EPOLL_CTL_MOD,
 			    fd, &epe);
@@ -129,7 +149,6 @@
 		ctx->fds = nfds;
 	}
 
-
 	/* Now populate the structure and add it to the list */
 	e->fd = fd;
 	e->read_cb = read_cb;
@@ -137,7 +156,13 @@
 	e->write_cb = write_cb;
 	e->write_cb_arg = write_cb_arg;
 
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+	EV_SET(&ke[0], fd, EVFILT_READ, EV_ADD, 0, 0, UPTR(e));
+	if (write_cb)
+		EV_SET(&ke[1], fd, EVFILT_WRITE, EV_ADD, 0, 0, UPTR(e));
+	if (kevent(ctx->kqueue_fd, ke, write_cb ? 2 : 1, NULL, 0, NULL) == -1)
+		goto err;
+#elif HAVE_EPOLL
 	epe.data.ptr = e;
 	if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, fd, &epe) == -1)
 		goto err;
@@ -166,15 +191,34 @@
 eloop_event_delete(struct eloop_ctx *ctx, int fd, int write_only)
 {
 	struct eloop_event *e;
+#ifdef HAVE_KQUEUE
+	struct kevent ke;
+#endif
 
 	TAILQ_FOREACH(e, &ctx->events, next) {
 		if (e->fd == fd) {
 			if (write_only) {
-				e->write_cb = NULL;
-				e->write_cb_arg = NULL;
+				if (e->write_cb) {
+					e->write_cb = NULL;
+					e->write_cb_arg = NULL;
+#ifdef HAVE_KQUEUE
+					EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE,
+					    0, 0, UPTR(NULL));
+					kevent(ctx->kqueue_fd, &ke, 1, NULL, 0,
+					    NULL);
+#endif
+				}
+
 			} else {
 				TAILQ_REMOVE(&ctx->events, e, next);
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+				EV_SET(&ke, fd, EVFILT_READ, EV_DELETE,
+				    0, 0, UPTR(NULL));
+				kevent(ctx->kqueue_fd, &ke, 1, NULL, 0, NULL);
+				EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE,
+				    0, 0, UPTR(NULL));
+				kevent(ctx->kqueue_fd, &ke, 1, NULL, 0, NULL);
+#elif HAVE_EPOLL
 				/* NULL event is safe because we
 				 * rely on epoll_pwait which as added
 				 * after the delete without event was fixed. */
@@ -299,6 +343,9 @@
 {
 	struct eloop_ctx *ctx;
 	struct timespec now;
+#ifdef HAVE_KQUEUE
+	int i;
+#endif
 
 	/* Check we have a working monotonic clock. */
 	if (get_monotonic(&now) == -1)
@@ -311,7 +358,32 @@
 		TAILQ_INIT(&ctx->timeouts);
 		TAILQ_INIT(&ctx->free_timeouts);
 		ctx->exitcode = EXIT_FAILURE;
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+		if ((ctx->kqueue_fd = kqueue()) == -1) {
+			free(ctx);
+			return NULL;
+		}
+		/* There is no sigmask parameter to kqueue, instead
+		 * we have to use it's filters. */
+		ctx->fds_len = 0;
+		while ((int)ctx->fds_len < dhcpcd_handlesigs[ctx->fds_len])
+			ctx->fds_len++;
+		ctx->fds = malloc(ctx->fds_len * sizeof(*ctx->fds));
+		if (ctx->fds == NULL) {
+			free(ctx);
+			return NULL;
+		}
+		for (i = 0; i < dhcpcd_handlesigs[i]; i++)
+			EV_SET(&ctx->fds[i], dhcpcd_handlesigs[i],
+			    EVFILT_SIGNAL, EV_ADD, 0, 0, UPTR(NULL));
+		if (kevent(ctx->kqueue_fd, ctx->fds, ctx->fds_len,
+		    NULL, 0, NULL) == -1)
+		{
+			free(ctx->fds);
+			free(ctx);
+			return NULL;
+		}
+#elif HAVE_EPOLL
 		if ((ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) == -1) {
 			free(ctx);
 			return NULL;
@@ -332,6 +404,12 @@
 	if (ctx == NULL)
 		return;
 
+#ifdef HAVE_KQUEUE
+	close(ctx->kqueue_fd);
+#elif HAVE_EPOLL
+	close(ctx->epoll_fd);
+#endif
+
 	while ((e = TAILQ_FIRST(&ctx->events))) {
 		TAILQ_REMOVE(&ctx->events, e, next);
 		free(e);
@@ -364,7 +442,7 @@
 #if defined(HAVE_EPOLL) || !defined(USE_SIGNALS)
 	int timeout;
 #endif
-#ifdef HAVE_EPOLL
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
 	int i;
 #endif
 
@@ -411,7 +489,10 @@
 			    (tsp->tv_nsec + 999999) / 1000000);
 #endif
 
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+		n = kevent(ctx->kqueue_fd, NULL, 0, ctx->fds, ctx->events_len,
+		    tsp);
+#elif HAVE_EPOLL
 #ifdef USE_SIGNALS
 		n = epoll_pwait(ctx->epoll_fd, ctx->fds, (int)ctx->events_len,
 		    timeout, &dctx->sigset);
@@ -434,27 +515,43 @@
 			break;
 		}
 
-		/* Process any triggered events. */
-#ifdef HAVE_EPOLL
+		/* Process any triggered events.
+		 * We break after calling each callback incase
+		 * the current event or next event is removed. */
+#ifdef HAVE_KQUEUE
+		for (i = 0; i < n; i++) {
+			if (ctx->fds[i].filter == EVFILT_SIGNAL) {
+				struct dhcpcd_siginfo si;
+
+				si.signo = (int)ctx->fds[i].ident;
+				dhcpcd_handle_signal(&si);
+				break;
+			}
+			e = (struct eloop_event *)ctx->fds[i].udata;
+			if (ctx->fds[i].filter == EVFILT_WRITE &&
+			    e->write_cb)
+			{
+				e->write_cb(e->write_cb_arg);
+				break;
+			}
+			if (ctx->fds[i].filter == EVFILT_READ) {
+				e->read_cb(e->read_cb_arg);
+				break;
+			}
+		}
+#elif HAVE_EPOLL
 		for (i = 0; i < n; i++) {
 			e = (struct eloop_event *)ctx->fds[i].data.ptr;
 			if (ctx->fds[i].events & EPOLLOUT &&
 			    e->write_cb)
 			{
 				e->write_cb(e->write_cb_arg);
-				/* We need to break here as the
-				 * callback could destroy the next
-				 * fd to process. */
 				break;
 			}
-			if (ctx->fds[i].events &&
-			    ctx->fds[i].events &
+			if (ctx->fds[i].events &
 			    (EPOLLIN | EPOLLERR | EPOLLHUP))
 			{
 				e->read_cb(e->read_cb_arg);
-				/* We need to break here as the
-				 * callback could destroy the next
-				 * fd to process. */
 				break;
 			}
 		}
@@ -465,16 +562,10 @@
 				    e->write_cb)
 				{
 					e->write_cb(e->write_cb_arg);
-					/* We need to break here as the
-					 * callback could destroy the next
-					 * fd to process. */
 					break;
 				}
 				if (e->pollfd->revents) {
 					e->read_cb(e->read_cb_arg);
-					/* We need to break here as the
-					 * callback could destroy the next
-					 * fd to process. */
 					break;
 				}
 			}
--- a/eloop.h	Wed Mar 04 11:51:55 2015 +0000
+++ b/eloop.h	Wed Mar 04 15:47:04 2015 +0000
@@ -47,7 +47,7 @@
 	void *read_cb_arg;
 	void (*write_cb)(void *);
 	void *write_cb_arg;
-#if !defined(HAVE_EPOLL)
+#if !defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL)
 	struct pollfd *pollfd;
 #endif
 };
@@ -71,7 +71,10 @@
 	void (*timeout0)(void *);
 	void *timeout0_arg;
 
-#ifdef HAVE_EPOLL
+#ifdef HAVE_KQUEUE
+	int kqueue_fd;
+	struct kevent *fds;
+#elif HAVE_EPOLL
 	int epoll_fd;
 	struct epoll_event *fds;
 #else