--- /dev/null
+/* Define if kqueue works correctly with pipes */
+#undef HAVE_WORKING_KQUEUE
+
+/* Define to `unsigned long long' if <sys/types.h> doesn't define. */
+#undef u_int64_t
+
+/* Define to `unsigned int' if <sys/types.h> doesn't define. */
+#undef u_int32_t
+
+/* Define to `unsigned short' if <sys/types.h> doesn't define. */
+#undef u_int16_t
+
+/* Define to `unsigned char' if <sys/types.h> doesn't define. */
+#undef u_int8_t
+
+/* Define if timeradd is defined in <sys/time.h> */
+#undef HAVE_TIMERADD
+#ifndef HAVE_TIMERADD
+#define timeradd(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
+ if ((vvp)->tv_usec >= 1000000) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif /* !HAVE_TIMERADD */
+
+/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
+#undef HAVE_TAILQFOREACH
+#ifndef HAVE_TAILQFOREACH
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+#endif /* TAILQ_FOREACH */
--- /dev/null
+dnl configure.in for libevent
+dnl Dug Song <dugsong@monkey.org>
+AC_INIT(event.c)
+
+AC_CONFIG_HEADER(config.h)
+
+dnl Initialize prefix.
+if test "$prefix" = "NONE"; then
+ prefix="/usr/local"
+fi
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(unistd.h sys/time.h sys/queue.h sys/event.h)
+if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
+ AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
+ AC_EGREP_CPP(yes,
+[
+#include <sys/queue.h>
+#ifdef TAILQ_FOREACH
+ yes
+#endif
+], [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_TAILQFOREACH) ], AC_MSG_RESULT(no)
+ )
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+ AC_MSG_CHECKING(for timeradd in sys/time.h)
+ AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timeradd
+ yes
+#endif
+], [ AC_DEFINE(HAVE_TIMERADD)
+ AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(gettimeofday)
+
+AC_SUBST(EVSRCS)
+AC_CHECK_FUNCS(select, [haveselect=yes], )
+if test "x$haveselect" = "xyes" ; then
+ EVSRCS="select.c"
+fi
+
+havekqueue=no
+if test "x$ac_cv_header_sys_event_h" = "xyes"; then
+ AC_CHECK_FUNCS(kqueue, [havekqueue=yes], )
+ if test "x$havekqueue" = "xyes" ; then
+ AC_MSG_CHECKING(for working kqueue)
+ AC_TRY_RUN(
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main(int argc, char **argv)
+{
+ int kq;
+ int n;
+ int fd[[2]];
+ struct kevent ev;
+ struct timespec ts;
+ char buf[[8000]];
+
+ if (pipe(fd) == -1)
+ exit(1);
+ if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1)
+ exit(1);
+
+ while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf))
+ ;
+
+ if ((kq = kqueue()) == -1)
+ exit(1);
+
+ ev.ident = fd[[1]];
+ ev.filter = EVFILT_WRITE;
+ ev.flags = EV_ADD | EV_ENABLE;
+ n = kevent(kq, &ev, 1, NULL, 0, NULL);
+ if (n == -1)
+ exit(1);
+
+ read(fd[[0]], buf, sizeof(buf));
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ n = kevent(kq, NULL, 0, &ev, 1, &ts);
+ if (n == -1 || n == 0)
+ exit(1);
+
+ exit(0);
+}, [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_WORKING_KQUEUE)
+ EVSRCS="$EVSRCS kqueue.c"], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+ fi
+fi
+
+AC_OUTPUT(Makefile test/Makefile)
--- /dev/null
+.\" $OpenBSD: timeout.9,v 1.11 2000/10/12 18:06:03 aaron Exp $
+.\"
+.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 23, 1996
+.Dt EVENT 3
+.Os
+.Sh NAME
+.Nm event_init ,
+.Nm event_dispatch ,
+.Nm event_loop ,
+.Nm event_set ,
+.Nm event_add ,
+.Nm event_del ,
+.Nm event_pending ,
+.Nm event_initalized ,
+.Nm timeout_set ,
+.Nm timeout_add ,
+.Nm timeout_del
+.Nm timeout_pending ,
+.Nm timeout_initalized ,
+.Nd execute a function when a specific event occurs
+.Sh SYNOPSIS
+.Fd #include <event.h>
+.Ft void
+.Fn "event_init"
+.Ft int
+.Fn "event_dispatch"
+.Ft int
+.Fn "event_loop" "int flags"
+.Ft void
+.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
+.Ft int
+.Fn "event_add" "struct event *ev" "struct timeval *tv"
+.Ft int
+.Fn "event_del" "struct event *ev"
+.Ft int
+.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv"
+.Ft int
+.Fn "event_initialized" "struct event *ev"
+.Ft void
+.Fn "timeout_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg"
+.Ft void
+.Fn "timeout_add" "struct event *ev" "struct timeval *"
+.Ft void
+.Fn "timeout_del" "struct event *ev"
+.Ft int
+.Fn "timeout_pending" "struct event *ev" "struct timeval *tv"
+.Ft int
+.Fn "timeout_initialized" "struct event *ev"
+.Ft int
+.Fa (*event_sigcb)(void) ;
+.Ft int
+.Fa event_gotsig ;
+.Sh DESCRIPTION
+The
+.Nm event
+API provides a mechanism to execute a function when a specific event
+on a file descriptor occurs or after at a given time has passed.
+.Pp
+The
+.Nm event
+API needs to be initalized with
+.Fn event_init
+before it can be used.
+.Pp
+In order to process events, an application needs to call
+.Fn event_dispatch .
+This functions only returns on error, and should replace the event core
+of the application program.
+.Pp
+In order to avoid races in signal handlers, the
+.Nm event
+API provides two variables:
+.Va event_sigcb
+and
+.Va event_gotsig .
+A signal handler
+sets
+.Va event_gotsig
+to indicate that a signal has been received.
+The application sets
+.Va event_sigcb
+to a callback function. After the signal handler sets
+.Va event_gotsig ,
+.Nm event_dispatch
+will execute the callback function to process received signals. The
+callback returns 1 when no events are registered any more. It can
+return -1 to indicate an error to the
+.Nm event
+library, causing
+.Fn event_dispatch
+to terminate with
+.Va errno
+set to
+.Er EINTR.
+.Pp
+The
+.Nm event_loop
+function provides an interface for single pass execution of pending
+events. The flags
+.Va EVLOOP_ONCE
+and
+.Va EVLOOP_NONBLOCK
+are recognized.
+.Pp
+It is the responsibility of the caller to provide these functions with
+pre-allocated event structures.
+.Pp
+The function
+.Fn event_set
+prepares the event structure
+.Fa ev
+to be used in future calls to
+.Fn event_add
+and
+.Fn event_del .
+The event will be prepared to call the function specified by the
+.Fa fn
+argument with an
+.Fa int
+argument indicating the file descriptor, with a
+.Fa short
+argument indicating the type of event, and a
+.Fa void *
+argument given in the
+.Fa arg
+argument.
+The
+.Fa fd
+indicates the file descriptor that should be monitored for events.
+The events can be either
+.Va EV_READ,
+.Va EV_WRITE,
+or both.
+Indicating that an application can read or write from the file descriptor
+respectively without blocking.
+.Pp
+The function
+.Fa fn
+will be called with the file descriptor that triggered the event and
+the type of event which will be either
+.Va EV_TIMEOUT ,
+.Va EV_READ ,
+or
+.Va EV_WRITE .
+.Pp
+Once initialized, the
+.Fa ev
+structure can be used in repeatedly in
+.Fn event_add
+and
+.Fn event_del
+and does not need to be reinitialized unless you wish to
+change the function called and/or the argument to it.
+.Pp
+The function
+.Fn event_add
+schedules the execution of the
+.Fa ev
+event when the event specified in
+.Fn event_set
+occurs or in at least the time specified in the
+.Fa tv .
+If
+.Fa tv
+is NULL, no timeout occurs and the function will only be called
+if a matching event occurs on the file descriptor.
+The event in the
+.Fa ev
+argument must be already initialized by
+.Fn event_set
+and may not be used in calls to
+.Fn event_set
+until it has timed out or been removed with
+.Fn event_del .
+If the event in the
+.Fa ev
+argument has already a scheduled timeout, the old timeout will be
+replaced by the new one.
+.Pp
+The function
+.Fn event_del
+will cancel the event in the argument
+.Fa ev .
+If the event has already executed or has never been added
+the call will have no effect.
+.Pp
+The
+.Fn event_pending
+function can be used to check if the event specified by
+.Fa event
+is pending to run.
+If
+.Va EV_TIMEOUT
+was specified and
+.Fa tv
+is not
+.Va NULL ,
+the expiration time of the event will be returned in
+.Fa tv .
+.Pp
+The
+.Fn event_initialized
+macro can be used to check if an event has been initialized.
+.Pp
+The functions
+.Fn timeout_set ,
+.Fn timeout_add ,
+.Fn timeout_del ,
+.Fn timeout_initialized ,
+and
+.Fn timeout_pending
+are abbreviations for common situations where only a timeout is required.
+The file descriptor passed will be 0, and the event type will be
+.Va EV_TIMEOUT .
+.Pp
+It is possible to disable support for
+.Va kqueue
+but setting the environment variable
+.Va EVENT_NOKQUEUE .
+.Pp
+.Sh RETURN VALUES
+Upon successful completion
+.Fn event_add
+and
+.Fn event_del
+return 0.
+Otherwise, -1 is returned and the global variable errno is
+set to indicate the error.
+.Sh SEE ALSO
+.Xr timeout 9 ,
+.Xr select 2 ,
+.Xr kqueue 2
+.Sh HISTORY
+The
+.Nm event
+API manpage is based on the
+.Xr timeout 9
+manpage by Artur Grabowski.
+.Sh AUTHORS
+The
+.Nm event
+library was written by Niels Provos.
--- /dev/null
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niels Provos.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/tree.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <assert.h>
+
+#ifdef USE_LOG
+#include "log.h"
+#else
+#define LOG_DBG(x)
+#define log_error(x) perror(x)
+#endif
+
+#include "event.h"
+
+#ifdef HAVE_SELECT
+extern struct eventop selectops;
+#endif
+#ifdef HAVE_WORKING_KQUEUE
+extern struct eventop kqops;
+#endif
+
+/* In order of preference */
+struct eventop *eventops[] = {
+#ifdef HAVE_WORKING_KQUEUE
+ &kqops,
+#endif
+#ifdef HAVE_SELECT
+ &selectops,
+#endif
+ NULL
+};
+
+struct eventop *evsel;
+void *evbase;
+
+/* Handle signals */
+int (*event_sigcb)(void); /* Signal callback when gotsig is set */
+int event_gotsig; /* Set in signal handler */
+
+/* Prototypes */
+void event_queue_insert(struct event *, int);
+void event_queue_remove(struct event *, int);
+
+static RB_HEAD(event_tree, event) timetree;
+static struct event_list activequeue;
+struct event_list eventqueue;
+static struct timeval event_tv;
+
+static int
+compare(struct event *a, struct event *b)
+{
+ if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
+ return (-1);
+ else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
+ return (1);
+ return (0);
+}
+
+RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
+
+RB_GENERATE(event_tree, event, ev_timeout_node, compare);
+
+
+void
+event_init(void)
+{
+ int i;
+
+ event_sigcb = NULL;
+ event_gotsig = 0;
+ gettimeofday(&event_tv, NULL);
+
+ RB_INIT(&timetree);
+ TAILQ_INIT(&eventqueue);
+ TAILQ_INIT(&activequeue);
+
+ evbase = NULL;
+ for (i = 0; eventops[i] && !evbase; i++) {
+ evsel = eventops[i];
+
+ evbase = evsel->init();
+ }
+
+#if defined(USE_LOG) && defined(USE_DEBUG)
+ log_to(stderr);
+ log_debug_cmd(LOG_MISC, 80);
+#endif
+}
+
+int
+event_haveevents(void)
+{
+ return (RB_ROOT(&timetree) || TAILQ_FIRST(&eventqueue) ||
+ TAILQ_FIRST(&activequeue));
+}
+
+void
+event_process_active(void)
+{
+ struct event *ev;
+
+ for (ev = TAILQ_FIRST(&activequeue); ev;
+ ev = TAILQ_FIRST(&activequeue)) {
+ event_queue_remove(ev, EVLIST_ACTIVE);
+
+ (*ev->ev_callback)(ev->ev_fd, ev->ev_res, ev->ev_arg);
+ }
+}
+
+int
+event_dispatch(void)
+{
+ return (event_loop(0));
+}
+
+int
+event_loop(int flags)
+{
+ struct timeval tv;
+ int res, done;
+
+ /* Calculate the initial events that we are waiting for */
+ if (evsel->recalc(evbase, 0) == -1)
+ return (-1);
+
+ done = 0;
+ while (!done) {
+ while (event_gotsig) {
+ event_gotsig = 0;
+ if (event_sigcb) {
+ res = (*event_sigcb)();
+ if (res == -1) {
+ errno = EINTR;
+ return (-1);
+ }
+ }
+ }
+
+ /* Check if time is running backwards */
+ gettimeofday(&tv, NULL);
+ if (timercmp(&tv, &event_tv, <)) {
+ struct timeval off;
+ LOG_DBG((LOG_MIST, 10,
+ "%s: time is running backwards, corrected",
+ __FUNCTION__));
+
+ timersub(&event_tv, &tv, &off);
+ timeout_correct(&off);
+ }
+ event_tv = tv;
+
+ if (!(flags & EVLOOP_NONBLOCK))
+ timeout_next(&tv);
+ else
+ timerclear(&tv);
+
+ /* If we have no events, we just exit */
+ if (!event_haveevents())
+ return (1);
+
+ res = evsel->dispatch(evbase, &tv);
+
+ if (res == -1)
+ return (-1);
+
+ timeout_process();
+
+ if (TAILQ_FIRST(&activequeue)) {
+ event_process_active();
+ if (flags & EVLOOP_ONCE)
+ done = 1;
+ } else if (flags & EVLOOP_NONBLOCK)
+ done = 1;
+
+ if (evsel->recalc(evbase, 0) == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+event_set(struct event *ev, int fd, short events,
+ void (*callback)(int, short, void *), void *arg)
+{
+ ev->ev_callback = callback;
+ ev->ev_arg = arg;
+ ev->ev_fd = fd;
+ ev->ev_events = events;
+ ev->ev_flags = EVLIST_INIT;
+}
+
+/*
+ * Checks if a specific event is pending or scheduled.
+ */
+
+int
+event_pending(struct event *ev, short event, struct timeval *tv)
+{
+ int flags = 0;
+
+ if (ev->ev_flags & EVLIST_INSERTED)
+ flags |= (ev->ev_events & (EV_READ|EV_WRITE));
+ if (ev->ev_flags & EVLIST_ACTIVE)
+ flags |= ev->ev_res;
+ if (ev->ev_flags & EVLIST_TIMEOUT)
+ flags |= EV_TIMEOUT;
+
+ event &= (EV_TIMEOUT|EV_READ|EV_WRITE);
+
+ /* See if there is a timeout that we should report */
+ if (tv != NULL && (flags & event & EV_TIMEOUT))
+ *tv = ev->ev_timeout;
+
+ return (flags & event);
+}
+
+int
+event_add(struct event *ev, struct timeval *tv)
+{
+ LOG_DBG((LOG_MISC, 55,
+ "event_add: event: %p, %s%s%scall %p",
+ ev,
+ ev->ev_events & EV_READ ? "EV_READ " : " ",
+ ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
+ tv ? "EV_TIMEOUT " : " ",
+ ev->ev_callback));
+
+ assert(!(ev->ev_flags & ~EVLIST_ALL));
+
+ if (tv != NULL) {
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ timeradd(&now, tv, &ev->ev_timeout);
+
+ LOG_DBG((LOG_MISC, 55,
+ "event_add: timeout in %d seconds, call %p",
+ tv->tv_sec, ev->ev_callback));
+ if (ev->ev_flags & EVLIST_TIMEOUT)
+ event_queue_remove(ev, EVLIST_TIMEOUT);
+
+ event_queue_insert(ev, EVLIST_TIMEOUT);
+ }
+
+ if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
+ !(ev->ev_flags & EVLIST_INSERTED)) {
+ event_queue_insert(ev, EVLIST_INSERTED);
+
+ return (evsel->add(evbase, ev));
+ }
+
+ return (0);
+}
+
+int
+event_del(struct event *ev)
+{
+ LOG_DBG((LOG_MISC, 80, "event_del: %p, callback %p",
+ ev, ev->ev_callback));
+
+ assert(!(ev->ev_flags & ~EVLIST_ALL));
+
+ if (ev->ev_flags & EVLIST_TIMEOUT)
+ event_queue_remove(ev, EVLIST_TIMEOUT);
+
+ if (ev->ev_flags & EVLIST_ACTIVE)
+ event_queue_remove(ev, EVLIST_ACTIVE);
+
+ if (ev->ev_flags & EVLIST_INSERTED) {
+ event_queue_remove(ev, EVLIST_INSERTED);
+ return (evsel->del(evbase, ev));
+ }
+
+ return (0);
+}
+
+void
+event_active(struct event *ev, int res)
+{
+ ev->ev_res = res;
+ event_queue_insert(ev, EVLIST_ACTIVE);
+}
+
+int
+timeout_next(struct timeval *tv)
+{
+ struct timeval now;
+ struct event *ev;
+
+ if ((ev = RB_MIN(event_tree, &timetree)) == NULL) {
+ timerclear(tv);
+ tv->tv_sec = TIMEOUT_DEFAULT;
+ return (0);
+ }
+
+ if (gettimeofday(&now, NULL) == -1)
+ return (-1);
+
+ if (timercmp(&ev->ev_timeout, &now, <=)) {
+ timerclear(tv);
+ return (0);
+ }
+
+ timersub(&ev->ev_timeout, &now, tv);
+
+ LOG_DBG((LOG_MISC, 60, "timeout_next: in %d seconds", tv->tv_sec));
+ return (0);
+}
+
+void
+timeout_correct(struct timeval *off)
+{
+ struct event *ev;
+
+ /* We can modify the key element of the node without destroying
+ * the key, beause we apply it to all in the right order.
+ */
+ RB_FOREACH(ev, event_tree, &timetree)
+ timersub(&ev->ev_timeout, off, &ev->ev_timeout);
+}
+
+void
+timeout_process(void)
+{
+ struct timeval now;
+ struct event *ev, *next;
+
+ gettimeofday(&now, NULL);
+
+ for (ev = RB_MIN(event_tree, &timetree); ev; ev = next) {
+ if (timercmp(&ev->ev_timeout, &now, >))
+ break;
+ next = RB_NEXT(event_tree, &timetree, ev);
+
+ event_queue_remove(ev, EVLIST_TIMEOUT);
+
+ /* delete this event from the I/O queues */
+ event_del(ev);
+
+ LOG_DBG((LOG_MISC, 60, "timeout_process: call %p",
+ ev->ev_callback));
+ event_active(ev, EV_TIMEOUT);
+ }
+}
+
+void
+timeout_insert(struct event *ev)
+{
+ struct event *tmp;
+
+ tmp = RB_FIND(event_tree, &timetree, ev);
+
+ if (tmp != NULL) {
+ struct timeval tv;
+ struct timeval add = {0,1};
+
+ /* Find unique time */
+ tv = ev->ev_timeout;
+ do {
+ timeradd(&tv, &add, &tv);
+ tmp = RB_NEXT(event_tree, &timetree, tmp);
+ } while (tmp != NULL && timercmp(&tmp->ev_timeout, &tv, ==));
+
+ ev->ev_timeout = tv;
+ }
+
+ tmp = RB_INSERT(event_tree, &timetree, ev);
+ assert(tmp == NULL);
+}
+
+void
+event_queue_remove(struct event *ev, int queue)
+{
+ if (!(ev->ev_flags & queue))
+ errx(1, "%s: %p(fd %d) not on queue %x", __FUNCTION__,
+ ev, ev->ev_fd, queue);
+
+ ev->ev_flags &= ~queue;
+ switch (queue) {
+ case EVLIST_ACTIVE:
+ TAILQ_REMOVE(&activequeue, ev, ev_active_next);
+ break;
+ case EVLIST_TIMEOUT:
+ RB_REMOVE(event_tree, &timetree, ev);
+ break;
+ case EVLIST_INSERTED:
+ TAILQ_REMOVE(&eventqueue, ev, ev_next);
+ break;
+ default:
+ errx(1, "%s: unknown queue %x", __FUNCTION__, queue);
+ }
+}
+
+void
+event_queue_insert(struct event *ev, int queue)
+{
+ if (ev->ev_flags & queue)
+ errx(1, "%s: %p(fd %d) already on queue %x", __FUNCTION__,
+ ev, ev->ev_fd, queue);
+
+ ev->ev_flags |= queue;
+ switch (queue) {
+ case EVLIST_ACTIVE:
+ TAILQ_INSERT_TAIL(&activequeue, ev, ev_active_next);
+ break;
+ case EVLIST_TIMEOUT:
+ timeout_insert(ev);
+ break;
+ case EVLIST_INSERTED:
+ TAILQ_INSERT_TAIL(&eventqueue, ev, ev_next);
+ break;
+ default:
+ errx(1, "%s: unknown queue %x", __FUNCTION__, queue);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niels Provos.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _EVENT_H_
+#define _EVENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVLIST_TIMEOUT 0x01
+#define EVLIST_INSERTED 0x02
+#define EVLIST_ACTIVE 0x08
+#define EVLIST_INIT 0x80
+
+/* EVLIST_X_ Private space: 0x1000-0xf000 */
+#define EVLIST_ALL (0xf000 | 0x8f)
+
+#define EV_TIMEOUT 0x01
+#define EV_READ 0x02
+#define EV_WRITE 0x04
+
+/* Fix so that ppl dont have to run with <sys/queue.h> */
+#ifndef TAILQ_ENTRY
+#define _EVENT_DEFINED_TQENTRY
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+#endif /* !TAILQ_ENTRY */
+#ifndef RB_ENTRY
+#define _EVENT_DEFINED_RBENTRY
+#define RB_ENTRY(type) \
+struct { \
+ struct type *rbe_left; /* left element */ \
+ struct type *rbe_right; /* right element */ \
+ struct type *rbe_parent; /* parent element */ \
+ int rbe_color; /* node color */ \
+}
+#endif /* !RB_ENTRY */
+
+struct event {
+ TAILQ_ENTRY (event) ev_next;
+ TAILQ_ENTRY (event) ev_active_next;
+ RB_ENTRY (event) ev_timeout_node;
+
+ int ev_fd;
+ short ev_events;
+
+ struct timeval ev_timeout;
+
+ void (*ev_callback)(int, short, void *arg);
+ void *ev_arg;
+
+ int ev_res; /* result passed to event callback */
+ int ev_flags;
+};
+
+#ifdef _EVENT_DEFINED_TQENTRY
+#undef TAILQ_ENTRY
+#undef _EVENT_DEFINED_TQENTRY
+#else
+TAILQ_HEAD (event_list, event);
+#endif /* _EVENT_DEFINED_TQENTRY */
+#ifdef _EVENT_DEFINED_RBENTRY
+#undef RB_ENTRY
+#undef _EVENT_DEFINED_RBENTRY
+#endif /* _EVENT_DEFINED_RBENTRY */
+
+struct eventop {
+ char *name;
+ void *(*init)(void);
+ int (*add)(void *, struct event *);
+ int (*del)(void *, struct event *);
+ int (*recalc)(void *, int);
+ int (*dispatch)(void *, struct timeval *);
+};
+
+#define TIMEOUT_DEFAULT 5
+
+void event_init(void);
+int event_dispatch(void);
+
+#define EVLOOP_ONCE 0x01
+#define EVLOOP_NONBLOCK 0x02
+int event_loop(int);
+
+int timeout_next(struct timeval *);
+void timeout_correct(struct timeval *);
+void timeout_process(void);
+
+#define timeout_add(ev, tv) event_add(ev, tv)
+#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
+#define timeout_del(ev) event_del(ev)
+#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
+#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
+
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+int event_add(struct event *, struct timeval *);
+int event_del(struct event *);
+ void event_active(struct event *, int);
+
+int event_pending(struct event *, short, struct timeval *);
+
+#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_H_ */
--- /dev/null
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
--- /dev/null
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niels Provos.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/event.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef USE_LOG
+#include "log.h"
+#else
+#define LOG_DBG(x)
+#define log_error(x) perror(x)
+#endif
+
+#include "event.h"
+
+extern struct event_list timequeue;
+extern struct event_list eventqueue;
+extern struct event_list addqueue;
+
+#define EVLIST_X_KQINKERNEL 0x1000
+
+#define NEVENT 64
+
+struct kqop {
+ struct kevent *changes;
+ int nchanges;
+ struct kevent *events;
+ int nevents;
+ int kq;
+} kqop;
+
+void *kq_init (void);
+int kq_add (void *, struct event *);
+int kq_del (void *, struct event *);
+int kq_recalc (void *, int);
+int kq_dispatch (void *, struct timeval *);
+
+struct eventop kqops = {
+ "kqueue",
+ kq_init,
+ kq_add,
+ kq_del,
+ kq_recalc,
+ kq_dispatch
+};
+
+void *
+kq_init(void)
+{
+ int kq;
+
+ /* Disable kqueue when this environment variable is set */
+ if (getenv("EVENT_NOKQUEUE"))
+ return (NULL);
+
+ memset(&kqop, 0, sizeof(kqop));
+
+ /* Initalize the kernel queue */
+
+ if ((kq = kqueue()) == -1) {
+ log_error("kqueue");
+ return (NULL);
+ }
+
+ kqop.kq = kq;
+
+ /* Initalize fields */
+ kqop.changes = malloc(NEVENT * sizeof(struct kevent));
+ if (kqop.changes == NULL)
+ return (NULL);
+ kqop.events = malloc(NEVENT * sizeof(struct kevent));
+ if (kqop.events == NULL) {
+ free (kqop.changes);
+ return (NULL);
+ }
+ kqop.nevents = NEVENT;
+
+ return (&kqop);
+}
+
+int
+kq_recalc(void *arg, int max)
+{
+ return (0);
+}
+
+int
+kq_insert(struct kqop *kqop, struct kevent *kev)
+{
+ int nevents = kqop->nevents;
+
+ if (kqop->nchanges == nevents) {
+ struct kevent *newchange;
+ struct kevent *newresult;
+
+ nevents *= 2;
+
+ newchange = realloc(kqop->changes,
+ nevents * sizeof(struct kevent));
+ if (newchange == NULL) {
+ log_error(__FUNCTION__": malloc");
+ return (-1);
+ }
+ kqop->changes = newchange;
+
+ newresult = realloc(kqop->changes,
+ nevents * sizeof(struct kevent));
+
+ /*
+ * If we fail, we don't have to worry about freeing,
+ * the next realloc will pick it up.
+ */
+ if (newresult == NULL) {
+ log_error(__FUNCTION__": malloc");
+ return (-1);
+ }
+ kqop->events = newchange;
+
+ kqop->nevents = nevents;
+ }
+
+ memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
+
+ LOG_DBG((LOG_MISC, 70, __FUNCTION__": fd %d %s%s",
+ kev->ident,
+ kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
+ kev->flags == EV_DELETE ? " (del)" : ""));
+
+ return (0);
+}
+
+int
+kq_dispatch(void *arg, struct timeval *tv)
+{
+ struct kqop *kqop = arg;
+ struct kevent *changes = kqop->changes;
+ struct kevent *events = kqop->events;
+ struct event *ev;
+ struct timespec ts;
+ int i, res;
+
+ TIMEVAL_TO_TIMESPEC(tv, &ts);
+
+ res = kevent(kqop->kq, changes, kqop->nchanges,
+ events, kqop->nevents, &ts);
+ kqop->nchanges = 0;
+ if (res == -1) {
+ if (errno != EINTR) {
+ log_error("kevent");
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ LOG_DBG((LOG_MISC, 80, __FUNCTION__": kevent reports %d", res));
+
+ for (i = 0; i < res; i++) {
+ int which = 0;
+
+ if (events[i].flags & EV_ERROR) {
+ /*
+ * Error messages that can happen, when a delete fails.
+ * EBADF happens when the file discriptor has been
+ * closed,
+ * ENOENT when the file discriptor was closed and
+ * then reopened.
+ * An error is also indicated when a callback deletes
+ * an event we are still processing. In that case
+ * the data field is set to ENOENT.
+ */
+ if (events[i].data == EBADF ||
+ events[i].data == ENOENT)
+ continue;
+ return (-1);
+ }
+
+ ev = events[i].udata;
+
+ if (events[i].filter == EVFILT_READ) {
+ which |= EV_READ;
+ } else if (events[i].filter == EVFILT_WRITE) {
+ which |= EV_WRITE;
+ }
+
+ if (which) {
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ event_del(ev);
+ event_active(ev, which);
+ }
+ }
+
+ return (0);
+}
+
+
+int
+kq_add(void *arg, struct event *ev)
+{
+ struct kqop *kqop = arg;
+ struct kevent kev;
+
+ if (ev->ev_events & EV_READ) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_READ;
+ kev.flags = EV_ADD | EV_ONESHOT;
+ kev.udata = ev;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags |= EVLIST_X_KQINKERNEL;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_WRITE;
+ kev.flags = EV_ADD | EV_ONESHOT;
+ kev.udata = ev;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags |= EVLIST_X_KQINKERNEL;
+ }
+
+ return (0);
+}
+
+int
+kq_del(void *arg, struct event *ev)
+{
+ struct kqop *kqop = arg;
+ struct kevent kev;
+
+ if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
+ return (0);
+
+ if (ev->ev_events & EV_READ) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_READ;
+ kev.flags = EV_DELETE;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_WRITE;
+ kev.flags = EV_DELETE;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Compile with:
+ * cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event.h>
+
+void
+fifo_read(int fd, short event, void *arg)
+{
+ char buf[255];
+ int len;
+ struct event *ev = arg;
+
+ /* Reschedule this event */
+ event_add(ev, NULL);
+
+ fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
+ fd, event, arg);
+
+ len = read(fd, buf, sizeof(buf) - 1);
+ if (len == -1) {
+ perror("read");
+ return;
+ } else if (len == 0) {
+ fprintf(stderr, "Connection closed\n");
+ return;
+ }
+
+ buf[len] = '\0';
+ fprintf(stdout, "Read: %s\n", buf);
+}
+
+int
+main (int argc, char **argv)
+{
+ struct stat st;
+ char *fifo = "event.fifo";
+ int socket;
+ struct event evfifo;
+
+ if (lstat (fifo, &st) == 0) {
+ if ((st.st_mode & S_IFMT) == S_IFREG) {
+ errno = EEXIST;
+ perror("lstat");
+ exit (1);
+ }
+ }
+
+ unlink (fifo);
+ if (mkfifo (fifo, 0600) == -1) {
+ perror("mkfifo");
+ exit (1);
+ }
+
+ /* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */
+#ifdef __linux
+ socket = open (fifo, O_RDWR | O_NONBLOCK, 0);
+#else
+ socket = open (fifo, O_RDONLY | O_NONBLOCK, 0);
+#endif
+
+ if (socket == -1) {
+ perror("open");
+ exit (1);
+ }
+
+ fprintf(stderr, "Write data to %s\n", fifo);
+
+ /* Initalize the event library */
+ event_init();
+
+ /* Initalize one event */
+ event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
+
+ /* Add it to the active events, without a timeout */
+ event_add(&evfifo, NULL);
+
+ event_dispatch();
+
+ return (0);
+}
+
--- /dev/null
+/*
+ * Compile with:
+ * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event.h>
+
+int lasttime;
+
+void
+timeout_cb(int fd, short event, void *arg)
+{
+ struct timeval tv;
+ struct event *timeout = arg;
+ int newtime = time(NULL);
+
+ printf("%s: called at %d: %d\n", __FUNCTION__, newtime,
+ newtime - lasttime);
+ lasttime = newtime;
+
+ timerclear(&tv);
+ tv.tv_sec = 2;
+ event_add(timeout, &tv);
+}
+
+int
+main (int argc, char **argv)
+{
+ struct event timeout;
+ struct timeval tv;
+
+ /* Initalize the event library */
+ event_init();
+
+ /* Initalize one event */
+ timeout_set(&timeout, timeout_cb, &timeout);
+
+ timerclear(&tv);
+ tv.tv_sec = 2;
+ event_add(&timeout, &tv);
+
+ lasttime = time(NULL);
+
+ event_dispatch();
+
+ return (0);
+}
+
--- /dev/null
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niels Provos.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef USE_LOG
+#include "log.h"
+#else
+#define LOG_DBG(x)
+#define log_error(x) perror(x)
+#endif
+
+#include "event.h"
+
+extern struct event_list timequeue;
+extern struct event_list eventqueue;
+extern struct event_list addqueue;
+
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+
+struct selectop {
+ int event_fds; /* Highest fd in fd set */
+ int event_fdsz;
+ fd_set *event_readset;
+ fd_set *event_writeset;
+} sop;
+
+void *select_init (void);
+int select_add (void *, struct event *);
+int select_del (void *, struct event *);
+int select_recalc (void *, int);
+int select_dispatch (void *, struct timeval *);
+
+struct eventop selectops = {
+ "select",
+ select_init,
+ select_add,
+ select_del,
+ select_recalc,
+ select_dispatch
+};
+
+void *
+select_init(void)
+{
+ memset(&sop, 0, sizeof(sop));
+
+ return (&sop);
+}
+
+/*
+ * Called with the highest fd that we know about. If it is 0, completely
+ * recalculate everything.
+ */
+
+int
+select_recalc(void *arg, int max)
+{
+ struct selectop *sop = arg;
+ fd_set *readset, *writeset;
+ struct event *ev;
+ int fdsz;
+
+ if (sop->event_fds < max)
+ sop->event_fds = max;
+
+ if (!sop->event_fds) {
+ TAILQ_FOREACH(ev, &eventqueue, ev_next)
+ if (ev->ev_fd > sop->event_fds)
+ sop->event_fds = ev->ev_fd;
+ }
+
+ fdsz = howmany(sop->event_fds + 1, NFDBITS) * sizeof(fd_mask);
+ if (fdsz > sop->event_fdsz) {
+ if ((readset = realloc(sop->event_readset, fdsz)) == NULL) {
+ log_error("malloc");
+ return (-1);
+ }
+
+ if ((writeset = realloc(sop->event_writeset, fdsz)) == NULL) {
+ log_error("malloc");
+ free(readset);
+ return (-1);
+ }
+
+ memset((char *)readset + sop->event_fdsz, 0,
+ fdsz - sop->event_fdsz);
+ memset((char *)writeset + sop->event_fdsz, 0,
+ fdsz - sop->event_fdsz);
+
+ sop->event_readset = readset;
+ sop->event_writeset = writeset;
+ sop->event_fdsz = fdsz;
+ }
+
+ return (0);
+}
+
+int
+select_dispatch(void *arg, struct timeval *tv)
+{
+ int maxfd, res;
+ struct event *ev, *next;
+ struct selectop *sop = arg;
+
+ memset(sop->event_readset, 0, sop->event_fdsz);
+ memset(sop->event_writeset, 0, sop->event_fdsz);
+
+ TAILQ_FOREACH(ev, &eventqueue, ev_next) {
+ if (ev->ev_events & EV_WRITE)
+ FD_SET(ev->ev_fd, sop->event_writeset);
+ if (ev->ev_events & EV_READ)
+ FD_SET(ev->ev_fd, sop->event_readset);
+ }
+
+
+ if ((res = select(sop->event_fds + 1, sop->event_readset,
+ sop->event_writeset, NULL, tv)) == -1) {
+ if (errno != EINTR) {
+ log_error("select");
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ LOG_DBG((LOG_MISC, 80, __FUNCTION__": select reports %d",
+ res));
+
+ maxfd = 0;
+ for (ev = TAILQ_FIRST(&eventqueue); ev != NULL; ev = next) {
+ next = TAILQ_NEXT(ev, ev_next);
+
+ res = 0;
+ if (FD_ISSET(ev->ev_fd, sop->event_readset))
+ res |= EV_READ;
+ if (FD_ISSET(ev->ev_fd, sop->event_writeset))
+ res |= EV_WRITE;
+ res &= ev->ev_events;
+
+ if (res) {
+ event_del(ev);
+ event_active(ev, res);
+ } else if (ev->ev_fd > maxfd)
+ maxfd = ev->ev_fd;
+ }
+
+ sop->event_fds = maxfd;
+
+ return (0);
+}
+
+int
+select_add(void *arg, struct event *ev)
+{
+ struct selectop *sop = arg;
+
+ /*
+ * Keep track of the highest fd, so that we can calculate the size
+ * of the fd_sets for select(2)
+ */
+ if (sop->event_fds < ev->ev_fd)
+ sop->event_fds = ev->ev_fd;
+
+ return (0);
+}
+
+/*
+ * Nothing to be done here.
+ */
+
+int
+select_del(void *arg, struct event *ev)
+{
+ return (0);
+}
--- /dev/null
+/*
+ * Compile with:
+ * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event.h>
+
+int test_okay = 1;
+int called = 0;
+
+void
+read_cb(int fd, short event, void *arg)
+{
+ char buf[256];
+ int len;
+
+ len = read(fd, buf, sizeof(buf));
+
+ printf("%s: read %d%s\n", __FUNCTION__,
+ len, len ? "" : " - means EOF");
+
+ if (len) {
+ if (!called)
+ event_add(arg, NULL);
+ } else if (called == 1)
+ test_okay = 0;
+
+ called++;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct event ev;
+ char *test = "test string";
+ int pair[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
+ return (1);
+
+
+ write(pair[0], test, strlen(test)+1);
+ shutdown(pair[0], SHUT_WR);
+
+ /* Initalize the event library */
+ event_init();
+
+ /* Initalize one event */
+ event_set(&ev, pair[1], EV_READ, read_cb, &ev);
+
+ event_add(&ev, NULL);
+
+ event_dispatch();
+
+ return (test_okay);
+}
+
--- /dev/null
+/*
+ * Compile with:
+ * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event.h>
+
+int pair[2];
+int test_okay = 1;
+int called = 0;
+
+void
+write_cb(int fd, short event, void *arg)
+{
+ char *test = "test string";
+ int len;
+
+ len = write(fd, test, strlen(test) + 1);
+
+ printf("%s: write %d%s\n", __FUNCTION__,
+ len, len ? "" : " - means EOF");
+
+ if (len > 0) {
+ if (!called)
+ event_add(arg, NULL);
+ close(pair[0]);
+ } else if (called == 1):
+ test_okay = 0;
+
+ called++;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct event ev;
+
+ if (signal(SIGPIPE, SIG_IGN) == SIG_IGN)
+ return (1);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
+ return (1);
+
+ /* Initalize the event library */
+ event_init();
+
+ /* Initalize one event */
+ event_set(&ev, pair[1], EV_WRITE, write_cb, &ev);
+
+ event_add(&ev, NULL);
+
+ event_dispatch();
+
+ return (test_okay);
+}
+