]> granicus.if.org Git - libevent/commitdiff
Initial revision
authorNiels Provos <provos@gmail.com>
Tue, 9 Apr 2002 15:14:06 +0000 (15:14 +0000)
committerNiels Provos <provos@gmail.com>
Tue, 9 Apr 2002 15:14:06 +0000 (15:14 +0000)
svn:r2

12 files changed:
acconfig.h [new file with mode: 0644]
configure.in [new file with mode: 0644]
event.3 [new file with mode: 0644]
event.c [new file with mode: 0644]
event.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
kqueue.c [new file with mode: 0644]
sample/event-test.c [new file with mode: 0644]
sample/time-test.c [new file with mode: 0644]
select.c [new file with mode: 0644]
test/test-eof.c [new file with mode: 0644]
test/test-weof.c [new file with mode: 0644]

diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..440d54a
--- /dev/null
@@ -0,0 +1,55 @@
+/* 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 */
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..fdeb3dd
--- /dev/null
@@ -0,0 +1,117 @@
+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)
diff --git a/event.3 b/event.3
new file mode 100644 (file)
index 0000000..287dd7f
--- /dev/null
+++ b/event.3
@@ -0,0 +1,268 @@
+.\"    $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.
diff --git a/event.c b/event.c
new file mode 100644 (file)
index 0000000..b4272af
--- /dev/null
+++ b/event.c
@@ -0,0 +1,454 @@
+/*
+ * 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);
+       }
+}
diff --git a/event.h b/event.h
new file mode 100644 (file)
index 0000000..cdc8d2a
--- /dev/null
+++ b/event.h
@@ -0,0 +1,138 @@
+/*
+ * 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_ */
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..89fc9b0
--- /dev/null
@@ -0,0 +1,238 @@
+#! /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
diff --git a/kqueue.c b/kqueue.c
new file mode 100644 (file)
index 0000000..594fe16
--- /dev/null
+++ b/kqueue.c
@@ -0,0 +1,300 @@
+/*
+ * 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);
+}
diff --git a/sample/event-test.c b/sample/event-test.c
new file mode 100644 (file)
index 0000000..f4cfa11
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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);
+}
+
diff --git a/sample/time-test.c b/sample/time-test.c
new file mode 100644 (file)
index 0000000..4f42f00
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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);
+}
+
diff --git a/select.c b/select.c
new file mode 100644 (file)
index 0000000..b132988
--- /dev/null
+++ b/select.c
@@ -0,0 +1,213 @@
+/*
+ * 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);
+}
diff --git a/test/test-eof.c b/test/test-eof.c
new file mode 100644 (file)
index 0000000..a1c1eeb
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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);
+}
+
diff --git a/test/test-weof.c b/test/test-weof.c
new file mode 100644 (file)
index 0000000..637063e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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);
+}
+