Import new eloop from dhcpcd
authorRoy Marples <roy@marples.name>
Tue, 12 May 2015 20:22:57 +0000 (20:22 +0000)
committerRoy Marples <roy@marples.name>
Tue, 12 May 2015 20:22:57 +0000 (20:22 +0000)
src/dhcpcd-curses/dhcpcd-curses.c
src/dhcpcd-curses/dhcpcd-curses.h
src/dhcpcd-curses/eloop.c
src/dhcpcd-curses/eloop.h

index 3584b9b57b49ba9c91188e53e73281bf37bccb86..6b7bd0cd90bfc69ba1bb4c6a9475e7b0d8eedb09 100644 (file)
@@ -42,7 +42,7 @@
        void _nc_free_and_exit(void);
 #endif
 
-const int handle_sigs[] = {
+const int sigs[] = {
        SIGHUP,
        SIGINT,
        SIGPIPE,
@@ -51,9 +51,6 @@ const int handle_sigs[] = {
        0
 };
 
-/* Handling signals needs *some* context */
-static struct ctx *_ctx;
-
 static void try_open(void *);
 
 static void
@@ -197,7 +194,7 @@ dispatch(void *arg)
 
        if (dhcpcd_get_fd(ctx->con) == -1) {
                warning(ctx, _("dhcpcd connection lost"));
-               eloop_event_delete(ctx->eloop, -1, NULL, ctx->con, 0);
+               eloop_event_delete(ctx->eloop, ctx->fd, 0);
                eloop_timeout_add_msec(ctx->eloop, DHCPCD_RETRYOPEN,
                    try_open, ctx);
                return;
@@ -229,6 +226,8 @@ try_open(void *arg)
        }
 
 unprived:
+       last_error = 0;
+
        /* Start listening to WPA events */
        dhcpcd_wpa_start(ctx->con);
 
@@ -245,7 +244,7 @@ status_cb(DHCPCD_CONNECTION *con,
        set_status(ctx, status_msg);
 
        if (status == DHC_DOWN) {
-               eloop_event_delete(ctx->eloop, ctx->fd, NULL, NULL, 0);
+               eloop_event_delete(ctx->eloop, ctx->fd, 0);
                ctx->fd = -1;
                ctx->online = ctx->carrier = false;
                eloop_timeout_delete(ctx->eloop, NULL, ctx);
@@ -306,7 +305,6 @@ wpa_dispatch(void *arg)
        dhcpcd_wpa_dispatch(wpa);
 }
 
-
 static void
 wpa_scan_cb(DHCPCD_WPA *wpa, void *arg)
 {
@@ -397,6 +395,8 @@ wpa_scan_cb(DHCPCD_WPA *wpa, void *arg)
        }
 }
 
+/*
+ * XXXX Fixme, ideally in the wpa_dispatch
 static void
 wpa_status_cb(DHCPCD_WPA *wpa,
     unsigned int status, const char *status_msg, void *arg)
@@ -418,6 +418,7 @@ wpa_status_cb(DHCPCD_WPA *wpa,
                }
        }
 }
+*/
 
 static void
 bg_scan(void *arg)
@@ -441,8 +442,9 @@ bg_scan(void *arg)
 }
 
 static void
-signal_handler(int sig)
+signal_cb(int sig, void *arg)
 {
+       struct ctx *ctx = arg;
        struct winsize ws;
 
        switch(sig) {
@@ -451,39 +453,23 @@ signal_handler(int sig)
                        resizeterm(ws.ws_row, ws.ws_col);
                break;
        case SIGINT:
-               debug(_ctx, _("SIGINT caught, exiting"));
-               eloop_exit(_ctx->eloop, EXIT_FAILURE);
+               debug(ctx, _("SIGINT caught, exiting"));
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
                break;
        case SIGTERM:
-               debug(_ctx, _("SIGTERM caught, exiting"));
-               eloop_exit(_ctx->eloop, EXIT_FAILURE);
+               debug(ctx, _("SIGTERM caught, exiting"));
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
                break;
        case SIGHUP:
-               debug(_ctx, _("SIGHUP caught, ignoring"));
+               debug(ctx, ("SIGHUP caught, ignoring"));
                break;
        case SIGPIPE:
-               debug(_ctx, _("SIGPIPE caught, ignoring"));
+               debug(ctx, ("SIGPIPE caught, ignoring"));
                break;
        }
 }
 
 static int
-setup_signals()
-{
-       struct sigaction sa;
-       int i;
-
-       memset(&sa, 0, sizeof(sa));
-       sa.sa_handler = signal_handler;
-
-       for (i = 0; handle_sigs[i]; i++) {
-               if (sigaction(handle_sigs[i], &sa, NULL) == -1)
-                       return -1;
-       }
-       return 0;
-}
-
-static int
 create_windows(struct ctx *ctx)
 {
        int h, w;
@@ -522,21 +508,22 @@ main(void)
 {
        struct ctx ctx;
        WI_SCAN *wi;
+       sigset_t sigmask;
 
        memset(&ctx, 0, sizeof(ctx));
        ctx.fd = -1;
        TAILQ_INIT(&ctx.wi_scans);
-       _ctx = &ctx;
 
-       if (setup_signals() == -1)
-               err(EXIT_FAILURE, "setup_signals");
+       if ((ctx.eloop = eloop_new()) == NULL)
+               err(EXIT_FAILURE, "eloop_new");
+       if (eloop_signal_set_cb(ctx.eloop, sigs, signal_cb, &ctx) == -1)
+               err(EXIT_FAILURE, "eloop_signal_set_cb");
+       if (eloop_signal_mask(ctx.eloop, &sigmask) == -1)
+               err(EXIT_FAILURE, "eloop_signal_mask");
 
        if ((ctx.con = dhcpcd_new()) == NULL)
                err(EXIT_FAILURE, "dhcpcd_new");
 
-       if ((ctx.eloop = eloop_init()) == NULL)
-               err(EXIT_FAILURE, "malloc");
-
        if ((ctx.stdscr = initscr()) == NULL)
                err(EXIT_FAILURE, "initscr");
 
@@ -552,12 +539,12 @@ main(void)
        dhcpcd_set_status_callback(ctx.con, status_cb, &ctx);
        dhcpcd_set_if_callback(ctx.con, if_cb, &ctx);
        dhcpcd_wpa_set_scan_callback(ctx.con, wpa_scan_cb, &ctx);
-       dhcpcd_wpa_set_status_callback(ctx.con, wpa_status_cb, &ctx);
+       //dhcpcd_wpa_set_status_callback(ctx.con, wpa_status_cb, &ctx);
 
        eloop_timeout_add_sec(ctx.eloop, 0, try_open, &ctx);
        eloop_timeout_add_msec(ctx.eloop, DHCPCD_WPA_SCAN_SHORT,
            bg_scan, &ctx);
-       eloop_start(ctx.eloop);
+       eloop_start(ctx.eloop, &sigmask);
 
        /* Un-resgister the callbacks to avoid spam on close */
        dhcpcd_set_status_callback(ctx.con, NULL, NULL);
index c94d09c76699fbcd1db17d872987d192d6fb0416..7bc8b149e64931933cac333064e5476ee54a330f 100644 (file)
@@ -58,7 +58,7 @@ typedef struct wi_scan {
 typedef TAILQ_HEAD(wi_scan_head, wi_scan) WI_SCANS;
 
 struct ctx {
-       ELOOP_CTX *eloop;
+       struct eloop *eloop;
        DHCPCD_CONNECTION *con;
        int fd;
        bool online;
index f70f04ef36262824c7e527dfc59d239c6f07a14c..85e65a98f14d65736db16d6483626e2ce0b4af99 100644 (file)
 
 #include <sys/time.h>
 
+#include <assert.h>
 #include <errno.h>
 #include <limits.h>
-#include <poll.h>
 #include <signal.h>
 #include <stdlib.h>
-#include <syslog.h>
-
-#include <stdio.h>
-
-#define IN_ELOOP
+#include <string.h>
+#include <unistd.h>
 
+/* config.h should define HAVE_KQUEUE, HAVE_EPOLL, etc */
 #include "config.h"
-#include "common.h"
 #include "eloop.h"
 
-/* Handy function to get the time.
- * We only care about time advancements, not the actual time itself
- * Which is why we use CLOCK_MONOTONIC, but it is not available on all
- * platforms.
- */
-#define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
-static int
-get_monotonic(struct timespec *ts)
-{
-
-#if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
-       return clock_gettime(CLOCK_MONOTONIC, ts);
-               return 0;
-#elif defined(__APPLE__)
-       /* We can use mach kernel functions here.
-        * This is crap though - why can't they implement clock_gettime?*/
-       static struct mach_timebase_info info = { 0, 0 };
-       static double factor = 0.0;
-       uint64_t nano;
-       long rem;
-
-       if (!posix_clock_set) {
-               if (mach_timebase_info(&info) == KERN_SUCCESS) {
-                       factor = (double)info.numer / (double)info.denom;
-                       clock_monotonic = posix_clock_set = 1;
-               }
-       }
-       if (clock_monotonic) {
-               nano = mach_absolute_time();
-               if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
-                       nano *= factor;
-               ts->tv_sec = nano / NSEC_PER_SEC;
-               ts->tv_nsec = nano % NSEC_PER_SEC;
-               if (ts->tv_nsec < 0) {
-                       ts->tv_sec--;
-                       ts->tv_nsec += NSEC_PER_SEC;
-               }
-               return 0;
-       }
+#ifndef UNUSED
+#define UNUSED(a) (void)((a))
+#endif
+#ifndef __unused
+#ifdef __GNUC__
+#define __unused   __attribute__((__unused__))
+#else
+#define __unused
 #endif
-
-#if 0
-       /* Something above failed, so fall back to gettimeofday */
-       if (!posix_clock_set) {
-               syslog(LOG_WARNING, NO_MONOTONIC);
-               posix_clock_set = 1;
-       }
 #endif
-       {
-               struct timeval tv;
-               if (gettimeofday(&tv, NULL) == 0) {
-                       TIMEVAL_TO_TIMESPEC(&tv, ts);
-                       return 0;
-               }
-       }
 
-       return -1;
-}
+#ifndef MSEC_PER_SEC
+#define MSEC_PER_SEC   1000L
+#define NSEC_PER_MSEC  1000000L
+#endif
 
+#if defined(HAVE_KQUEUE)
+#include <sys/event.h>
+#include <fcntl.h>
+#ifdef __NetBSD__
+/* udata is void * except on NetBSD
+ * lengths are int except on NetBSD */
+#define UPTR(x)        ((intptr_t)(x))
+#define LENC(x)        (x)
+#else
+#define UPTR(x)        (x)
+#define LENC(x)        ((int)(x))
+#endif
+#define eloop_event_setup_fds(eloop)
+#elif defined(HAVE_EPOLL)
+#include <sys/epoll.h>
+#define eloop_event_setup_fds(eloop)
+#else
+#include <poll.h>
 static void
-eloop_event_setup_fds(ELOOP_CTX *ctx)
+eloop_event_setup_fds(struct eloop *eloop)
 {
        struct eloop_event *e;
        size_t i;
 
        i = 0;
-       TAILQ_FOREACH(e, &ctx->events, next) {
-               ctx->fds[i].fd = e->fd;
-               ctx->fds[i].events = 0;
+       TAILQ_FOREACH(e, &eloop->events, next) {
+               eloop->fds[i].fd = e->fd;
+               eloop->fds[i].events = 0;
                if (e->read_cb)
-                       ctx->fds[i].events |= POLLIN;
+                       eloop->fds[i].events |= POLLIN;
                if (e->write_cb)
-                       ctx->fds[i].events |= POLLOUT;
-               ctx->fds[i].revents = 0;
-               e->pollfd = &ctx->fds[i];
+                       eloop->fds[i].events |= POLLOUT;
+               eloop->fds[i].revents = 0;
+               e->pollfd = &eloop->fds[i];
                i++;
        }
 }
 
+#ifndef pollts
+/* Wrapper around pselect, to imitate the NetBSD pollts call. */
+static int
+pollts(struct pollfd * fds, nfds_t nfds,
+    const struct timespec *ts, const sigset_t *sigmask)
+{
+       fd_set read_fds;
+       nfds_t n;
+       int maxfd, r;
+
+       FD_ZERO(&read_fds);
+       maxfd = 0;
+       for (n = 0; n < nfds; n++) {
+               if (fds[n].events & POLLIN) {
+                       FD_SET(fds[n].fd, &read_fds);
+                       if (fds[n].fd > maxfd)
+                               maxfd = fds[n].fd;
+               }
+       }
+
+       r = pselect(maxfd + 1, &read_fds, NULL, NULL, ts, sigmask);
+       if (r > 0) {
+               for (n = 0; n < nfds; n++) {
+                       fds[n].revents =
+                           FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0;
+               }
+       }
+
+       return r;
+}
+#endif
+#endif
+
 int
-eloop_event_add(ELOOP_CTX *ctx, int fd,
+eloop_event_add(struct eloop *eloop, int fd,
     void (*read_cb)(void *), void *read_cb_arg,
     void (*write_cb)(void *), void *write_cb_arg)
 {
        struct eloop_event *e;
+#if defined(HAVE_KQUEUE)
+       struct kevent ke[2];
+#elif defined(HAVE_EPOLL)
+       struct epoll_event epe;
+#else
        struct pollfd *nfds;
+#endif
+
+#ifdef HAVE_EPOLL
+       memset(&epe, 0, sizeof(epe));
+       epe.data.fd = fd;
+       epe.events = EPOLLIN;
+       if (write_cb)
+               epe.events |= EPOLLOUT;
+#endif
 
        /* We should only have one callback monitoring the fd */
-       TAILQ_FOREACH(e, &ctx->events, next) {
+       TAILQ_FOREACH(e, &eloop->events, next) {
                if (e->fd == fd) {
+                       int error;
+
+#if defined(HAVE_KQUEUE)
+                       EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ, EV_ADD,
+                           0, 0, UPTR(e));
+                       if (write_cb)
+                               EV_SET(&ke[1], (uintptr_t)fd, EVFILT_WRITE,
+                                   EV_ADD, 0, 0, UPTR(e));
+                       else if (e->write_cb)
+                               EV_SET(&ke[1], (uintptr_t)fd, EVFILT_WRITE,
+                                   EV_DELETE, 0, 0, UPTR(e));
+                       error = kevent(eloop->poll_fd, ke,
+                           e->write_cb || write_cb ? 2 : 1, NULL, 0, NULL);
+#elif defined(HAVE_EPOLL)
+                       epe.data.ptr = e;
+                       error = epoll_ctl(eloop->poll_fd, EPOLL_CTL_MOD,
+                           fd, &epe);
+#else
+                       error = 0;
+#endif
                        if (read_cb) {
                                e->read_cb = read_cb;
                                e->read_cb_arg = read_cb_arg;
@@ -140,37 +179,32 @@ eloop_event_add(ELOOP_CTX *ctx, int fd,
                                e->write_cb = write_cb;
                                e->write_cb_arg = write_cb_arg;
                        }
-                       eloop_event_setup_fds(ctx);
-                       return 0;
+                       eloop_event_setup_fds(eloop);
+                       return error;
                }
        }
 
        /* Allocate a new event if no free ones already allocated */
-       if ((e = TAILQ_FIRST(&ctx->free_events))) {
-               TAILQ_REMOVE(&ctx->free_events, e, next);
+       if ((e = TAILQ_FIRST(&eloop->free_events))) {
+               TAILQ_REMOVE(&eloop->free_events, e, next);
        } else {
                e = malloc(sizeof(*e));
-               if (e == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       return -1;
-               }
+               if (e == NULL)
+                       goto err;
        }
 
        /* Ensure we can actually listen to it */
-       ctx->events_len++;
-       if (ctx->events_len > ctx->fds_len) {
-               ctx->fds_len += 5;
-               nfds = malloc(sizeof(*ctx->fds) * (ctx->fds_len + 5));
-               if (nfds == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       ctx->events_len--;
-                       TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
-                       return -1;
-               }
-               ctx->fds_len += 5;
-               free(ctx->fds);
-               ctx->fds = nfds;
+       eloop->events_len++;
+#if !defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL)
+       if (eloop->events_len > eloop->fds_len) {
+               nfds = realloc(eloop->fds,
+                   sizeof(*eloop->fds) * (eloop->fds_len + 5));
+               if (nfds == NULL)
+                       goto err;
+               eloop->fds_len += 5;
+               eloop->fds = nfds;
        }
+#endif
 
        /* Now populate the structure and add it to the list */
        e->fd = fd;
@@ -178,50 +212,105 @@ eloop_event_add(ELOOP_CTX *ctx, int fd,
        e->read_cb_arg = read_cb_arg;
        e->write_cb = write_cb;
        e->write_cb_arg = write_cb_arg;
+
+#if defined(HAVE_KQUEUE)
+       EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ, EV_ADD, 0, 0, UPTR(e));
+       if (write_cb)
+               EV_SET(&ke[1], (uintptr_t)fd, EVFILT_WRITE,
+                   EV_ADD, 0, 0, UPTR(e));
+       if (kevent(eloop->poll_fd, ke, write_cb ? 2 : 1, NULL, 0, NULL) == -1)
+               goto err;
+#elif defined(HAVE_EPOLL)
+       epe.data.ptr = e;
+       if (epoll_ctl(eloop->poll_fd, EPOLL_CTL_ADD, fd, &epe) == -1)
+               goto err;
+#endif
+
        /* The order of events should not matter.
         * However, some PPP servers love to close the link right after
         * sending their final message. So to ensure dhcpcd processes this
         * message (which is likely to be that the DHCP addresses are wrong)
         * we insert new events at the queue head as the link fd will be
         * the first event added. */
-       TAILQ_INSERT_HEAD(&ctx->events, e, next);
-       eloop_event_setup_fds(ctx);
+       TAILQ_INSERT_HEAD(&eloop->events, e, next);
+       eloop_event_setup_fds(eloop);
        return 0;
+
+err:
+       if (e) {
+               eloop->events_len--;
+               TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
+       }
+       return -1;
 }
 
 void
-eloop_event_delete(ELOOP_CTX *ctx, int fd, void (*callback)(void *), void *arg,
-    int write_only)
+eloop_event_delete(struct eloop *eloop, int fd, int write_only)
 {
        struct eloop_event *e;
+#if defined(HAVE_KQUEUE)
+       struct kevent ke[2];
+#elif defined(HAVE_EPOLL)
+       struct epoll_event epe;
+#endif
 
-       TAILQ_FOREACH(e, &ctx->events, next) {
-               if (e->fd == fd ||
-                   e->read_cb == callback || (arg && e->read_cb_arg == arg))
-               {
+       TAILQ_FOREACH(e, &eloop->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;
+#if defined(HAVE_KQUEUE)
+                                       EV_SET(&ke[0], (uintptr_t)fd,
+                                           EVFILT_WRITE, EV_DELETE,
+                                           0, 0, UPTR(NULL));
+                                       kevent(eloop->poll_fd, ke, 1, NULL, 0,
+                                           NULL);
+#elif defined(HAVE_EPOLL)
+                                       memset(&epe, 0, sizeof(epe));
+                                       epe.data.fd = e->fd;
+                                       epe.data.ptr = e;
+                                       epe.events = EPOLLIN;
+                                       epoll_ctl(eloop->poll_fd,
+                                           EPOLL_CTL_MOD, fd, &epe);
+#endif
+                               }
+
                        } else {
-                               TAILQ_REMOVE(&ctx->events, e, next);
-                               TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
-                               ctx->events_len--;
+                               TAILQ_REMOVE(&eloop->events, e, next);
+#if defined(HAVE_KQUEUE)
+                               EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ,
+                                   EV_DELETE, 0, 0, UPTR(NULL));
+                               if (e->write_cb)
+                                       EV_SET(&ke[1], (uintptr_t)fd,
+                                           EVFILT_WRITE, EV_DELETE,
+                                           0, 0, UPTR(NULL));
+                               kevent(eloop->poll_fd, ke, e->write_cb ? 2 : 1,
+                                   NULL, 0, NULL);
+#elif defined(HAVE_EPOLL)
+                               /* NULL event is safe because we
+                                * rely on epoll_pwait which as added
+                                * after the delete without event was fixed. */
+                               epoll_ctl(eloop->poll_fd, EPOLL_CTL_DEL,
+                                   fd, NULL);
+#endif
+                               TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
+                               eloop->events_len--;
                        }
-                       eloop_event_setup_fds(ctx);
+                       eloop_event_setup_fds(eloop);
                        break;
                }
        }
 }
 
 int
-eloop_q_timeout_add_tv(ELOOP_CTX *ctx, int queue,
+eloop_q_timeout_add_tv(struct eloop *eloop, int queue,
     const struct timespec *when, void (*callback)(void *), void *arg)
 {
-       struct timespec now;
-       struct timespec w;
+       struct timespec now, w;
        struct eloop_timeout *t, *tt = NULL;
 
-       get_monotonic(&now);
+       clock_gettime(CLOCK_MONOTONIC, &now);
        timespecadd(&now, when, &w);
        /* Check for time_t overflow. */
        if (timespeccmp(&w, &now, <)) {
@@ -230,191 +319,368 @@ eloop_q_timeout_add_tv(ELOOP_CTX *ctx, int queue,
        }
 
        /* Remove existing timeout if present */
-       TAILQ_FOREACH(t, &ctx->timeouts, next) {
+       TAILQ_FOREACH(t, &eloop->timeouts, next) {
                if (t->callback == callback && t->arg == arg) {
-                       TAILQ_REMOVE(&ctx->timeouts, t, next);
+                       TAILQ_REMOVE(&eloop->timeouts, t, next);
                        break;
                }
        }
 
        if (t == NULL) {
                /* No existing, so allocate or grab one from the free pool */
-               if ((t = TAILQ_FIRST(&ctx->free_timeouts))) {
-                       TAILQ_REMOVE(&ctx->free_timeouts, t, next);
+               if ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
+                       TAILQ_REMOVE(&eloop->free_timeouts, t, next);
                } else {
-                       t = malloc(sizeof(*t));
-                       if (t == NULL) {
-                               syslog(LOG_ERR, "%s: %m", __func__);
+                       if ((t = malloc(sizeof(*t))) == NULL)
                                return -1;
-                       }
                }
        }
 
-       t->when = *when;
+       t->when = w;
        t->callback = callback;
        t->arg = arg;
        t->queue = queue;
 
        /* The timeout list should be in chronological order,
         * soonest first. */
-       TAILQ_FOREACH(tt, &ctx->timeouts, next) {
+       TAILQ_FOREACH(tt, &eloop->timeouts, next) {
                if (timespeccmp(&t->when, &tt->when, <)) {
                        TAILQ_INSERT_BEFORE(tt, t, next);
                        return 0;
                }
        }
-       TAILQ_INSERT_TAIL(&ctx->timeouts, t, next);
+       TAILQ_INSERT_TAIL(&eloop->timeouts, t, next);
        return 0;
 }
 
 int
-eloop_q_timeout_add_sec(ELOOP_CTX *ctx, int queue, time_t when,
+eloop_q_timeout_add_sec(struct eloop *eloop, int queue, time_t when,
     void (*callback)(void *), void *arg)
 {
        struct timespec tv;
 
        tv.tv_sec = when;
        tv.tv_nsec = 0;
-       return eloop_q_timeout_add_tv(ctx, queue, &tv, callback, arg);
+       return eloop_q_timeout_add_tv(eloop, queue, &tv, callback, arg);
 }
 
 int
-eloop_q_timeout_add_msec(ELOOP_CTX *ctx, int queue, suseconds_t when,
+eloop_q_timeout_add_msec(struct eloop *eloop, int queue, long when,
     void (*callback)(void *), void *arg)
 {
        struct timespec tv;
 
-       tv.tv_sec = 0;
-       tv.tv_nsec = when * MSEC_PER_NSEC;
-       timespecnorm(&tv);
-       return eloop_q_timeout_add_tv(ctx, queue, &tv, callback, arg);
+       tv.tv_sec = when / MSEC_PER_SEC;
+       tv.tv_nsec = (when % MSEC_PER_SEC) * NSEC_PER_MSEC;
+       return eloop_q_timeout_add_tv(eloop, queue, &tv, callback, arg);
 }
 
-int
-eloop_timeout_add_now(ELOOP_CTX *ctx,
+#if !defined(HAVE_KQUEUE)
+static int
+eloop_timeout_add_now(struct eloop *eloop,
     void (*callback)(void *), void *arg)
 {
 
-       if (ctx->timeout0 != NULL) {
-               syslog(LOG_WARNING, "%s: timeout0 already set", __func__);
-               return eloop_q_timeout_add_sec(ctx, 0, 0, callback, arg);
-       }
-
-       ctx->timeout0 = callback;
-       ctx->timeout0_arg = arg;
+       assert(eloop->timeout0 == NULL);
+       eloop->timeout0 = callback;
+       eloop->timeout0_arg = arg;
        return 0;
 }
+#endif
 
 void
-eloop_q_timeout_delete(ELOOP_CTX *ctx, int queue,
+eloop_q_timeout_delete(struct eloop *eloop, int queue,
     void (*callback)(void *), void *arg)
 {
        struct eloop_timeout *t, *tt;
 
-       TAILQ_FOREACH_SAFE(t, &ctx->timeouts, next, tt) {
+       TAILQ_FOREACH_SAFE(t, &eloop->timeouts, next, tt) {
                if ((queue == 0 || t->queue == queue) &&
                    t->arg == arg &&
                    (!callback || t->callback == callback))
                {
-                       TAILQ_REMOVE(&ctx->timeouts, t, next);
-                       TAILQ_INSERT_TAIL(&ctx->free_timeouts, t, next);
+                       TAILQ_REMOVE(&eloop->timeouts, t, next);
+                       TAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);
                }
        }
 }
 
 void
-eloop_exit(ELOOP_CTX *ctx, int code)
+eloop_exit(struct eloop *eloop, int code)
 {
 
-       ctx->exitcode = code;
-       ctx->exitnow = 1;
+       eloop->exitcode = code;
+       eloop->exitnow = 1;
 }
 
-ELOOP_CTX *
-eloop_init(void)
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+static int
+eloop_open(struct eloop *eloop)
 {
-       ELOOP_CTX *ctx;
-
-       ctx = calloc(1, sizeof(*ctx));
-       if (ctx) {
-               TAILQ_INIT(&ctx->events);
-               TAILQ_INIT(&ctx->free_events);
-               TAILQ_INIT(&ctx->timeouts);
-               TAILQ_INIT(&ctx->free_timeouts);
-               ctx->exitcode = EXIT_FAILURE;
+#if defined(HAVE_KQUEUE1)
+       return (eloop->poll_fd = kqueue1(O_CLOEXEC));
+#elif defined(HAVE_KQUEUE)
+       int i;
+
+       if ((eloop->poll_fd = kqueue()) == -1)
+               return -1;
+       if ((i = fcntl(eloop->poll_fd, F_GETFD, 0)) == -1 ||
+           fcntl(eloop->poll_fd, F_SETFD, i | FD_CLOEXEC) == -1)
+       {
+               close(eloop->poll_fd);
+               eloop->poll_fd = -1;
+               return -1;
        }
-       return ctx;
+
+       return eloop->poll_fd;
+#elif defined (HAVE_EPOLL)
+       return (eloop->poll_fd = epoll_create1(EPOLL_CLOEXEC));
+#endif
 }
 
+int
+eloop_requeue(struct eloop *eloop)
+{
+       struct eloop_event *e;
+       int error;
+#if defined(HAVE_KQUEUE)
+       size_t i;
+       struct kevent *ke;
+#elif defined(HAVE_EPOLL)
+       struct epoll_event epe;
+#endif
 
-void eloop_free(ELOOP_CTX *ctx)
+       if (eloop->poll_fd != -1)
+               close(eloop->poll_fd);
+       if (eloop_open(eloop) == -1)
+               return -1;
+#if defined (HAVE_KQUEUE)
+       i = 0;
+       while (eloop->signals[i] != 0)
+               i++;
+       TAILQ_FOREACH(e, &eloop->events, next) {
+               i++;
+               if (e->write_cb)
+                       i++;
+       }
+
+       if ((ke = malloc(sizeof(*ke) * i)) == NULL)
+               return -1;
+
+       for (i = 0; eloop->signals[i] != 0; i++)
+               EV_SET(&ke[i], (uintptr_t)eloop->signals[i],
+                   EVFILT_SIGNAL, EV_ADD, 0, 0, UPTR(NULL));
+
+       TAILQ_FOREACH(e, &eloop->events, next) {
+               EV_SET(&ke[i], (uintptr_t)e->fd, EVFILT_READ,
+                   EV_ADD, 0, 0, UPTR(e));
+               i++;
+               if (e->write_cb) {
+                       EV_SET(&ke[i], (uintptr_t)e->fd, EVFILT_WRITE,
+                           EV_ADD, 0, 0, UPTR(e));
+                       i++;
+               }
+       }
+
+       error =  kevent(eloop->poll_fd, ke, LENC(i), NULL, 0, NULL);
+       free(ke);
+
+#elif defined(HAVE_EPOLL)
+
+       error = 0;
+       TAILQ_FOREACH(e, &eloop->events, next) {
+               memset(&epe, 0, sizeof(epe));
+               epe.data.fd = e->fd;
+               epe.events = EPOLLIN;
+               if (e->write_cb)
+                       epe.events |= EPOLLOUT;
+               epe.data.ptr = e;
+               if (epoll_ctl(eloop->poll_fd, EPOLL_CTL_ADD, e->fd, &epe) == -1)
+                       error = -1;
+       }
+#endif
+
+       return error;
+}
+#endif
+
+int
+eloop_signal_set_cb(struct eloop *eloop,
+    const int *signals, void (*signal_cb)(int, void *), void *signal_cb_ctx)
+{
+
+       assert(eloop);
+       eloop->signals = signals;
+       eloop->signal_cb = signal_cb;
+       eloop->signal_cb_ctx = signal_cb_ctx;
+       return eloop_requeue(eloop);
+}
+
+#ifndef HAVE_KQUEUE
+struct eloop_siginfo {
+       int sig;
+       struct eloop *eloop;
+};
+static struct eloop_siginfo _eloop_siginfo;
+static struct eloop *_eloop;
+
+static void
+eloop_signal1(void *arg)
+{
+       struct eloop_siginfo *si = arg;
+
+       si->eloop->signal_cb(si->sig, si->eloop->signal_cb_ctx);
+}
+
+static void
+eloop_signal3(int sig, __unused siginfo_t *siginfo, __unused void *arg)
+{
+
+       /* 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. */
+       _eloop_siginfo.eloop = _eloop;
+       _eloop_siginfo.sig = sig;
+       eloop_timeout_add_now(_eloop_siginfo.eloop,
+           eloop_signal1, &_eloop_siginfo);
+}
+#endif
+
+int
+eloop_signal_mask(struct eloop *eloop, sigset_t *oldset)
+{
+       sigset_t newset;
+#ifndef HAVE_KQUEUE
+       int i;
+       struct sigaction sa;
+#endif
+
+       sigfillset(&newset);
+       if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
+               return -1;
+
+#ifdef HAVE_KQUEUE
+       UNUSED(eloop);
+#else
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_sigaction = eloop_signal3;
+       sa.sa_flags = SA_SIGINFO;
+       sigemptyset(&sa.sa_mask);
+
+       for (i = 0; eloop->signals[i]; i++) {
+               if (sigaction(eloop->signals[i], &sa, NULL) == -1)
+                       return -1;
+       }
+#endif
+       return 0;
+}
+
+struct eloop *
+eloop_new(void)
+{
+       struct eloop *eloop;
+       struct timespec now;
+
+       /* Check we have a working monotonic clock. */
+       if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+               return NULL;
+
+       eloop = calloc(1, sizeof(*eloop));
+       if (eloop) {
+               TAILQ_INIT(&eloop->events);
+               TAILQ_INIT(&eloop->free_events);
+               TAILQ_INIT(&eloop->timeouts);
+               TAILQ_INIT(&eloop->free_timeouts);
+               eloop->exitcode = EXIT_FAILURE;
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+               eloop->poll_fd = -1;
+#endif
+       }
+
+       return eloop;
+}
+
+void eloop_free(struct eloop *eloop)
 {
        struct eloop_event *e;
        struct eloop_timeout *t;
 
-       if (ctx == NULL)
+       if (eloop == NULL)
                return;
 
-       while ((e = TAILQ_FIRST(&ctx->events))) {
-               TAILQ_REMOVE(&ctx->events, e, next);
+       while ((e = TAILQ_FIRST(&eloop->events))) {
+               TAILQ_REMOVE(&eloop->events, e, next);
                free(e);
        }
-       while ((e = TAILQ_FIRST(&ctx->free_events))) {
-               TAILQ_REMOVE(&ctx->free_events, e, next);
+       while ((e = TAILQ_FIRST(&eloop->free_events))) {
+               TAILQ_REMOVE(&eloop->free_events, e, next);
                free(e);
        }
-       while ((t = TAILQ_FIRST(&ctx->timeouts))) {
-               TAILQ_REMOVE(&ctx->timeouts, t, next);
+       while ((t = TAILQ_FIRST(&eloop->timeouts))) {
+               TAILQ_REMOVE(&eloop->timeouts, t, next);
                free(t);
        }
-       while ((t = TAILQ_FIRST(&ctx->free_timeouts))) {
-               TAILQ_REMOVE(&ctx->free_timeouts, t, next);
+       while ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
+               TAILQ_REMOVE(&eloop->free_timeouts, t, next);
                free(t);
        }
-       free(ctx->fds);
-       free(ctx);
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+       close(eloop->poll_fd);
+#else
+       free(eloop->fds);
+#endif
+       free(eloop);
 }
 
 int
-eloop_start(ELOOP_CTX *ctx)
+eloop_start(struct eloop *eloop, sigset_t *signals)
 {
        int n;
        struct eloop_event *e;
        struct eloop_timeout *t;
-       struct timespec now, ts, tv, *tsp;
+       struct timespec now, ts, *tsp;
        void (*t0)(void *);
+#if defined(HAVE_KQUEUE)
+       struct kevent ke;
+       UNUSED(signals);
+#elif defined(HAVE_EPOLL)
+       struct epoll_event epe;
+#endif
+#ifndef HAVE_KQUEUE
        int timeout;
 
+       _eloop = eloop;
+#endif
+
        for (;;) {
-               if (ctx->exitnow)
+               if (eloop->exitnow)
                        break;
 
                /* Run all timeouts first */
-               if (ctx->timeout0) {
-                       t0 = ctx->timeout0;
-                       ctx->timeout0 = NULL;
-                       t0(ctx->timeout0_arg);
+               if (eloop->timeout0) {
+                       t0 = eloop->timeout0;
+                       eloop->timeout0 = NULL;
+                       t0(eloop->timeout0_arg);
                        continue;
                }
-               if ((t = TAILQ_FIRST(&ctx->timeouts))) {
-                       get_monotonic(&now);
+               if ((t = TAILQ_FIRST(&eloop->timeouts))) {
+                       clock_gettime(CLOCK_MONOTONIC, &now);
                        if (timespeccmp(&now, &t->when, >)) {
-                               TAILQ_REMOVE(&ctx->timeouts, t, next);
+                               TAILQ_REMOVE(&eloop->timeouts, t, next);
                                t->callback(t->arg);
-                               TAILQ_INSERT_TAIL(&ctx->free_timeouts, t, next);
+                               TAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);
                                continue;
                        }
-                       timespecsub(&t->when, &now, &tv);
+                       timespecsub(&t->when, &now, &ts);
                        tsp = &ts;
                } else
                        /* No timeouts, so wait forever */
                        tsp = NULL;
 
-               if (tsp == NULL && ctx->events_len == 0) {
-                       syslog(LOG_ERR, "nothing to do");
+               if (tsp == NULL && eloop->events_len == 0)
                        break;
-               }
 
+#ifndef HAVE_KQUEUE
                if (tsp == NULL)
                        timeout = -1;
                else if (tsp->tv_sec > INT_MAX / 1000 ||
@@ -424,36 +690,80 @@ eloop_start(ELOOP_CTX *ctx)
                else
                        timeout = (int)(tsp->tv_sec * 1000 +
                            (tsp->tv_nsec + 999999) / 1000000);
-               n = poll(ctx->fds, ctx->events_len, timeout);
+#endif
+
+#if defined(HAVE_KQUEUE)
+               n = kevent(eloop->poll_fd, NULL, 0, &ke, 1, tsp);
+#elif defined(HAVE_EPOLL)
+               if (signals)
+                       n = epoll_pwait(eloop->poll_fd, &epe, 1,
+                           timeout, signals);
+               else
+                       n = epoll_wait(eloop->poll_fd, &epe, 1, timeout);
+#else
+               if (signals)
+                       n = pollts(eloop->fds, (nfds_t)eloop->events_len,
+                           tsp, signals);
+               else
+                       n = poll(eloop->fds, (nfds_t)eloop->events_len,
+                           timeout);
+#endif
                if (n == -1) {
                        if (errno == EINTR)
                                continue;
-                       syslog(LOG_ERR, "poll: %m");
-                       break;
+                       return -errno;
                }
 
-               /* Process any triggered events. */
+               /* Process any triggered events.
+                * We go back to the start after calling each callback incase
+                * the current event or next event is removed. */
+#if defined(HAVE_KQUEUE)
+               if (n) {
+                       if (ke.filter == EVFILT_SIGNAL) {
+                               eloop->signal_cb((int)ke.ident,
+                                   eloop->signal_cb_ctx);
+                               continue;
+                       }
+                       e = (struct eloop_event *)ke.udata;
+                       if (ke.filter == EVFILT_WRITE) {
+                               e->write_cb(e->write_cb_arg);
+                               continue;
+                       } else if (ke.filter == EVFILT_READ) {
+                               e->read_cb(e->read_cb_arg);
+                               continue;
+                       }
+               }
+#elif defined(HAVE_EPOLL)
+               if (n) {
+                       e = (struct eloop_event *)epe.data.ptr;
+                       if (epe.events & EPOLLOUT && e->write_cb) {
+                               e->write_cb(e->write_cb_arg);
+                               continue;
+                       }
+                       if (epe.events &
+                           (EPOLLIN | EPOLLERR | EPOLLHUP))
+                       {
+                               e->read_cb(e->read_cb_arg);
+                               continue;
+                       }
+               }
+#else
                if (n > 0) {
-                       TAILQ_FOREACH(e, &ctx->events, next) {
+                       TAILQ_FOREACH(e, &eloop->events, next) {
                                if (e->pollfd->revents & POLLOUT &&
-                                       e->write_cb)
+                                   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;
                                }
                        }
                }
+#endif
        }
 
-       return ctx->exitcode;
+       return eloop->exitcode;
 }
index dc1dd5c3bf493d5bbac9c8bfdcb7a331b8ebe362..5fda52ba1d14dbb0a43bab225569748535232f37 100644 (file)
 
 #include <time.h>
 
-#ifndef ELOOP_QUEUE
-  #define ELOOP_QUEUE 1
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+/* Attempt to autodetect kqueue or epoll.
+ * If we can't, the system has to support pselect, which is a POSIX call. */
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+#if defined(BSD)
+/* Assume BSD has a working sys/queue.h and kqueue(2) interface */
+#define HAVE_SYS_QUEUE_H
+#define HAVE_KQUEUE
+#elif defined(__linux__)
+/* Assume Linux has a working epoll(3) interface */
+#define HAVE_EPOLL
+#endif
 #endif
 
-/* EXIT_FAILURE is a non zero value and EXIT_SUCCESS is zero.
- * To add a CONTINUE definition, simply do the opposite of EXIT_FAILURE. */
-#define ELOOP_CONTINUE -EXIT_FAILURE
-
-#ifdef IN_ELOOP
+/* Our structures require TAILQ macros, which really every libc should
+ * ship as they are useful beyond belief.
+ * Sadly some libc's don't have sys/queue.h and some that do don't have
+ * the TAILQ_FOREACH macro. For those that don't, the application using
+ * this implementation will need to ship a working queue.h somewhere.
+ * If we don't have sys/queue.h found in config.h, then
+ * allow QUEUE_H to override loading queue.h in the current directory. */
+#ifndef TAILQ_FOREACH
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h>
+#elif defined(QUEUE_H)
+#define __QUEUE_HEADER(x) #x
+#define _QUEUE_HEADER(x) __QUEUE_HEADER(x)
+#include _QUEUE_HEADER(QUEUE_H)
+#else
 #include "queue.h"
+#endif
+#endif
+
+/* Some systems don't define timespec macros */
+#ifndef timespecclear
+#define timespecclear(tsp)      (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
+#define timespecisset(tsp)      ((tsp)->tv_sec || (tsp)->tv_nsec)
+#define timespeccmp(tsp, usp, cmp)                                      \
+        (((tsp)->tv_sec == (usp)->tv_sec) ?                             \
+            ((tsp)->tv_nsec cmp (usp)->tv_nsec) :                       \
+            ((tsp)->tv_sec cmp (usp)->tv_sec))
+#define timespecadd(tsp, usp, vsp)                                      \
+        do {                                                            \
+                (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;          \
+                (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;       \
+                if ((vsp)->tv_nsec >= 1000000000L) {                    \
+                        (vsp)->tv_sec++;                                \
+                        (vsp)->tv_nsec -= 1000000000L;                  \
+                }                                                       \
+        } while (/* CONSTCOND */ 0)
+#define timespecsub(tsp, usp, vsp)                                      \
+        do {                                                            \
+                (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;          \
+                (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;       \
+                if ((vsp)->tv_nsec < 0) {                               \
+                        (vsp)->tv_sec--;                                \
+                        (vsp)->tv_nsec += 1000000000L;                  \
+                }                                                       \
+        } while (/* CONSTCOND */ 0)
+#endif
+
+/* eloop queues are really only for deleting timeouts registered
+ * for a function or object.
+ * The idea being that one interface as different timeouts for
+ * say DHCP and DHCPv6. */
+#ifndef ELOOP_QUEUE
+  #define ELOOP_QUEUE 1
+#endif
 
 struct eloop_event {
        TAILQ_ENTRY(eloop_event) next;
@@ -48,7 +110,9 @@ struct eloop_event {
        void *read_cb_arg;
        void (*write_cb)(void *);
        void *write_cb_arg;
+#if !defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL)
        struct pollfd *pollfd;
+#endif
 };
 
 struct eloop_timeout {
@@ -59,8 +123,8 @@ struct eloop_timeout {
        int queue;
 };
 
-typedef struct eloop_ctx {
-       nfds_t events_len;
+struct eloop {
+       size_t events_len;
        TAILQ_HEAD (event_head, eloop_event) events;
        struct event_head free_events;
 
@@ -69,41 +133,54 @@ typedef struct eloop_ctx {
 
        void (*timeout0)(void *);
        void *timeout0_arg;
+       const int *signals;
+       void (*signal_cb)(int, void *);
+       void *signal_cb_ctx;
 
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+       int poll_fd;
+#else
        struct pollfd *fds;
-       nfds_t fds_len;
+       size_t fds_len;
+#endif
 
        int exitnow;
        int exitcode;
-} ELOOP_CTX;
-#else
-typedef void *ELOOP_CTX;
-#endif
-
-#define eloop_timeout_add_tv(a, b, c, d) \
-    eloop_q_timeout_add_tv(a, ELOOP_QUEUE, b, c, d)
-#define eloop_timeout_add_sec(a, b, c, d) \
-    eloop_q_timeout_add_sec(a, ELOOP_QUEUE, b, c, d)
-#define eloop_timeout_add_msec(a, b, c, d) \
-    eloop_q_timeout_add_msec(a, ELOOP_QUEUE, b, c, d)
-#define eloop_timeout_delete(a, b, c) \
-    eloop_q_timeout_delete(a, ELOOP_QUEUE, b, c)
+};
 
-int eloop_event_add(ELOOP_CTX *, int,
+int eloop_event_add(struct eloop *, int,
     void (*)(void *), void *,
     void (*)(void *), void *);
-void eloop_event_delete(ELOOP_CTX *, int, void (*)(void *), void *, int);
-int eloop_q_timeout_add_sec(ELOOP_CTX *, int queue,
-    time_t, void (*)(void *), void *);
-int eloop_q_timeout_add_msec(ELOOP_CTX *, int queue,
-    suseconds_t, void (*)(void *), void *);
-int eloop_q_timeout_add_tv(ELOOP_CTX *, int queue,
+void eloop_event_delete(struct eloop *, int, int);
+
+#define eloop_timeout_add_tv(eloop, tv, cb, ctx) \
+    eloop_q_timeout_add_tv((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))
+#define eloop_timeout_add_sec(eloop, tv, cb, ctx) \
+    eloop_q_timeout_add_sec((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))
+#define eloop_timeout_add_msec(eloop, ms, cb, ctx) \
+    eloop_q_timeout_add_msec((eloop), ELOOP_QUEUE, (ms), (cb), (ctx))
+#define eloop_timeout_delete(eloop, cb, ctx) \
+    eloop_q_timeout_delete((eloop), ELOOP_QUEUE, (cb), (ctx))
+int eloop_q_timeout_add_tv(struct eloop *, int queue,
     const struct timespec *, void (*)(void *), void *);
-int eloop_timeout_add_now(ELOOP_CTX *, void (*)(void *), void *);
-void eloop_q_timeout_delete(ELOOP_CTX *, int, void (*)(void *), void *);
-ELOOP_CTX * eloop_init(void);
-void eloop_free(ELOOP_CTX *);
-void eloop_exit(ELOOP_CTX *, int);
-int eloop_start(ELOOP_CTX *);
+int eloop_q_timeout_add_sec(struct eloop *, int queue,
+    time_t, void (*)(void *), void *);
+int eloop_q_timeout_add_msec(struct eloop *, int queue,
+    long, void (*)(void *), void *);
+void eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *);
+
+int eloop_signal_set_cb(struct eloop *, const int *,
+    void (*)(int, void *), void *);
+int eloop_signal_mask(struct eloop *, sigset_t *oldset);
+
+struct eloop * eloop_new(void);
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+int eloop_requeue(struct eloop *);
+#else
+#define eloop_requeue(eloop) (0)
+#endif
+void eloop_free(struct eloop *);
+void eloop_exit(struct eloop *, int);
+int eloop_start(struct eloop *, sigset_t *);
 
 #endif