summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am55
-rw-r--r--README35
-rw-r--r--WIN32-Code/config.h231
-rw-r--r--WIN32-Code/misc.c91
-rw-r--r--WIN32-Code/misc.h11
-rw-r--r--WIN32-Code/win32.c433
-rw-r--r--WIN32-Prj/event_test/event_test.dsp100
-rw-r--r--WIN32-Prj/event_test/test.txt180
-rw-r--r--WIN32-Prj/libevent.dsp128
-rw-r--r--WIN32-Prj/libevent.dsw74
-rw-r--r--WIN32-Prj/signal_test/signal_test.dsp100
-rw-r--r--WIN32-Prj/time_test/time_test.dsp100
-rw-r--r--acconfig.h79
-rw-r--r--buffer.c446
-rw-r--r--compat/sys/_time.h163
-rw-r--r--compat/sys/queue.h488
-rw-r--r--compat/sys/tree.h677
-rw-r--r--configure.in372
-rw-r--r--devpoll.c423
-rw-r--r--epoll.c369
-rw-r--r--epoll_sub.c52
-rw-r--r--evbuffer.c413
-rw-r--r--evdns.3322
-rw-r--r--evdns.c2176
-rw-r--r--evdns.h306
-rw-r--r--event-internal.h56
-rw-r--r--event.3578
-rw-r--r--event.c865
-rw-r--r--event.h339
-rwxr-xr-xevent_rpcgen.py1378
-rw-r--r--event_tagging.c360
-rw-r--r--evhttp.h128
-rw-r--r--evport.c509
-rw-r--r--evsignal.h37
-rw-r--r--http-internal.h137
-rw-r--r--http.c1632
-rwxr-xr-xinstall-sh238
-rw-r--r--kqueue.c413
-rw-r--r--log.c219
-rw-r--r--log.h43
-rwxr-xr-xmissing360
-rwxr-xr-xmkinstalldirs40
-rw-r--r--poll.c377
-rw-r--r--rtsig.c985
-rw-r--r--sample/Makefile.am15
-rw-r--r--sample/event-test.c135
-rw-r--r--sample/signal-test.c62
-rw-r--r--sample/time-test.c70
-rw-r--r--select.c376
-rw-r--r--signal.c212
-rw-r--r--stamp-h.in1
-rw-r--r--strlcpy.c74
-rw-r--r--test/Makefile.am31
-rw-r--r--test/bench.c181
-rw-r--r--test/regress.c923
-rw-r--r--test/regress.h43
-rw-r--r--test/regress.rpc17
-rw-r--r--test/regress_dns.c144
-rw-r--r--test/regress_http.c426
-rw-r--r--test/test-eof.c68
-rw-r--r--test/test-init.c27
-rw-r--r--test/test-time.c68
-rw-r--r--test/test-weof.c68
-rwxr-xr-xtest/test.sh91
64 files changed, 19550 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..30ca7ea5
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,55 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+
+bin_SCRIPTS = event_rpcgen.py
+
+EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h evdns.3 \
+ event.3 \
+ kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
+ evport.c devpoll.c event_rpcgen.py \
+ sample/Makefile.am sample/Makefile.in sample/event-test.c \
+ sample/signal-test.c sample/time-test.c \
+ test/Makefile.am test/Makefile.in test/bench.c test/regress.c \
+ test/test-eof.c test/test-weof.c test/test-time.c \
+ test/test-init.c test/test.sh \
+ compat/sys/queue.h compat/sys/tree.h compat/sys/_time.h \
+ WIN32-Code/config.h WIN32-Code/misc.c \
+ WIN32-Code/win32.c WIN32-Code/misc.h \
+ WIN32-Prj/event_test/event_test.dsp \
+ WIN32-Prj/event_test/test.txt WIN32-Prj/libevent.dsp \
+ WIN32-Prj/libevent.dsw WIN32-Prj/signal_test/signal_test.dsp \
+ WIN32-Prj/time_test/time_test.dsp
+
+lib_LTLIBRARIES = libevent.la
+
+if BUILD_WIN32
+
+SUBDIRS = . sample
+SYS_LIBS = -lws2_32
+SYS_SRC = WIN32-Code/misc.c WIN32-Code/win32.c
+SYS_INCLUDES = -IWIN32-Code
+
+else
+
+SUBDIRS = . sample test
+SYS_LIBS =
+SYS_SRC =
+SYS_INCLUDES =
+
+endif
+
+libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \
+ http.c evhttp.h http-internal.h evdns.c evdns.h $(SYS_SRC)
+libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
+libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:3:0
+
+include_HEADERS = event.h evhttp.h evdns.h
+
+INCLUDES = -Icompat $(SYS_INCLUDES)
+
+man_MANS = event.3 evdns.3
+
+verify: libevent.la
+ cd $(srcdir)/test && make verify
+
+DISTCLEANFILES = *~
diff --git a/README b/README
new file mode 100644
index 00000000..bd4a84ca
--- /dev/null
+++ b/README
@@ -0,0 +1,35 @@
+To build libevent, type
+
+$ ./configure && make
+
+Install as root via
+
+# make install
+
+You can run the regression tests by
+
+$ make verify
+
+Before, reporting any problems, please run the regression tests.
+
+To enable the low-level tracing build the library as:
+
+CFLAGS=-DUSE_DEBUG ./configure [...]
+
+Acknowledgements:
+-----------------
+
+The following people have helped with suggestions, ideas, code or
+fixing bugs:
+
+ Nick Mathewson
+ Andrew Danforth
+ Shie Erlich
+ Mike Davis
+ William Ahern
+ Alexander von Gernler
+ Artur Grabowski
+ Stas Bekman
+ Tassilo von Parseval
+
+If I have forgotten your name, please contact me.
diff --git a/WIN32-Code/config.h b/WIN32-Code/config.h
new file mode 100644
index 00000000..aed9774d
--- /dev/null
+++ b/WIN32-Code/config.h
@@ -0,0 +1,231 @@
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+/* 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
+#undef timersub
+#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 */
+
+#undef HAVE_TIMERCLEAR
+#ifndef HAVE_TIMERCLEAR
+#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
+#endif
+
+#define HAVE_TIMERCMP
+#ifndef HAVE_TIMERCMP
+#undef timercmp
+#define timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#endif
+
+#undef HAVE_TIMERISSET
+#ifndef HAVE_TIMERISSET
+#undef timerisset
+#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
+#define 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 */
+
+/* Define if /dev/poll is available */
+#undef HAVE_DEVPOLL
+
+/* Define if your system supports the epoll system calls */
+#undef HAVE_EPOLL
+
+/* Define if you have the `epoll_ctl' function. */
+#undef HAVE_EPOLL_CTL
+
+/* Define if you have the `err' function. */
+#undef HAVE_ERR
+
+/* Define if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if you have the `kqueue' function. */
+#undef HAVE_KQUEUE
+
+/* Define if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
+/* Define if your system supports POSIX realtime signals */
+#undef HAVE_RTSIG
+
+/* Define if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define if F_SETFD is defined in <fcntl.h> */
+#undef HAVE_SETFD
+
+/* Define if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define if you have the `sigtimedwait' function. */
+#undef HAVE_SIGTIMEDWAIT
+
+/* Define if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/devpoll.h> header file. */
+#undef HAVE_SYS_DEVPOLL_H
+
+/* Define if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
+/* Define if you have the <sys/event.h> header file. */
+#undef HAVE_SYS_EVENT_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/queue.h> header file. */
+#undef HAVE_SYS_QUEUE_H
+
+/* Define if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
+#undef HAVE_TAILQFOREACH
+
+/* Define if timeradd is defined in <sys/time.h> */
+#undef HAVE_TIMERADD
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the `vasprintf' function. */
+#undef HAVE_VASPRINTF
+
+/* Define if kqueue works correctly with pipes */
+#undef HAVE_WORKING_KQUEUE
+
+/* Define if realtime signals work on pipes */
+#undef HAVE_WORKING_RTSIG
+
+/* Name of package */
+#define PACKAGE "libevent"
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Version number of package */
+#define VERSION "1.0b"
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+#define inline __inline
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to unsigned int if you dont have it */
+#undef socklen_t
+
+/* Define to `unsigned short' if <sys/types.h> does not define. */
+#undef u_int16_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef u_int32_t
+
+/* Define to `unsigned long long' if <sys/types.h> does not define. */
+/* #undef u_int64_t */
+
+/* Define to `unsigned char' if <sys/types.h> does not define. */
+/* #undef u_int8_t */
+
+/* Define to __FUNCTION__ or __file__ if your compiler doesn't have __func__ */
+#define __func__ __FUNCTION__
diff --git a/WIN32-Code/misc.c b/WIN32-Code/misc.c
new file mode 100644
index 00000000..6f63ddfe
--- /dev/null
+++ b/WIN32-Code/misc.c
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+#include <sys/timeb.h>
+#include <time.h>
+
+#ifdef __GNUC__
+/*our prototypes for timeval and timezone are in here, just in case the above
+ headers don't have them*/
+#include "misc.h"
+#endif
+
+/****************************************************************************
+ *
+ * Function: gettimeofday(struct timeval *, struct timezone *)
+ *
+ * Purpose: Get current time of day.
+ *
+ * Arguments: tv => Place to store the curent time of day.
+ * tz => Ignored.
+ *
+ * Returns: 0 => Success.
+ *
+ ****************************************************************************/
+
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *tv, struct timezone *tz) {
+ struct _timeb tb;
+
+ if(tv == NULL)
+ return -1;
+
+ _ftime(&tb);
+ tv->tv_sec = tb.time;
+ tv->tv_usec = ((int) tb.millitm) * 1000;
+ return 0;
+}
+#endif
+
+int
+win_read(int fd, void *buf, unsigned int length)
+{
+ DWORD dwBytesRead;
+ int res = ReadFile((HANDLE) fd, buf, length, &dwBytesRead, NULL);
+ if (res == 0) {
+ DWORD error = GetLastError();
+ if (error == ERROR_NO_DATA)
+ return (0);
+ return (-1);
+ } else
+ return (dwBytesRead);
+}
+
+int
+win_write(int fd, void *buf, unsigned int length)
+{
+ DWORD dwBytesWritten;
+ int res = WriteFile((HANDLE) fd, buf, length, &dwBytesWritten, NULL);
+ if (res == 0) {
+ DWORD error = GetLastError();
+ if (error == ERROR_NO_DATA)
+ return (0);
+ return (-1);
+ } else
+ return (dwBytesWritten);
+}
+
+int
+socketpair(int d, int type, int protocol, int *sv)
+{
+ static int count;
+ char buf[64];
+ HANDLE fd;
+ DWORD dwMode;
+ sprintf(buf, "\\\\.\\pipe\\levent-%d", count++);
+ /* Create a duplex pipe which will behave like a socket pair */
+ fd = CreateNamedPipe(buf, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_NOWAIT,
+ PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL);
+ if (fd == INVALID_HANDLE_VALUE)
+ return (-1);
+ sv[0] = (int)fd;
+
+ fd = CreateFile(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd == INVALID_HANDLE_VALUE)
+ return (-1);
+ dwMode = PIPE_NOWAIT;
+ SetNamedPipeHandleState(fd, &dwMode, NULL, NULL);
+ sv[1] = (int)fd;
+
+ return (0);
+} \ No newline at end of file
diff --git a/WIN32-Code/misc.h b/WIN32-Code/misc.h
new file mode 100644
index 00000000..aced5746
--- /dev/null
+++ b/WIN32-Code/misc.h
@@ -0,0 +1,11 @@
+#ifndef MISC_H
+#define MISC_H
+
+struct timezone;
+struct timeval;
+
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *,struct timezone *);
+#endif
+
+#endif
diff --git a/WIN32-Code/win32.c b/WIN32-Code/win32.c
new file mode 100644
index 00000000..701d1c3c
--- /dev/null
+++ b/WIN32-Code/win32.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * Copyright 2003 Michael A. Davis <mike@datanerds.net>
+ * 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 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 <winsock2.h>
+
+#include <windows.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "log.h"
+#include "event.h"
+#include "event-internal.h"
+
+#define XFREE(ptr) do { if (ptr) free(ptr); } while(0)
+
+extern struct event_list timequeue;
+extern struct event_list addqueue;
+extern struct event_list signalqueue;
+
+struct win_fd_set {
+ u_int fd_count;
+ SOCKET fd_array[1];
+};
+
+int evsigcaught[NSIG];
+volatile sig_atomic_t signal_caught = 0;
+/* MSDN says this is required to handle SIGFPE */
+volatile double SIGFPE_REQ = 0.0f;
+
+static void signal_handler(int sig);
+
+void signal_process(void);
+int signal_recalc(void);
+
+struct win32op {
+ int fd_setsz;
+ struct win_fd_set *readset_in;
+ struct win_fd_set *writeset_in;
+ struct win_fd_set *readset_out;
+ struct win_fd_set *writeset_out;
+ struct win_fd_set *exset_out;
+ int n_events;
+ int n_events_alloc;
+ struct event **events;
+};
+
+void *win32_init (void);
+int win32_insert (void *, struct event *);
+int win32_del (void *, struct event *);
+int win32_recalc (struct event_base *base, void *, int);
+int win32_dispatch (struct event_base *base, void *, struct timeval *);
+void win32_dealloc (void *);
+
+struct eventop win32ops = {
+ "win32",
+ win32_init,
+ win32_insert,
+ win32_del,
+ win32_recalc,
+ win32_dispatch,
+ win32_dealloc
+};
+
+#define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET)))
+
+static int
+realloc_fd_sets(struct win32op *op, size_t new_size)
+{
+ size_t size;
+
+ assert(new_size >= op->readset_in->fd_count &&
+ new_size >= op->writeset_in->fd_count);
+ assert(new_size >= 1);
+
+ size = FD_SET_ALLOC_SIZE(new_size);
+ if (!(op->readset_in = realloc(op->readset_in, size)))
+ return (-1);
+ if (!(op->writeset_in = realloc(op->writeset_in, size)))
+ return (-1);
+ if (!(op->readset_out = realloc(op->readset_out, size)))
+ return (-1);
+ if (!(op->exset_out = realloc(op->exset_out, size)))
+ return (-1);
+ if (!(op->writeset_out = realloc(op->writeset_out, size)))
+ return (-1);
+ op->fd_setsz = new_size;
+ return (0);
+}
+
+static int
+timeval_to_ms(struct timeval *tv)
+{
+ return ((tv->tv_sec * 1000) + (tv->tv_usec / 1000));
+}
+
+static int
+do_fd_set(struct win32op *op, SOCKET s, int read)
+{
+ unsigned int i;
+ struct win_fd_set *set = read ? op->readset_in : op->writeset_in;
+ for (i=0;i<set->fd_count;++i) {
+ if (set->fd_array[i]==s)
+ return (0);
+ }
+ if (set->fd_count == op->fd_setsz) {
+ if (realloc_fd_sets(op, op->fd_setsz*2))
+ return (-1);
+ /* set pointer will have changed and needs reiniting! */
+ set = read ? op->readset_in : op->writeset_in;
+ }
+ set->fd_array[set->fd_count] = s;
+ return (set->fd_count++);
+}
+
+static int
+do_fd_clear(struct win32op *op, SOCKET s, int read)
+{
+ unsigned int i;
+ struct win_fd_set *set = read ? op->readset_in : op->writeset_in;
+ for (i=0;i<set->fd_count;++i) {
+ if (set->fd_array[i]==s) {
+ if (--set->fd_count != i) {
+ set->fd_array[i] = set->fd_array[set->fd_count];
+ }
+ return (0);
+ }
+ }
+ return (0);
+}
+
+#define NEVENT 64
+void *
+win32_init(void)
+{
+ struct win32op *winop;
+ size_t size;
+ if (!(winop = calloc(1, sizeof(struct win32op))))
+ return NULL;
+ winop->fd_setsz = NEVENT;
+ size = FD_SET_ALLOC_SIZE(NEVENT);
+ if (!(winop->readset_in = malloc(size)))
+ goto err;
+ if (!(winop->writeset_in = malloc(size)))
+ goto err;
+ if (!(winop->readset_out = malloc(size)))
+ goto err;
+ if (!(winop->writeset_out = malloc(size)))
+ goto err;
+ if (!(winop->exset_out = malloc(size)))
+ goto err;
+ winop->n_events = 0;
+ winop->n_events_alloc = NEVENT;
+ if (!(winop->events = malloc(NEVENT*sizeof(struct event*))))
+ goto err;
+ winop->readset_in->fd_count = winop->writeset_in->fd_count = 0;
+ winop->readset_out->fd_count = winop->writeset_out->fd_count
+ = winop->exset_out->fd_count = 0;
+
+ return (winop);
+ err:
+ XFREE(winop->readset_in);
+ XFREE(winop->writeset_in);
+ XFREE(winop->readset_out);
+ XFREE(winop->writeset_out);
+ XFREE(winop->exset_out);
+ XFREE(winop->events);
+ XFREE(winop);
+ return (NULL);
+}
+
+int
+win32_recalc(struct event_base *base, void *arg, int max)
+{
+ return (signal_recalc());
+}
+
+int
+win32_insert(void *op, struct event *ev)
+{
+ struct win32op *win32op = op;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL) {
+ if (ev->ev_events & (EV_READ|EV_WRITE))
+ event_errx(1, "%s: EV_SIGNAL incompatible use",
+ __func__);
+ if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1)
+ return (-1);
+
+ return (0);
+ }
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ for (i=0;i<win32op->n_events;++i) {
+ if(win32op->events[i] == ev) {
+ event_debug(("%s: Event for %d already inserted.",
+ __func__, (int)ev->ev_fd));
+ return (0);
+ }
+ }
+ event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd));
+ if (ev->ev_events & EV_READ) {
+ if (do_fd_set(win32op, ev->ev_fd, 1)<0)
+ return (-1);
+ }
+ if (ev->ev_events & EV_WRITE) {
+ if (do_fd_set(win32op, ev->ev_fd, 0)<0)
+ return (-1);
+ }
+
+ if (win32op->n_events_alloc == win32op->n_events) {
+ size_t sz;
+ win32op->n_events_alloc *= 2;
+ sz = sizeof(struct event*)*win32op->n_events_alloc;
+ if (!(win32op->events = realloc(win32op->events, sz)))
+ return (-1);
+ }
+ win32op->events[win32op->n_events++] = ev;
+
+ return (0);
+}
+
+int
+win32_del(void *op, struct event *ev)
+{
+ struct win32op *win32op = op;
+ int i, found;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return ((int)signal(EVENT_SIGNAL(ev), SIG_IGN));
+
+ found = -1;
+ for (i=0;i<win32op->n_events;++i) {
+ if(win32op->events[i] == ev) {
+ found = i;
+ break;
+ }
+ }
+ if (found < 0) {
+ event_debug(("%s: Unable to remove non-inserted event for %d",
+ __func__, ev->ev_fd));
+ return (-1);
+ }
+ event_debug(("%s: Removing event for %d", __func__, ev->ev_fd));
+ if (ev->ev_events & EV_READ)
+ do_fd_clear(win32op, ev->ev_fd, 1);
+ if (ev->ev_events & EV_WRITE)
+ do_fd_clear(win32op, ev->ev_fd, 0);
+
+ if (i != --win32op->n_events) {
+ win32op->events[i] = win32op->events[win32op->n_events];
+ }
+
+ return 0;
+}
+
+static void
+fd_set_copy(struct win_fd_set *out, const struct win_fd_set *in)
+{
+ out->fd_count = in->fd_count;
+ memcpy(out->fd_array, in->fd_array, in->fd_count * (sizeof(SOCKET)));
+}
+
+/*
+ static void dump_fd_set(struct win_fd_set *s)
+ {
+ unsigned int i;
+ printf("[ ");
+ for(i=0;i<s->fd_count;++i)
+ printf("%d ",(int)s->fd_array[i]);
+ printf("]\n");
+ }
+*/
+
+int
+win32_dispatch(struct event_base *base, void *op,
+ struct timeval *tv)
+{
+ struct win32op *win32op = op;
+ int res = 0;
+ int i;
+ int fd_count;
+
+ fd_set_copy(win32op->readset_out, win32op->readset_in);
+ fd_set_copy(win32op->exset_out, win32op->readset_in);
+ fd_set_copy(win32op->writeset_out, win32op->writeset_in);
+
+ fd_count =
+ (win32op->readset_out->fd_count > win32op->writeset_out->fd_count) ?
+ win32op->readset_out->fd_count : win32op->writeset_out->fd_count;
+
+ if (!fd_count) {
+ /* Windows doesn't like you to call select() with no sockets */
+ Sleep(timeval_to_ms(tv));
+ signal_process();
+ return (0);
+ }
+
+ res = select(fd_count,
+ (struct fd_set*)win32op->readset_out,
+ (struct fd_set*)win32op->writeset_out,
+ (struct fd_set*)win32op->exset_out, tv);
+
+ event_debug(("%s: select returned %d", __func__, res));
+
+ if(res <= 0) {
+ signal_process();
+ return res;
+ }
+
+ for (i=0;i<win32op->n_events;++i) {
+ struct event *ev;
+ int got = 0;
+ ev = win32op->events[i];
+ if ((ev->ev_events & EV_READ)) {
+ if (FD_ISSET(ev->ev_fd, win32op->readset_out) ||
+ FD_ISSET(ev->ev_fd, win32op->exset_out)) {
+ got |= EV_READ;
+ }
+ }
+ if ((ev->ev_events & EV_WRITE)) {
+ if (FD_ISSET(ev->ev_fd, win32op->writeset_out)) {
+ got |= EV_WRITE;
+ }
+ }
+ if (!got)
+ continue;
+ if (!(ev->ev_events & EV_PERSIST)) {
+ event_del(ev);
+ }
+ event_active(ev,got,1);
+ }
+
+ if (signal_recalc() == -1)
+ return (-1);
+
+ return (0);
+}
+
+void
+win32_dealloc(void *arg)
+{
+ struct win32op *win32op = arg;
+
+ if (win32op->readset_in)
+ free(win32op->readset_in);
+ if (win32op->writeset_in)
+ free(win32op->writeset_in);
+ if (win32op->readset_out)
+ free(win32op->readset_out);
+ if (win32op->writeset_out)
+ free(win32op->writeset_out);
+ if (win32op->exset_out)
+ free(win32op->exset_out);
+ if (win32op->events)
+ free(win32op->events);
+
+ memset(win32op, 0, sizeof(win32op));
+ free(win32op);
+}
+
+static void
+signal_handler(int sig)
+{
+ evsigcaught[sig]++;
+ signal_caught = 1;
+}
+
+int
+signal_recalc(void)
+{
+ struct event *ev;
+
+ /* Reinstall our signal handler. */
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+ if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+void
+signal_process(void)
+{
+ struct event *ev;
+ short ncalls;
+
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+ ncalls = evsigcaught[EVENT_SIGNAL(ev)];
+ if (ncalls) {
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, EV_SIGNAL, ncalls);
+ }
+ }
+
+ memset(evsigcaught, 0, sizeof(evsigcaught));
+ signal_caught = 0;
+}
diff --git a/WIN32-Prj/event_test/event_test.dsp b/WIN32-Prj/event_test/event_test.dsp
new file mode 100644
index 00000000..bdc3fdbd
--- /dev/null
+++ b/WIN32-Prj/event_test/event_test.dsp
@@ -0,0 +1,100 @@
+# Microsoft Developer Studio Project File - Name="event_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=event_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "event_test.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "event_test.mak" CFG="event_test - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "event_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "event_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "event_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\\" /I "..\..\WIN32-Code" /I "..\..\compat" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "event_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\\" /I "..\..\WIN32-Code" /I "..\..\compat" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "event_test - Win32 Release"
+# Name "event_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\..\sample\event-test.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/WIN32-Prj/event_test/test.txt b/WIN32-Prj/event_test/test.txt
new file mode 100644
index 00000000..d131054d
--- /dev/null
+++ b/WIN32-Prj/event_test/test.txt
@@ -0,0 +1,180 @@
+
+ Platform SDK: File Storage
+ReadFile
+The ReadFile function reads data from a file, starting at the position indicated by the file pointer. After the read operation has been completed, the file pointer is adjusted by the number of bytes actually read, unless the file handle is created with the overlapped attribute. If the file handle is created for overlapped input and output (I/O), the application must adjust the position of the file pointer after the read operation.
+
+This function is designed for both synchronous and asynchronous operation. The ReadFileEx function is designed solely for asynchronous operation. It lets an application perform other processing during a file read operation.
+
+BOOL ReadFile(
+ HANDLE hFile, // handle to file
+ LPVOID lpBuffer, // data buffer
+ DWORD nNumberOfBytesToRead, // number of bytes to read
+ LPDWORD lpNumberOfBytesRead, // number of bytes read
+ LPOVERLAPPED lpOverlapped // overlapped buffer
+);
+Parameters
+hFile
+[in] Handle to the file to be read. The file handle must have been created with GENERIC_READ access to the file.
+Windows NT/2000/XP: For asynchronous read operations, hFile can be any handle opened with the FILE_FLAG_OVERLAPPED flag by the CreateFile function, or a socket handle returned by the socket or accept function.
+
+Windows 95/98/Me: For asynchronous read operations, hFile can be a communications resource opened with the FILE_FLAG_OVERLAPPED flag by CreateFile, or a socket handle returned by socket or accept. You cannot perform asynchronous read operations on mailslots, named pipes, or disk files.
+
+lpBuffer
+[out] Pointer to the buffer that receives the data read from the file.
+nNumberOfBytesToRead
+[in] Specifies the number of bytes to be read from the file.
+lpNumberOfBytesRead
+[out] Pointer to the variable that receives the number of bytes read. ReadFile sets this value to zero before doing any work or error checking. If this parameter is zero when ReadFile returns TRUE on a named pipe, the other end of the message-mode pipe called the WriteFile function with nNumberOfBytesToWrite set to zero.
+Windows NT/2000/XP: If lpOverlapped is NULL, lpNumberOfBytesRead cannot be NULL. If lpOverlapped is not NULL, lpNumberOfBytesRead can be NULL. If this is an overlapped read operation, you can get the number of bytes read by calling GetOverlappedResult. If hFile is associated with an I/O completion port, you can get the number of bytes read by calling GetQueuedCompletionStatus.
+
+If I/O completion ports are used and you are using a callback routine to free the memory allocated to the OVERLAPPED structure pointed to by the lpOverlapped parameter, specify NULL as the value of this parameter to avoid a memory corruption problem during the deallocation. This memory corruption problem will cause an invalid number of bytes to be returned in this parameter.
+
+Windows 95/98/Me: This parameter cannot be NULL.
+
+lpOverlapped
+[in] Pointer to an OVERLAPPED structure. This structure is required if hFile was created with FILE_FLAG_OVERLAPPED.
+If hFile was opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must not be NULL. It must point to a valid OVERLAPPED structure. If hFile was created with FILE_FLAG_OVERLAPPED and lpOverlapped is NULL, the function can incorrectly report that the read operation is complete.
+
+If hFile was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the read operation starts at the offset specified in the OVERLAPPED structure and ReadFile may return before the read operation has been completed. In this case, ReadFile returns FALSE and the GetLastError function returns ERROR_IO_PENDING. This allows the calling process to continue while the read operation finishes. The event specified in the OVERLAPPED structure is set to the signaled state upon completion of the read operation.
+
+If hFile was not opened with FILE_FLAG_OVERLAPPED and lpOverlapped is NULL, the read operation starts at the current file position and ReadFile does not return until the operation has been completed.
+
+Windows NT/2000/XP: If hFile is not opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the read operation starts at the offset specified in the OVERLAPPED structure. ReadFile does not return until the read operation has been completed.
+
+Windows 95/98/Me: For operations on files, disks, pipes, or mailslots, this parameter must be NULL; a pointer to an OVERLAPPED structure causes the call to fail. However, Windows 95/98/Me supports overlapped I/O on serial and parallel ports.
+
+Return Values
+The ReadFile function returns when one of the following is true: a write operation completes on the write end of the pipe, the number of bytes requested has been read, or an error occurs.
+
+If the function succeeds, the return value is nonzero.
+
+If the return value is nonzero and the number of bytes read is zero, the file pointer was beyond the current end of the file at the time of the read operation. However, if the file was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the return value is FALSE and GetLastError returns ERROR_HANDLE_EOF when the file pointer goes beyond the current end of file.
+
+If the function fails, the return value is zero. To get extended error information, call GetLastError.
+
+Remarks
+If part of the file is locked by another process and the read operation overlaps the locked portion, this function fails.
+
+An application must meet certain requirements when working with files opened with FILE_FLAG_NO_BUFFERING:
+
+File access must begin at byte offsets within the file that are integer multiples of the volume's sector size. To determine a volume's sector size, call the GetDiskFreeSpace function.
+File access must be for numbers of bytes that are integer multiples of the volume's sector size. For example, if the sector size is 512 bytes, an application can request reads and writes of 512, 1024, or 2048 bytes, but not of 335, 981, or 7171 bytes.
+Buffer addresses for read and write operations must be sector aligned (aligned on addresses in memory that are integer multiples of the volume's sector size). One way to sector align buffers is to use the VirtualAlloc function to allocate the buffers. This function allocates memory that is aligned on addresses that are integer multiples of the system's page size. Because both page and volume sector sizes are powers of 2, memory aligned by multiples of the system's page size is also aligned by multiples of the volume's sector size.
+Accessing the input buffer while a read operation is using the buffer may lead to corruption of the data read into that buffer. Applications must not read from, write to, reallocate, or free the input buffer that a read operation is using until the read operation completes.
+
+Characters can be read from the console input buffer by using ReadFile with a handle to console input. The console mode determines the exact behavior of the ReadFile function.
+
+If a named pipe is being read in message mode and the next message is longer than the nNumberOfBytesToRead parameter specifies, ReadFile returns FALSE and GetLastError returns ERROR_MORE_DATA. The remainder of the message may be read by a subsequent call to the ReadFile or PeekNamedPipe function.
+
+When reading from a communications device, the behavior of ReadFile is governed by the current communication time-outs as set and retrieved using the SetCommTimeouts and GetCommTimeouts functions. Unpredictable results can occur if you fail to set the time-out values. For more information about communication time-outs, see COMMTIMEOUTS.
+
+If ReadFile attempts to read from a mailslot whose buffer is too small, the function returns FALSE and GetLastError returns ERROR_INSUFFICIENT_BUFFER.
+
+If the anonymous write pipe handle has been closed and ReadFile attempts to read using the corresponding anonymous read pipe handle, the function returns FALSE and GetLastError returns ERROR_BROKEN_PIPE.
+
+The ReadFile function may fail and return ERROR_INVALID_USER_BUFFER or ERROR_NOT_ENOUGH_MEMORY whenever there are too many outstanding asynchronous I/O requests.
+
+The ReadFile code to check for the end-of-file condition (eof) differs for synchronous and asynchronous read operations.
+
+When a synchronous read operation reaches the end of a file, ReadFile returns TRUE and sets *lpNumberOfBytesRead to zero. The following sample code tests for end-of-file for a synchronous read operation:
+
+// Attempt a synchronous read operation.
+bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL) ;
+// Check for end of file.
+if (bResult && nBytesRead == 0, )
+{
+ // we're at the end of the file
+}
+An asynchronous read operation can encounter the end of a file during the initiating call to ReadFile, or during subsequent asynchronous operation.
+
+If EOF is detected at ReadFile time for an asynchronous read operation, ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF.
+
+If EOF is detected during subsequent asynchronous operation, the call to GetOverlappedResult to obtain the results of that operation returns FALSE and GetLastError returns ERROR_HANDLE_EOF.
+
+To cancel all pending asynchronous I/O operations, use the CancelIo function. This function only cancels operations issued by the calling thread for the specified file handle. I/O operations that are canceled complete with the error ERROR_OPERATION_ABORTED.
+
+If you are attempting to read from a floppy drive that does not have a floppy disk, the system displays a message box prompting the user to retry the operation. To prevent the system from displaying this message box, call the SetErrorMode function with SEM_NOOPENFILEERRORBOX.
+
+The following sample code illustrates testing for end-of-file for an asynchronous read operation:
+
+// set up overlapped structure fields
+gOverLapped.Offset = 0;
+gOverLapped.OffsetHigh = 0;
+gOverLapped.hEvent = hEvent;
+
+// attempt an asynchronous read operation
+bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead,
+ &gOverlapped) ;
+
+// if there was a problem, or the async. operation's still pending ...
+if (!bResult)
+{
+ // deal with the error code
+ switch (dwError = GetLastError())
+ {
+ case ERROR_HANDLE_EOF:
+ {
+ // we have reached the end of the file
+ // during the call to ReadFile
+
+ // code to handle that
+ }
+
+ case ERROR_IO_PENDING:
+ {
+ // asynchronous i/o is still in progress
+
+ // do something else for a while
+ GoDoSomethingElse() ;
+
+ // check on the results of the asynchronous read
+ bResult = GetOverlappedResult(hFile, &gOverlapped,
+ &nBytesRead, FALSE) ;
+
+ // if there was a problem ...
+ if (!bResult)
+ {
+ // deal with the error code
+ switch (dwError = GetLastError())
+ {
+ case ERROR_HANDLE_EOF:
+ {
+ // we have reached the end of
+ // the file during asynchronous
+ // operation
+ }
+
+ // deal with other error cases
+ }
+ }
+ } // end case
+
+ // deal with other error cases
+
+ } // end switch
+} // end if
+Example Code
+For an example, see Reading, Writing, and Locking Files.
+
+Requirements
+ Windows NT/2000/XP: Included in Windows NT 3.1 and later.
+ Windows 95/98/Me: Included in Windows 95 and later.
+ Header: Declared in Winbase.h; include Windows.h.
+ Library: Use Kernel32.lib.
+
+See Also
+File I/O Overview, File I/O Functions, CancelIo, CreateFile, GetCommTimeouts, GetOverlappedResult, GetQueuedCompletionStatus, OVERLAPPED, PeekNamedPipe, ReadFileEx, SetCommTimeouts, SetErrorMode, WriteFile
+
+Platform SDK Release: November 2001 What did you think of this topic?
+Let us know. Order a Platform SDK CD Online
+(U.S/Canada) (International)
+
+
+
+Requirements
+ Windows NT/2000/XP: Included in Windows NT 3.1 and later.
+ Windows 95/98/Me: Included in Windows 95 and later.
+ Header: Declared in Winbase.h; include Windows.h.
+ Library: Use Kernel32.lib.
+See Also
+File I/O Overview, File I/O Functions, CancelIo, CreateFile, GetCommTimeouts, GetOverlappedResult, GetQueuedCompletionStatus, OVERLAPPED, PeekNamedPipe, ReadFileEx, SetCommTimeouts, SetErrorMode, WriteFile
diff --git a/WIN32-Prj/libevent.dsp b/WIN32-Prj/libevent.dsp
new file mode 100644
index 00000000..61e9bc84
--- /dev/null
+++ b/WIN32-Prj/libevent.dsp
@@ -0,0 +1,128 @@
+# Microsoft Developer Studio Project File - Name="libevent" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=libevent - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libevent.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libevent.mak" CFG="libevent - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libevent - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "libevent - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libevent - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\\" /I "..\WIN32-Code" /I "..\compat" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "libevent - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\\" /I "..\WIN32-Code" /I "..\compat" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "libevent - Win32 Release"
+# Name "libevent - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\log.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\event.c
+# End Source File
+# Begin Source File
+
+SOURCE="..\WIN32-Code\misc.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\WIN32-Code\win32.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\acconfig.h
+# End Source File
+# Begin Source File
+
+SOURCE="..\WIN32-Code\config.h"
+# End Source File
+# Begin Source File
+
+SOURCE=..\compat\err.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\event.h
+# End Source File
+# Begin Source File
+
+SOURCE="..\WIN32-Code\misc.h"
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/WIN32-Prj/libevent.dsw b/WIN32-Prj/libevent.dsw
new file mode 100644
index 00000000..99a00cdf
--- /dev/null
+++ b/WIN32-Prj/libevent.dsw
@@ -0,0 +1,74 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "event_test"=".\event_test\event_test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libevent
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "libevent"=".\libevent.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "signal_test"=".\signal_test\signal_test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libevent
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "time_test"=".\time_test\time_test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libevent
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/WIN32-Prj/signal_test/signal_test.dsp b/WIN32-Prj/signal_test/signal_test.dsp
new file mode 100644
index 00000000..1a520b08
--- /dev/null
+++ b/WIN32-Prj/signal_test/signal_test.dsp
@@ -0,0 +1,100 @@
+# Microsoft Developer Studio Project File - Name="signal_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=signal_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "signal_test.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "signal_test.mak" CFG="signal_test - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "signal_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "signal_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "signal_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "signal_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\\" /I "..\..\WIN32-Code" /I "..\..\compat" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "signal_test - Win32 Release"
+# Name "signal_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\..\sample\signal-test.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/WIN32-Prj/time_test/time_test.dsp b/WIN32-Prj/time_test/time_test.dsp
new file mode 100644
index 00000000..0a3cf63a
--- /dev/null
+++ b/WIN32-Prj/time_test/time_test.dsp
@@ -0,0 +1,100 @@
+# Microsoft Developer Studio Project File - Name="time_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=time_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "time_test.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "time_test.mak" CFG="time_test - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "time_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "time_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "time_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "time_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\\" /I "..\..\WIN32-Code" /I "..\..\compat" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "time_test - Win32 Release"
+# Name "time_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\..\sample\time-test.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/acconfig.h b/acconfig.h
new file mode 100644
index 00000000..eab2a275
--- /dev/null
+++ b/acconfig.h
@@ -0,0 +1,79 @@
+/* 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
+#undef timersub
+#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 */
+
+#undef HAVE_TIMERCLEAR
+#ifndef HAVE_TIMERCLEAR
+#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
+#endif
+
+#undef HAVE_TIMERCMP
+#ifndef HAVE_TIMERCMP
+#undef timercmp
+#define timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#endif
+
+#undef HAVE_TIMERISSET
+#ifndef HAVE_TIMERISSET
+#undef timerisset
+#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+/* 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 */
+
+/* Define to __FUNCTION__ or __file__ if your compiler doesn't have __func__ */
+#undef __func__
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 00000000..e09cc3d3
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2002, 2003 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. 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_VASPRINTF
+/* If we have vasprintf, we need to define this before we include stdio.h. */
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "event.h"
+
+struct evbuffer *
+evbuffer_new(void)
+{
+ struct evbuffer *buffer;
+
+ buffer = calloc(1, sizeof(struct evbuffer));
+
+ return (buffer);
+}
+
+void
+evbuffer_free(struct evbuffer *buffer)
+{
+ if (buffer->orig_buffer != NULL)
+ free(buffer->orig_buffer);
+ free(buffer);
+}
+
+/*
+ * This is a destructive add. The data from one buffer moves into
+ * the other buffer.
+ */
+
+#define SWAP(x,y) do { \
+ (x)->buffer = (y)->buffer; \
+ (x)->orig_buffer = (y)->orig_buffer; \
+ (x)->misalign = (y)->misalign; \
+ (x)->totallen = (y)->totallen; \
+ (x)->off = (y)->off; \
+} while (0)
+
+int
+evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
+{
+ int res;
+
+ /* Short cut for better performance */
+ if (outbuf->off == 0) {
+ struct evbuffer tmp;
+ size_t oldoff = inbuf->off;
+
+ /* Swap them directly */
+ SWAP(&tmp, outbuf);
+ SWAP(outbuf, inbuf);
+ SWAP(inbuf, &tmp);
+
+ /*
+ * Optimization comes with a price; we need to notify the
+ * buffer if necessary of the changes. oldoff is the amount
+ * of data that we tranfered from inbuf to outbuf
+ */
+ if (inbuf->off != oldoff && inbuf->cb != NULL)
+ (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
+ if (oldoff && outbuf->cb != NULL)
+ (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
+
+ return (0);
+ }
+
+ res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
+ if (res == 0) {
+ /* We drain the input buffer on success */
+ evbuffer_drain(inbuf, inbuf->off);
+ }
+
+ return (res);
+}
+
+int
+evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
+{
+ char *buffer;
+ size_t space;
+ size_t oldoff = buf->off;
+ int sz;
+
+ for (;;) {
+ buffer = buf->buffer + buf->off;
+ space = buf->totallen - buf->misalign - buf->off;
+
+#ifdef WIN32
+ sz = vsnprintf(buffer, space - 1, fmt, ap);
+ buffer[space - 1] = '\0';
+#else
+ sz = vsnprintf(buffer, space, fmt, ap);
+#endif
+ if (sz == -1)
+ return (-1);
+ if (sz < space) {
+ buf->off += sz;
+ if (buf->cb != NULL)
+ (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+ return (sz);
+ }
+ if (evbuffer_expand(buf, sz + 1) == -1)
+ return (-1);
+
+ }
+ /* NOTREACHED */
+}
+
+int
+evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
+{
+ int res = -1;
+ va_list ap;
+
+ va_start(ap, fmt);
+ res = evbuffer_add_vprintf(buf, fmt, ap);
+ va_end(ap);
+
+ return (res);
+}
+
+/* Reads data from an event buffer and drains the bytes read */
+
+int
+evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
+{
+ size_t nread = datlen;
+ if (nread >= buf->off)
+ nread = buf->off;
+
+ memcpy(data, buf->buffer, nread);
+ evbuffer_drain(buf, nread);
+
+ return (nread);
+}
+
+/*
+ * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
+ * The returned buffer needs to be freed by the called.
+ */
+
+char *
+evbuffer_readline(struct evbuffer *buffer)
+{
+ u_char *data = EVBUFFER_DATA(buffer);
+ size_t len = EVBUFFER_LENGTH(buffer);
+ char *line;
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\r' || data[i] == '\n')
+ break;
+ }
+
+ if (i == len)
+ return (NULL);
+
+ if ((line = malloc(i + 1)) == NULL) {
+ fprintf(stderr, "%s: out of memory\n", __func__);
+ evbuffer_drain(buffer, i);
+ return (NULL);
+ }
+
+ memcpy(line, data, i);
+ line[i] = '\0';
+
+ /*
+ * Some protocols terminate a line with '\r\n', so check for
+ * that, too.
+ */
+ if ( i < len - 1 ) {
+ char fch = data[i], sch = data[i+1];
+
+ /* Drain one more character if needed */
+ if ( (sch == '\r' || sch == '\n') && sch != fch )
+ i += 1;
+ }
+
+ evbuffer_drain(buffer, i + 1);
+
+ return (line);
+}
+
+/* Adds data to an event buffer */
+
+static inline void
+evbuffer_align(struct evbuffer *buf)
+{
+ memmove(buf->orig_buffer, buf->buffer, buf->off);
+ buf->buffer = buf->orig_buffer;
+ buf->misalign = 0;
+}
+
+/* Expands the available space in the event buffer to at least datlen */
+
+int
+evbuffer_expand(struct evbuffer *buf, size_t datlen)
+{
+ size_t need = buf->misalign + buf->off + datlen;
+
+ /* If we can fit all the data, then we don't have to do anything */
+ if (buf->totallen >= need)
+ return (0);
+
+ /*
+ * If the misalignment fulfills our data needs, we just force an
+ * alignment to happen. Afterwards, we have enough space.
+ */
+ if (buf->misalign >= datlen) {
+ evbuffer_align(buf);
+ } else {
+ void *newbuf;
+ size_t length = buf->totallen;
+
+ if (length < 256)
+ length = 256;
+ while (length < need)
+ length <<= 1;
+
+ if (buf->orig_buffer != buf->buffer)
+ evbuffer_align(buf);
+ if ((newbuf = realloc(buf->buffer, length)) == NULL)
+ return (-1);
+
+ buf->orig_buffer = buf->buffer = newbuf;
+ buf->totallen = length;
+ }
+
+ return (0);
+}
+
+int
+evbuffer_add(struct evbuffer *buf, void *data, size_t datlen)
+{
+ size_t need = buf->misalign + buf->off + datlen;
+ size_t oldoff = buf->off;
+
+ if (buf->totallen < need) {
+ if (evbuffer_expand(buf, datlen) == -1)
+ return (-1);
+ }
+
+ memcpy(buf->buffer + buf->off, data, datlen);
+ buf->off += datlen;
+
+ if (datlen && buf->cb != NULL)
+ (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+
+ return (0);
+}
+
+void
+evbuffer_drain(struct evbuffer *buf, size_t len)
+{
+ size_t oldoff = buf->off;
+
+ if (len >= buf->off) {
+ buf->off = 0;
+ buf->buffer = buf->orig_buffer;
+ buf->misalign = 0;
+ goto done;
+ }
+
+ buf->buffer += len;
+ buf->misalign += len;
+
+ buf->off -= len;
+
+ done:
+ /* Tell someone about changes in this buffer */
+ if (buf->off != oldoff && buf->cb != NULL)
+ (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+
+}
+
+/*
+ * Reads data from a file descriptor into a buffer.
+ */
+
+#define EVBUFFER_MAX_READ 4096
+
+int
+evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
+{
+ u_char *p;
+ size_t oldoff = buf->off;
+ int n = EVBUFFER_MAX_READ;
+#ifdef WIN32
+ DWORD dwBytesRead;
+#endif
+
+#ifdef FIONREAD
+ if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
+ n = EVBUFFER_MAX_READ;
+ } else if (n > EVBUFFER_MAX_READ && n > howmuch) {
+ /*
+ * It's possible that a lot of data is available for
+ * reading. We do not want to exhaust resources
+ * before the reader has a chance to do something
+ * about it. If the reader does not tell us how much
+ * data we should read, we artifically limit it.
+ */
+ if (n > buf->totallen << 2)
+ n = buf->totallen << 2;
+ if (n < EVBUFFER_MAX_READ)
+ n = EVBUFFER_MAX_READ;
+ }
+#endif
+ if (howmuch < 0 || howmuch > n)
+ howmuch = n;
+
+ /* If we don't have FIONREAD, we might waste some space here */
+ if (evbuffer_expand(buf, howmuch) == -1)
+ return (-1);
+
+ /* We can append new data at this point */
+ p = buf->buffer + buf->off;
+
+#ifndef WIN32
+ n = read(fd, p, howmuch);
+ if (n == -1)
+ return (-1);
+ if (n == 0)
+ return (0);
+#else
+ n = ReadFile((HANDLE)fd, p, howmuch, &dwBytesRead, NULL);
+ if (n == 0)
+ return (-1);
+ if (dwBytesRead == 0)
+ return (0);
+ n = dwBytesRead;
+#endif
+
+ buf->off += n;
+
+ /* Tell someone about changes in this buffer */
+ if (buf->off != oldoff && buf->cb != NULL)
+ (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+
+ return (n);
+}
+
+int
+evbuffer_write(struct evbuffer *buffer, int fd)
+{
+ int n;
+#ifdef WIN32
+ DWORD dwBytesWritten;
+#endif
+
+#ifndef WIN32
+ n = write(fd, buffer->buffer, buffer->off);
+ if (n == -1)
+ return (-1);
+ if (n == 0)
+ return (0);
+#else
+ n = WriteFile((HANDLE)fd, buffer->buffer, buffer->off, &dwBytesWritten, NULL);
+ if (n == 0)
+ return (-1);
+ if (dwBytesWritten == 0)
+ return (0);
+ n = dwBytesWritten;
+#endif
+ evbuffer_drain(buffer, n);
+
+ return (n);
+}
+
+u_char *
+evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
+{
+ size_t remain = buffer->off;
+ u_char *search = buffer->buffer;
+ u_char *p;
+
+ while ((p = memchr(search, *what, remain)) != NULL && remain >= len) {
+ if (memcmp(p, what, len) == 0)
+ return (p);
+
+ search = p + 1;
+ remain = buffer->off - (size_t)(search - buffer->buffer);
+ }
+
+ return (NULL);
+}
+
+void evbuffer_setcb(struct evbuffer *buffer,
+ void (*cb)(struct evbuffer *, size_t, size_t, void *),
+ void *cbarg)
+{
+ buffer->cb = cb;
+ buffer->cbarg = cbarg;
+}
diff --git a/compat/sys/_time.h b/compat/sys/_time.h
new file mode 100644
index 00000000..8cabb0d5
--- /dev/null
+++ b/compat/sys/_time.h
@@ -0,0 +1,163 @@
+/* $OpenBSD: time.h,v 1.11 2000/10/10 13:36:48 itojun Exp $ */
+/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)time.h 8.2 (Berkeley) 7/10/94
+ */
+
+#ifndef _SYS_TIME_H_
+#define _SYS_TIME_H_
+
+#include <sys/types.h>
+
+/*
+ * Structure returned by gettimeofday(2) system call,
+ * and used in other calls.
+ */
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+
+/*
+ * Structure defined by POSIX.1b to be like a timeval.
+ */
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* and nanoseconds */
+};
+
+#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+}
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+}
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+#define DST_NONE 0 /* not on dst */
+#define DST_USA 1 /* USA style dst */
+#define DST_AUST 2 /* Australian style dst */
+#define DST_WET 3 /* Western European dst */
+#define DST_MET 4 /* Middle European dst */
+#define DST_EET 5 /* Eastern European dst */
+#define DST_CAN 6 /* Canada */
+
+/* Operations on timevals. */
+#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
+#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#define timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#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)
+
+/* Operations on timespecs. */
+#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0
+#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
+#define timespeccmp(tsp, usp, cmp) \
+ (((tsp)->tv_sec == (usp)->tv_sec) ? \
+ ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
+ ((tsp)->tv_sec cmp (usp)->tv_sec))
+#define timespecadd(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec >= 1000000000L) { \
+ (vsp)->tv_sec++; \
+ (vsp)->tv_nsec -= 1000000000L; \
+ } \
+ } while (0)
+#define timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
+
+/*
+ * Names of the interval timers, and structure
+ * defining a timer setting.
+ */
+#define ITIMER_REAL 0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF 2
+
+struct itimerval {
+ struct timeval it_interval; /* timer interval */
+ struct timeval it_value; /* current value */
+};
+
+/*
+ * Getkerninfo clock information structure
+ */
+struct clockinfo {
+ int hz; /* clock frequency */
+ int tick; /* micro-seconds per hz tick */
+ int tickadj; /* clock skew rate for adjtime() */
+ int stathz; /* statistics clock frequency */
+ int profhz; /* profiling clock frequency */
+};
+
+#define CLOCK_REALTIME 0
+#define CLOCK_VIRTUAL 1
+#define CLOCK_PROF 2
+
+#define TIMER_RELTIME 0x0 /* relative timer */
+#define TIMER_ABSTIME 0x1 /* absolute timer */
+
+/* --- stuff got cut here - niels --- */
+
+#endif /* !_SYS_TIME_H_ */
diff --git a/compat/sys/queue.h b/compat/sys/queue.h
new file mode 100644
index 00000000..c0956ddc
--- /dev/null
+++ b/compat/sys/queue.h
@@ -0,0 +1,488 @@
+/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#ifndef WIN32
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+#endif
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
+ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#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)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/compat/sys/tree.h b/compat/sys/tree.h
new file mode 100644
index 00000000..6a3381ca
--- /dev/null
+++ b/compat/sys/tree.h
@@ -0,0 +1,677 @@
+/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
+/*
+ * Copyright 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.
+ *
+ * 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 _SYS_TREE_H_
+#define _SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure. Every operation
+ * on the tree causes a splay to happen. The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree. On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n). The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute. It fulfills a set of conditions:
+ * - every search path from the root to a leaf consists of the
+ * same number of black nodes,
+ * - each red node (except for the root) has a black parent,
+ * - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type) \
+struct name { \
+ struct type *sph_root; /* root of the tree */ \
+}
+
+#define SPLAY_INITIALIZER(root) \
+ { NULL }
+
+#define SPLAY_INIT(root) do { \
+ (root)->sph_root = NULL; \
+} while (0)
+
+#define SPLAY_ENTRY(type) \
+struct { \
+ struct type *spe_left; /* left element */ \
+ struct type *spe_right; /* right element */ \
+}
+
+#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
+#define SPLAY_ROOT(head) (head)->sph_root
+#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do { \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do { \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
+ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp) \
+void name##_SPLAY(struct name *, struct type *); \
+void name##_SPLAY_MINMAX(struct name *, int); \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
+ \
+/* Finds the node with the same key as elm */ \
+static __inline struct type * \
+name##_SPLAY_FIND(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) \
+ return(NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) \
+ return (head->sph_root); \
+ return (NULL); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_NEXT(struct name *head, struct type *elm) \
+{ \
+ name##_SPLAY(head, elm); \
+ if (SPLAY_RIGHT(elm, field) != NULL) { \
+ elm = SPLAY_RIGHT(elm, field); \
+ while (SPLAY_LEFT(elm, field) != NULL) { \
+ elm = SPLAY_LEFT(elm, field); \
+ } \
+ } else \
+ elm = NULL; \
+ return (elm); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_MIN_MAX(struct name *head, int val) \
+{ \
+ name##_SPLAY_MINMAX(head, val); \
+ return (SPLAY_ROOT(head)); \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp) \
+struct type * \
+name##_SPLAY_INSERT(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) { \
+ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
+ } else { \
+ int __comp; \
+ name##_SPLAY(head, elm); \
+ __comp = (cmp)(elm, (head)->sph_root); \
+ if(__comp < 0) { \
+ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+ SPLAY_RIGHT(elm, field) = (head)->sph_root; \
+ SPLAY_LEFT((head)->sph_root, field) = NULL; \
+ } else if (__comp > 0) { \
+ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT(elm, field) = (head)->sph_root; \
+ SPLAY_RIGHT((head)->sph_root, field) = NULL; \
+ } else \
+ return ((head)->sph_root); \
+ } \
+ (head)->sph_root = (elm); \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *__tmp; \
+ if (SPLAY_EMPTY(head)) \
+ return (NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) { \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+ } else { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+ name##_SPLAY(head, elm); \
+ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
+ } \
+ return (elm); \
+ } \
+ return (NULL); \
+} \
+ \
+void \
+name##_SPLAY(struct name *head, struct type *elm) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+ int __comp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while ((__comp = (cmp)(elm, (head)->sph_root))) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) > 0){ \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+} \
+ \
+/* Splay with either the minimum or the maximum element \
+ * Used to find minimum or maximum element in tree. \
+ */ \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while (1) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp > 0) { \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+}
+
+#define SPLAY_NEGINF -1
+#define SPLAY_INF 1
+
+#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head) \
+ for ((x) = SPLAY_MIN(name, head); \
+ (x) != NULL; \
+ (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-back tree */
+#define RB_HEAD(name, type) \
+struct name { \
+ struct type *rbh_root; /* root of the tree */ \
+}
+
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (0)
+
+#define RB_BLACK 0
+#define RB_RED 1
+#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 */ \
+}
+
+#define RB_LEFT(elm, field) (elm)->field.rbe_left
+#define RB_RIGHT(elm, field) (elm)->field.rbe_right
+#define RB_PARENT(elm, field) (elm)->field.rbe_parent
+#define RB_COLOR(elm, field) (elm)->field.rbe_color
+#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do { \
+ RB_PARENT(elm, field) = parent; \
+ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
+ RB_COLOR(elm, field) = RB_RED; \
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do { \
+ RB_COLOR(black, field) = RB_BLACK; \
+ RB_COLOR(red, field) = RB_RED; \
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
+ (tmp) = RB_RIGHT(elm, field); \
+ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
+ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_LEFT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
+ (tmp) = RB_LEFT(elm, field); \
+ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
+ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_RIGHT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+struct type *name##_RB_REMOVE(struct name *, struct type *); \
+struct type *name##_RB_INSERT(struct name *, struct type *); \
+struct type *name##_RB_FIND(struct name *, struct type *); \
+struct type *name##_RB_NEXT(struct type *); \
+struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp) \
+void \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
+{ \
+ struct type *parent, *gparent, *tmp; \
+ while ((parent = RB_PARENT(elm, field)) && \
+ RB_COLOR(parent, field) == RB_RED) { \
+ gparent = RB_PARENT(parent, field); \
+ if (parent == RB_LEFT(gparent, field)) { \
+ tmp = RB_RIGHT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_RIGHT(parent, field) == elm) { \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_RIGHT(head, gparent, tmp, field); \
+ } else { \
+ tmp = RB_LEFT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_LEFT(parent, field) == elm) { \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_LEFT(head, gparent, tmp, field); \
+ } \
+ } \
+ RB_COLOR(head->rbh_root, field) = RB_BLACK; \
+} \
+ \
+void \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{ \
+ struct type *tmp; \
+ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
+ elm != RB_ROOT(head)) { \
+ if (RB_LEFT(parent, field) == elm) { \
+ tmp = RB_RIGHT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+ struct type *oleft; \
+ if ((oleft = RB_LEFT(tmp, field)))\
+ RB_COLOR(oleft, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_RIGHT(tmp, field)) \
+ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } else { \
+ tmp = RB_LEFT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+ struct type *oright; \
+ if ((oright = RB_RIGHT(tmp, field)))\
+ RB_COLOR(oright, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_LEFT(head, tmp, oright, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_LEFT(tmp, field)) \
+ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } \
+ } \
+ if (elm) \
+ RB_COLOR(elm, field) = RB_BLACK; \
+} \
+ \
+struct type * \
+name##_RB_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *child, *parent, *old = elm; \
+ int color; \
+ if (RB_LEFT(elm, field) == NULL) \
+ child = RB_RIGHT(elm, field); \
+ else if (RB_RIGHT(elm, field) == NULL) \
+ child = RB_LEFT(elm, field); \
+ else { \
+ struct type *left; \
+ elm = RB_RIGHT(elm, field); \
+ while ((left = RB_LEFT(elm, field))) \
+ elm = left; \
+ child = RB_RIGHT(elm, field); \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+ if (RB_PARENT(elm, field) == old) \
+ parent = elm; \
+ (elm)->field = (old)->field; \
+ if (RB_PARENT(old, field)) { \
+ if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+ RB_LEFT(RB_PARENT(old, field), field) = elm;\
+ else \
+ RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+ RB_AUGMENT(RB_PARENT(old, field)); \
+ } else \
+ RB_ROOT(head) = elm; \
+ RB_PARENT(RB_LEFT(old, field), field) = elm; \
+ if (RB_RIGHT(old, field)) \
+ RB_PARENT(RB_RIGHT(old, field), field) = elm; \
+ if (parent) { \
+ left = parent; \
+ do { \
+ RB_AUGMENT(left); \
+ } while ((left = RB_PARENT(left, field))); \
+ } \
+ goto color; \
+ } \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+color: \
+ if (color == RB_BLACK) \
+ name##_RB_REMOVE_COLOR(head, parent, child); \
+ return (old); \
+} \
+ \
+/* Inserts a node into the RB tree */ \
+struct type * \
+name##_RB_INSERT(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp; \
+ struct type *parent = NULL; \
+ int comp = 0; \
+ tmp = RB_ROOT(head); \
+ while (tmp) { \
+ parent = tmp; \
+ comp = (cmp)(elm, parent); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ RB_SET(elm, parent, field); \
+ if (parent != NULL) { \
+ if (comp < 0) \
+ RB_LEFT(parent, field) = elm; \
+ else \
+ RB_RIGHT(parent, field) = elm; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = elm; \
+ name##_RB_INSERT_COLOR(head, elm); \
+ return (NULL); \
+} \
+ \
+/* Finds the node with the same key as elm */ \
+struct type * \
+name##_RB_FIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_RB_NEXT(struct type *elm) \
+{ \
+ if (RB_RIGHT(elm, field)) { \
+ elm = RB_RIGHT(elm, field); \
+ while (RB_LEFT(elm, field)) \
+ elm = RB_LEFT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+struct type * \
+name##_RB_MINMAX(struct name *head, int val) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *parent = NULL; \
+ while (tmp) { \
+ parent = tmp; \
+ if (val < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else \
+ tmp = RB_RIGHT(tmp, field); \
+ } \
+ return (parent); \
+}
+
+#define RB_NEGINF -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#endif /* _SYS_TREE_H_ */
diff --git a/configure.in b/configure.in
new file mode 100644
index 00000000..fa43a9cc
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,372 @@
+dnl configure.in for libevent
+dnl Dug Song <dugsong@monkey.org>
+AC_INIT(event.c)
+
+AM_INIT_AUTOMAKE(libevent,1.2)
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+dnl Initialize prefix.
+if test "$prefix" = "NONE"; then
+ prefix="/usr/local"
+fi
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+AC_PROG_GCC_TRADITIONAL
+if test "$GCC" = yes ; then
+ CFLAGS="$CFLAGS -Wall"
+fi
+
+AC_PROG_LIBTOOL
+
+dnl Uncomment "AC_DISABLE_SHARED" to make shared librraries not get
+dnl built by default. You can also turn shared libs on and off from
+dnl the command line with --enable-shared and --disable-shared.
+dnl AC_DISABLE_SHARED
+AC_SUBST(LIBTOOL_DEPS)
+
+dnl Check for optional stuff
+AC_ARG_WITH(rtsig,
+ [ --with-rtsig compile with support for real time signals (experimental)],
+ [usertsig=yes], [usertsig=no])
+
+dnl Checks for libraries.
+AC_CHECK_LIB(socket, socket)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/ioctl.h sys/devpoll.h port.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, 1,
+ [Define if TAILQ_FOREACH is defined in <sys/queue.h>])],
+ 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, 1,
+ [Define if timeradd is defined in <sys/time.h>])
+ AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+ AC_MSG_CHECKING(for timercmp in sys/time.h)
+ AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timercmp
+ yes
+#endif
+], [ AC_DEFINE(HAVE_TIMERCMP, 1,
+ [Define if timercmp is defined in <sys/time.h>])
+ AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+ AC_MSG_CHECKING(for timerclear in sys/time.h)
+ AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timerclear
+ yes
+#endif
+], [ AC_DEFINE(HAVE_TIMERCLEAR, 1,
+ [Define if timerclear is defined in <sys/time.h>])
+ AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+ AC_MSG_CHECKING(for timerisset in sys/time.h)
+ AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timerisset
+ yes
+#endif
+], [ AC_DEFINE(HAVE_TIMERISSET, 1,
+ [Define if timerisset is defined in <sys/time.h>])
+ AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+dnl - check if the macro WIN32 is defined on this compiler.
+dnl - (this is how we check for a windows version of GCC)
+AC_MSG_CHECKING(for WIN32)
+AC_TRY_COMPILE(,
+ [
+ #ifndef WIN32
+ #error
+ #endif
+ ],
+ bwin32=true; AC_MSG_RESULT(yes),
+ bwin32=false; AC_MSG_RESULT(no),
+)
+
+AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r)
+AC_REPLACE_FUNCS(strlcpy)
+
+if test "x$ac_cv_func_clock_gettime" = "xyes"; then
+ AC_DEFINE(DNS_USE_CPU_CLOCK_FOR_ID, 1, [Define if clock_gettime is available in libc])
+else
+ AC_DEFINE(DNS_USE_GETTIMEOFDAY_FOR_ID, 1, [Define is no secure id variant is available])
+fi
+
+AC_MSG_CHECKING(for F_SETFD in fcntl.h)
+AC_EGREP_CPP(yes,
+[
+#define _GNU_SOURCE
+#include <fcntl.h>
+#ifdef F_SETFD
+yes
+#endif
+], [ AC_DEFINE(HAVE_SETFD, 1,
+ [Define if F_SETFD is defined in <fcntl.h>])
+ AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no))
+
+needsignal=no
+haveselect=no
+AC_CHECK_FUNCS(select, [haveselect=yes], )
+if test "x$haveselect" = "xyes" ; then
+ AC_LIBOBJ(select)
+ needsignal=yes
+fi
+
+havepoll=no
+havertsig=no
+AC_CHECK_FUNCS(poll, [havepoll=yes], )
+if test "x$havepoll" = "xyes" ; then
+ AC_LIBOBJ(poll)
+ needsignal=yes
+
+ if test "x$usertsig" = "xyes" ; then
+ AC_CHECK_FUNCS(sigtimedwait, [havertsig=yes], )
+ fi
+fi
+if test "x$havertsig" = "xyes" ; then
+ AC_MSG_CHECKING(for F_SETSIG in fcntl.h)
+ AC_EGREP_CPP(yes,
+[
+#define _GNU_SOURCE
+#include <fcntl.h>
+#ifdef F_SETSIG
+yes
+#endif
+], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no); havertsig=no])
+fi
+if test "x$havertsig" = "xyes" ; then
+ AC_DEFINE(HAVE_RTSIG, 1, [Define if your system supports POSIX realtime signals])
+ AC_LIBOBJ(rtsig)
+ AC_MSG_CHECKING(for working rtsig on pipes)
+ AC_TRY_RUN(
+[
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+
+int sigio()
+{
+ exit(0);
+}
+
+int main()
+{
+ int fd[2];
+
+ pipe(fd);
+ signal(SIGIO, sigio);
+ fcntl(fd[0], F_SETOWN, getpid());
+ fcntl(fd[0], F_SETSIG, SIGIO);
+ fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_ASYNC);
+ write(fd[1], "", 1);
+ return 1;
+}
+], [ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_WORKING_RTSIG, 1, [Define if realtime signals work on pipes])],
+ AC_MSG_RESULT(no))
+fi
+
+haveepoll=no
+AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], )
+if test "x$haveepoll" = "xyes" ; then
+ AC_DEFINE(HAVE_EPOLL, 1,
+ [Define if your system supports the epoll system calls])
+ AC_LIBOBJ(epoll)
+ needsignal=yes
+fi
+
+havedevpoll=no
+if test "x$ac_cv_header_sys_devpoll_h" = "xyes"; then
+ AC_DEFINE(HAVE_DEVPOLL, 1,
+ [Define if /dev/poll is available])
+ AC_LIBOBJ(devpoll)
+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, 1,
+ [Define if kqueue works correctly with pipes])
+ AC_LIBOBJ(kqueue)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+ fi
+fi
+
+haveepollsyscall=no
+if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then
+ if test "x$haveepoll" = "xno" ; then
+ AC_MSG_CHECKING(for epoll system call)
+ AC_TRY_RUN(
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+int
+epoll_create(int size)
+{
+ return (syscall(__NR_epoll_create, size));
+}
+
+int
+main(int argc, char **argv)
+{
+ int epfd;
+
+ epfd = epoll_create(256);
+ exit (epfd == -1 ? 1 : 0);
+}, [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_EPOLL, 1,
+ [Define if your system supports the epoll system calls])
+ needsignal=yes
+ AC_LIBOBJ(epoll_sub)
+ AC_LIBOBJ(epoll)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+ fi
+fi
+
+haveeventports=no
+AC_CHECK_FUNCS(port_create, [haveeventports=yes], )
+if test "x$haveeventports" = "xyes" ; then
+ AC_DEFINE(HAVE_EVENT_PORTS, 1,
+ [Define if your system supports event ports])
+ AC_LIBOBJ(evport)
+ needsignal=yes
+fi
+if test "x$needsignal" = "xyes" ; then
+ AC_LIBOBJ(signal)
+fi
+
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_CHECK_TYPE(u_int64_t, unsigned long long)
+AC_CHECK_TYPE(u_int32_t, unsigned int)
+AC_CHECK_TYPE(u_int16_t, unsigned short)
+AC_CHECK_TYPE(u_int8_t, unsigned char)
+
+AC_MSG_CHECKING([for socklen_t])
+AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>],
+ [socklen_t x;],
+ AC_MSG_RESULT([yes]),
+ [AC_MSG_RESULT([no])
+ AC_DEFINE(socklen_t, unsigned int,
+ [Define to unsigned int if you dont have it])]
+)
+
+AC_MSG_CHECKING([whether our compiler supports __func__])
+AC_TRY_COMPILE([],
+ [void foo() { const char *cp = __func__; }],
+ AC_MSG_RESULT([yes]),
+ AC_MSG_RESULT([no])
+ AC_MSG_CHECKING([whether our compiler supports __FUNCTION__])
+ AC_TRY_COMPILE([],
+ [void foo() { const char *cp = __FUNCTION__; }],
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(__func__, __FUNCTION__,
+ [Define to appropriate substitue if compiler doesnt have __func__]),
+ AC_MSG_RESULT([no])
+ AC_DEFINE(__func__, __FILE__,
+ [Define to appropriate substitue if compiler doesnt have __func__])))
+
+
+AC_OUTPUT(Makefile test/Makefile sample/Makefile)
diff --git a/devpoll.c b/devpoll.c
new file mode 100644
index 00000000..6db53eaf
--- /dev/null
+++ b/devpoll.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2000-2004 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/devpoll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "event.h"
+#include "evsignal.h"
+#include "log.h"
+
+extern volatile sig_atomic_t evsignal_caught;
+
+/* due to limitations in the devpoll interface, we need to keep track of
+ * all file descriptors outself.
+ */
+struct evdevpoll {
+ struct event *evread;
+ struct event *evwrite;
+};
+
+struct devpollop {
+ struct evdevpoll *fds;
+ int nfds;
+ struct pollfd *events;
+ int nevents;
+ int dpfd;
+ sigset_t evsigmask;
+ struct pollfd *changes;
+ int nchanges;
+};
+
+void *devpoll_init (void);
+int devpoll_add (void *, struct event *);
+int devpoll_del (void *, struct event *);
+int devpoll_recalc (struct event_base *, void *, int);
+int devpoll_dispatch (struct event_base *, void *, struct timeval *);
+void devpoll_dealloc (void *);
+
+struct eventop devpollops = {
+ "devpoll",
+ devpoll_init,
+ devpoll_add,
+ devpoll_del,
+ devpoll_recalc,
+ devpoll_dispatch,
+ devpoll_dealloc
+};
+
+#define NEVENT 32000
+
+static int
+devpoll_commit(struct devpollop *devpollop)
+{
+ /*
+ * Due to a bug in Solaris, we have to use pwrite with an offset of 0.
+ * Write is limited to 2GB of data, until it will fail.
+ */
+ if (pwrite(devpollop->dpfd, devpollop->changes,
+ sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
+ return(-1);
+
+ devpollop->nchanges = 0;
+ return(0);
+}
+
+static int
+devpoll_queue(struct devpollop *devpollop, int fd, int events) {
+ struct pollfd *pfd;
+
+ if (devpollop->nchanges >= devpollop->nevents) {
+ /*
+ * Change buffer is full, must commit it to /dev/poll before
+ * adding more
+ */
+ if (devpoll_commit(devpollop) != 0)
+ return(-1);
+ }
+
+ pfd = &devpollop->changes[devpollop->nchanges++];
+ pfd->fd = fd;
+ pfd->events = events;
+ pfd->revents = 0;
+
+ return(0);
+}
+
+void *
+devpoll_init(void)
+{
+ int dpfd, nfiles = NEVENT;
+ struct rlimit rl;
+ struct devpollop *devpollop;
+
+ /* Disable devpoll when this environment variable is set */
+ if (getenv("EVENT_NODEVPOLL"))
+ return (NULL);
+
+ if (!(devpollop = calloc(1, sizeof(struct devpollop))))
+ return (NULL);
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
+ rl.rlim_cur != RLIM_INFINITY)
+ nfiles = rl.rlim_cur - 1;
+
+ /* Initialize the kernel queue */
+ if ((dpfd = open("/dev/poll", O_RDWR)) == -1) {
+ event_warn("open: /dev/poll");
+ free(devpollop);
+ return (NULL);
+ }
+
+ devpollop->dpfd = dpfd;
+
+ /* Initialize fields */
+ devpollop->events = calloc(nfiles, sizeof(struct pollfd));
+ if (devpollop->events == NULL) {
+ free(devpollop);
+ close(dpfd);
+ return (NULL);
+ }
+ devpollop->nevents = nfiles;
+
+ devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll));
+ if (devpollop->fds == NULL) {
+ free(devpollop->events);
+ free(devpollop);
+ close(dpfd);
+ return (NULL);
+ }
+ devpollop->nfds = nfiles;
+
+ devpollop->changes = calloc(nfiles, sizeof(struct pollfd));
+ if (devpollop->changes == NULL) {
+ free(devpollop->fds);
+ free(devpollop->events);
+ free(devpollop);
+ close(dpfd);
+ return (NULL);
+ }
+
+ evsignal_init(&devpollop->evsigmask);
+
+ return (devpollop);
+}
+
+int
+devpoll_recalc(struct event_base *base, void *arg, int max)
+{
+ struct devpollop *devpollop = arg;
+
+ if (max > devpollop->nfds) {
+ struct evdevpoll *fds;
+ int nfds;
+
+ nfds = devpollop->nfds;
+ while (nfds < max)
+ nfds <<= 1;
+
+ fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll));
+ if (fds == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ devpollop->fds = fds;
+ memset(fds + devpollop->nfds, 0,
+ (nfds - devpollop->nfds) * sizeof(struct evdevpoll));
+ devpollop->nfds = nfds;
+ }
+
+ return (evsignal_recalc(&devpollop->evsigmask));
+}
+
+int
+devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ struct devpollop *devpollop = arg;
+ struct pollfd *events = devpollop->events;
+ struct dvpoll dvp;
+ struct evdevpoll *evdp;
+ int i, res, timeout;
+
+ if (evsignal_deliver(&devpollop->evsigmask) == -1)
+ return (-1);
+
+ if (devpollop->nchanges)
+ devpoll_commit(devpollop);
+
+ timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
+
+ dvp.dp_fds = devpollop->events;
+ dvp.dp_nfds = devpollop->nevents;
+ dvp.dp_timeout = timeout;
+
+ res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
+
+ if (evsignal_recalc(&devpollop->evsigmask) == -1)
+ return (-1);
+
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("ioctl: DP_POLL");
+ return (-1);
+ }
+
+ evsignal_process();
+ return (0);
+ } else if (evsignal_caught)
+ evsignal_process();
+
+ event_debug(("%s: devpoll_wait reports %d", __func__, res));
+
+ for (i = 0; i < res; i++) {
+ int which = 0;
+ int what = events[i].revents;
+ struct event *evread = NULL, *evwrite = NULL;
+
+ assert(events[i].fd < devpollop->nfds);
+ evdp = &devpollop->fds[events[i].fd];
+
+ if (what & POLLHUP)
+ what |= POLLIN | POLLOUT;
+ else if (what & POLLERR)
+ what |= POLLIN | POLLOUT;
+
+ if (what & POLLIN) {
+ evread = evdp->evread;
+ which |= EV_READ;
+ }
+
+ if (what & POLLOUT) {
+ evwrite = evdp->evwrite;
+ which |= EV_WRITE;
+ }
+
+ if (!which)
+ continue;
+
+ if (evread != NULL && !(evread->ev_events & EV_PERSIST))
+ event_del(evread);
+ if (evwrite != NULL && evwrite != evread &&
+ !(evwrite->ev_events & EV_PERSIST))
+ event_del(evwrite);
+
+ if (evread != NULL)
+ event_active(evread, EV_READ, 1);
+ if (evwrite != NULL)
+ event_active(evwrite, EV_WRITE, 1);
+ }
+
+ return (0);
+}
+
+
+int
+devpoll_add(void *arg, struct event *ev)
+{
+ struct devpollop *devpollop = arg;
+ struct evdevpoll *evdp;
+ int fd, events;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(&devpollop->evsigmask, ev));
+
+ fd = ev->ev_fd;
+ if (fd >= devpollop->nfds) {
+ /* Extend the file descriptor array as necessary */
+ if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1)
+ return (-1);
+ }
+ evdp = &devpollop->fds[fd];
+
+ /*
+ * It's not necessary to OR the existing read/write events that we
+ * are currently interested in with the new event we are adding.
+ * The /dev/poll driver ORs any new events with the existing events
+ * that it has cached for the fd.
+ */
+
+ events = 0;
+ if (ev->ev_events & EV_READ) {
+ if (evdp->evread && evdp->evread != ev) {
+ /* There is already a different read event registered */
+ return(-1);
+ }
+ events |= POLLIN;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ if (evdp->evwrite && evdp->evwrite != ev) {
+ /* There is already a different write event registered */
+ return(-1);
+ }
+ events |= POLLOUT;
+ }
+
+ if (devpoll_queue(devpollop, fd, events) != 0)
+ return(-1);
+
+ /* Update events responsible */
+ if (ev->ev_events & EV_READ)
+ evdp->evread = ev;
+ if (ev->ev_events & EV_WRITE)
+ evdp->evwrite = ev;
+
+ return (0);
+}
+
+int
+devpoll_del(void *arg, struct event *ev)
+{
+ struct devpollop *devpollop = arg;
+ struct evdevpoll *evdp;
+ int fd, events;
+ int needwritedelete = 1, needreaddelete = 1;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(&devpollop->evsigmask, ev));
+
+ fd = ev->ev_fd;
+ if (fd >= devpollop->nfds)
+ return (0);
+ evdp = &devpollop->fds[fd];
+
+ events = 0;
+ if (ev->ev_events & EV_READ)
+ events |= POLLIN;
+ if (ev->ev_events & EV_WRITE)
+ events |= POLLOUT;
+
+ /*
+ * The only way to remove an fd from the /dev/poll monitored set is
+ * to use POLLREMOVE by itself. This removes ALL events for the fd
+ * provided so if we care about two events and are only removing one
+ * we must re-add the other event after POLLREMOVE.
+ */
+
+ if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
+ return(-1);
+
+ if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
+ /*
+ * We're not deleting all events, so we must resubmit the
+ * event that we are still interested in if one exists.
+ */
+
+ if ((events & POLLIN) && evdp->evwrite != NULL) {
+ /* Deleting read, still care about write */
+ devpoll_queue(devpollop, fd, POLLOUT);
+ needwritedelete = 0;
+ } else if ((events & POLLOUT) && evdp->evread != NULL) {
+ /* Deleting write, still care about read */
+ devpoll_queue(devpollop, fd, POLLIN);
+ needreaddelete = 0;
+ }
+ }
+
+ if (needreaddelete)
+ evdp->evread = NULL;
+ if (needwritedelete)
+ evdp->evwrite = NULL;
+
+ return (0);
+}
+
+void
+devpoll_dealloc(void *arg)
+{
+ struct devpollop *devpollop = arg;
+
+ if (devpollop->fds)
+ free(devpollop->fds);
+ if (devpollop->events)
+ free(devpollop->events);
+ if (devpollop->changes)
+ free(devpollop->changes);
+ if (devpollop->dpfd >= 0)
+ close(devpollop->dpfd);
+
+ memset(devpollop, 0, sizeof(struct devpollop));
+ free(devpollop);
+}
diff --git a/epoll.c b/epoll.c
new file mode 100644
index 00000000..19a88a1f
--- /dev/null
+++ b/epoll.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2000-2003 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/epoll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "event.h"
+#include "evsignal.h"
+#include "log.h"
+
+extern volatile sig_atomic_t evsignal_caught;
+
+/* due to limitations in the epoll interface, we need to keep track of
+ * all file descriptors outself.
+ */
+struct evepoll {
+ struct event *evread;
+ struct event *evwrite;
+};
+
+struct epollop {
+ struct evepoll *fds;
+ int nfds;
+ struct epoll_event *events;
+ int nevents;
+ int epfd;
+ sigset_t evsigmask;
+};
+
+void *epoll_init (void);
+int epoll_add (void *, struct event *);
+int epoll_del (void *, struct event *);
+int epoll_recalc (struct event_base *, void *, int);
+int epoll_dispatch (struct event_base *, void *, struct timeval *);
+void epoll_dealloc (void *);
+
+struct eventop epollops = {
+ "epoll",
+ epoll_init,
+ epoll_add,
+ epoll_del,
+ epoll_recalc,
+ epoll_dispatch,
+ epoll_dealloc
+};
+
+#ifdef HAVE_SETFD
+#define FD_CLOSEONEXEC(x) do { \
+ if (fcntl(x, F_SETFD, 1) == -1) \
+ event_warn("fcntl(%d, F_SETFD)", x); \
+} while (0)
+#else
+#define FD_CLOSEONEXEC(x)
+#endif
+
+#define NEVENT 32000
+
+void *
+epoll_init(void)
+{
+ int epfd, nfiles = NEVENT;
+ struct rlimit rl;
+ struct epollop *epollop;
+
+ /* Disable epollueue when this environment variable is set */
+ if (getenv("EVENT_NOEPOLL"))
+ return (NULL);
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
+ rl.rlim_cur != RLIM_INFINITY) {
+ /*
+ * Solaris is somewhat retarded - it's important to drop
+ * backwards compatibility when making changes. So, don't
+ * dare to put rl.rlim_cur here.
+ */
+ nfiles = rl.rlim_cur - 1;
+ }
+
+ /* Initalize the kernel queue */
+
+ if ((epfd = epoll_create(nfiles)) == -1) {
+ event_warn("epoll_create");
+ return (NULL);
+ }
+
+ FD_CLOSEONEXEC(epfd);
+
+ if (!(epollop = calloc(1, sizeof(struct epollop))))
+ return (NULL);
+
+ epollop->epfd = epfd;
+
+ /* Initalize fields */
+ epollop->events = malloc(nfiles * sizeof(struct epoll_event));
+ if (epollop->events == NULL) {
+ free(epollop);
+ return (NULL);
+ }
+ epollop->nevents = nfiles;
+
+ epollop->fds = calloc(nfiles, sizeof(struct evepoll));
+ if (epollop->fds == NULL) {
+ free(epollop->events);
+ free(epollop);
+ return (NULL);
+ }
+ epollop->nfds = nfiles;
+
+ evsignal_init(&epollop->evsigmask);
+
+ return (epollop);
+}
+
+int
+epoll_recalc(struct event_base *base, void *arg, int max)
+{
+ struct epollop *epollop = arg;
+
+ if (max > epollop->nfds) {
+ struct evepoll *fds;
+ int nfds;
+
+ nfds = epollop->nfds;
+ while (nfds < max)
+ nfds <<= 1;
+
+ fds = realloc(epollop->fds, nfds * sizeof(struct evepoll));
+ if (fds == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ epollop->fds = fds;
+ memset(fds + epollop->nfds, 0,
+ (nfds - epollop->nfds) * sizeof(struct evepoll));
+ epollop->nfds = nfds;
+ }
+
+ return (evsignal_recalc(&epollop->evsigmask));
+}
+
+int
+epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ struct epollop *epollop = arg;
+ struct epoll_event *events = epollop->events;
+ struct evepoll *evep;
+ int i, res, timeout;
+
+ if (evsignal_deliver(&epollop->evsigmask) == -1)
+ return (-1);
+
+ timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
+ res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
+
+ if (evsignal_recalc(&epollop->evsigmask) == -1)
+ return (-1);
+
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("epoll_wait");
+ return (-1);
+ }
+
+ evsignal_process();
+ return (0);
+ } else if (evsignal_caught)
+ evsignal_process();
+
+ event_debug(("%s: epoll_wait reports %d", __func__, res));
+
+ for (i = 0; i < res; i++) {
+ int which = 0;
+ int what = events[i].events;
+ struct event *evread = NULL, *evwrite = NULL;
+
+ evep = (struct evepoll *)events[i].data.ptr;
+
+ if (what & EPOLLHUP)
+ what |= EPOLLIN | EPOLLOUT;
+ else if (what & EPOLLERR)
+ what |= EPOLLIN | EPOLLOUT;
+
+ if (what & EPOLLIN) {
+ evread = evep->evread;
+ which |= EV_READ;
+ }
+
+ if (what & EPOLLOUT) {
+ evwrite = evep->evwrite;
+ which |= EV_WRITE;
+ }
+
+ if (!which)
+ continue;
+
+ if (evread != NULL && !(evread->ev_events & EV_PERSIST))
+ event_del(evread);
+ if (evwrite != NULL && evwrite != evread &&
+ !(evwrite->ev_events & EV_PERSIST))
+ event_del(evwrite);
+
+ if (evread != NULL)
+ event_active(evread, EV_READ, 1);
+ if (evwrite != NULL)
+ event_active(evwrite, EV_WRITE, 1);
+ }
+
+ return (0);
+}
+
+
+int
+epoll_add(void *arg, struct event *ev)
+{
+ struct epollop *epollop = arg;
+ struct epoll_event epev = {0, {0}};
+ struct evepoll *evep;
+ int fd, op, events;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(&epollop->evsigmask, ev));
+
+ fd = ev->ev_fd;
+ if (fd >= epollop->nfds) {
+ /* Extent the file descriptor array as necessary */
+ if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
+ return (-1);
+ }
+ evep = &epollop->fds[fd];
+ op = EPOLL_CTL_ADD;
+ events = 0;
+ if (evep->evread != NULL) {
+ events |= EPOLLIN;
+ op = EPOLL_CTL_MOD;
+ }
+ if (evep->evwrite != NULL) {
+ events |= EPOLLOUT;
+ op = EPOLL_CTL_MOD;
+ }
+
+ if (ev->ev_events & EV_READ)
+ events |= EPOLLIN;
+ if (ev->ev_events & EV_WRITE)
+ events |= EPOLLOUT;
+
+ epev.data.ptr = evep;
+ epev.events = events;
+ if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
+ return (-1);
+
+ /* Update events responsible */
+ if (ev->ev_events & EV_READ)
+ evep->evread = ev;
+ if (ev->ev_events & EV_WRITE)
+ evep->evwrite = ev;
+
+ return (0);
+}
+
+int
+epoll_del(void *arg, struct event *ev)
+{
+ struct epollop *epollop = arg;
+ struct epoll_event epev = {0, {0}};
+ struct evepoll *evep;
+ int fd, events, op;
+ int needwritedelete = 1, needreaddelete = 1;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(&epollop->evsigmask, ev));
+
+ fd = ev->ev_fd;
+ if (fd >= epollop->nfds)
+ return (0);
+ evep = &epollop->fds[fd];
+
+ op = EPOLL_CTL_DEL;
+ events = 0;
+
+ if (ev->ev_events & EV_READ)
+ events |= EPOLLIN;
+ if (ev->ev_events & EV_WRITE)
+ events |= EPOLLOUT;
+
+ if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) {
+ if ((events & EPOLLIN) && evep->evwrite != NULL) {
+ needwritedelete = 0;
+ events = EPOLLOUT;
+ op = EPOLL_CTL_MOD;
+ } else if ((events & EPOLLOUT) && evep->evread != NULL) {
+ needreaddelete = 0;
+ events = EPOLLIN;
+ op = EPOLL_CTL_MOD;
+ }
+ }
+
+ epev.events = events;
+ epev.data.ptr = evep;
+
+ if (needreaddelete)
+ evep->evread = NULL;
+ if (needwritedelete)
+ evep->evwrite = NULL;
+
+ if (epoll_ctl(epollop->epfd, op, fd, &epev) == -1)
+ return (-1);
+
+ return (0);
+}
+
+void
+epoll_dealloc(void *arg)
+{
+ struct epollop *epollop = arg;
+
+ if (epollop->fds)
+ free(epollop->fds);
+ if (epollop->events)
+ free(epollop->events);
+ if (epollop->epfd >= 0)
+ close(epollop->epfd);
+
+ memset(epollop, 0, sizeof(struct epollop));
+ free(epollop);
+}
diff --git a/epoll_sub.c b/epoll_sub.c
new file mode 100644
index 00000000..431970c7
--- /dev/null
+++ b/epoll_sub.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003 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. 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 <stdint.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+int
+epoll_create(int size)
+{
+ return (syscall(__NR_epoll_create, size));
+}
+
+int
+epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+{
+
+ return (syscall(__NR_epoll_ctl, epfd, op, fd, event));
+}
+
+int
+epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
+{
+ return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout));
+}
diff --git a/evbuffer.c b/evbuffer.c
new file mode 100644
index 00000000..52712bce
--- /dev/null
+++ b/evbuffer.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2002-2004 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. 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 <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#include "event.h"
+
+/* prototypes */
+
+void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t);
+void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
+
+static int
+bufferevent_add(struct event *ev, int timeout)
+{
+ struct timeval tv, *ptv = NULL;
+
+ if (timeout) {
+ timerclear(&tv);
+ tv.tv_sec = timeout;
+ ptv = &tv;
+ }
+
+ return (event_add(ev, ptv));
+}
+
+/*
+ * This callback is executed when the size of the input buffer changes.
+ * We use it to apply back pressure on the reading side.
+ */
+
+void
+bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
+ void *arg) {
+ struct bufferevent *bufev = arg;
+ /*
+ * If we are below the watermark then reschedule reading if it's
+ * still enabled.
+ */
+ if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
+ evbuffer_setcb(buf, NULL, NULL);
+
+ if (bufev->enabled & EV_READ)
+ bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+ }
+}
+
+static void
+bufferevent_readcb(int fd, short event, void *arg)
+{
+ struct bufferevent *bufev = arg;
+ int res = 0;
+ short what = EVBUFFER_READ;
+ size_t len;
+ int howmuch = -1;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto error;
+ }
+
+ /*
+ * If we have a high watermark configured then we don't want to
+ * read more data than would make us reach the watermark.
+ */
+ if (bufev->wm_read.high != 0)
+ howmuch = bufev->wm_read.high;
+
+ res = evbuffer_read(bufev->input, fd, howmuch);
+ if (res == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ goto reschedule;
+ /* error case */
+ what |= EVBUFFER_ERROR;
+ } else if (res == 0) {
+ /* eof case */
+ what |= EVBUFFER_EOF;
+ }
+
+ if (res <= 0)
+ goto error;
+
+ bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+
+ /* See if this callbacks meets the water marks */
+ len = EVBUFFER_LENGTH(bufev->input);
+ if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
+ return;
+ if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
+ struct evbuffer *buf = bufev->input;
+ event_del(&bufev->ev_read);
+
+ /* Now schedule a callback for us */
+ evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
+ return;
+ }
+
+ /* Invoke the user callback - must always be called last */
+ if (bufev->readcb != NULL)
+ (*bufev->readcb)(bufev, bufev->cbarg);
+ return;
+
+ reschedule:
+ bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+ return;
+
+ error:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+bufferevent_writecb(int fd, short event, void *arg)
+{
+ struct bufferevent *bufev = arg;
+ int res = 0;
+ short what = EVBUFFER_WRITE;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto error;
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output)) {
+ res = evbuffer_write(bufev->output, fd);
+ if (res == -1) {
+#ifndef WIN32
+/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
+ *set errno. thus this error checking is not portable*/
+ if (errno == EAGAIN ||
+ errno == EINTR ||
+ errno == EINPROGRESS)
+ goto reschedule;
+ /* error case */
+ what |= EVBUFFER_ERROR;
+
+#else
+ goto reschedule;
+#endif
+
+ } else if (res == 0) {
+ /* eof case */
+ what |= EVBUFFER_EOF;
+ }
+ if (res <= 0)
+ goto error;
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+
+ /*
+ * Invoke the user callback if our buffer is drained or below the
+ * low watermark.
+ */
+ if (bufev->writecb != NULL &&
+ EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+ (*bufev->writecb)(bufev, bufev->cbarg);
+
+ return;
+
+ reschedule:
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+ return;
+
+ error:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+/*
+ * Create a new buffered event object.
+ *
+ * The read callback is invoked whenever we read new data.
+ * The write callback is invoked whenever the output buffer is drained.
+ * The error callback is invoked on a write/read error or on EOF.
+ *
+ * Both read and write callbacks maybe NULL. The error callback is not
+ * allowed to be NULL and have to be provided always.
+ */
+
+struct bufferevent *
+bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
+ everrorcb errorcb, void *cbarg)
+{
+ struct bufferevent *bufev;
+
+ if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
+ return (NULL);
+
+ if ((bufev->input = evbuffer_new()) == NULL) {
+ free(bufev);
+ return (NULL);
+ }
+
+ if ((bufev->output = evbuffer_new()) == NULL) {
+ evbuffer_free(bufev->input);
+ free(bufev);
+ return (NULL);
+ }
+
+ event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
+ event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
+
+ bufev->readcb = readcb;
+ bufev->writecb = writecb;
+ bufev->errorcb = errorcb;
+
+ bufev->cbarg = cbarg;
+
+ /*
+ * Set to EV_WRITE so that using bufferevent_write is going to
+ * trigger a callback. Reading needs to be explicitly enabled
+ * because otherwise no data will be available.
+ */
+ bufev->enabled = EV_WRITE;
+
+ return (bufev);
+}
+
+int
+bufferevent_priority_set(struct bufferevent *bufev, int priority)
+{
+ if (event_priority_set(&bufev->ev_read, priority) == -1)
+ return (-1);
+ if (event_priority_set(&bufev->ev_write, priority) == -1)
+ return (-1);
+
+ return (0);
+}
+
+/* Closing the file descriptor is the responsibility of the caller */
+
+void
+bufferevent_free(struct bufferevent *bufev)
+{
+ event_del(&bufev->ev_read);
+ event_del(&bufev->ev_write);
+
+ evbuffer_free(bufev->input);
+ evbuffer_free(bufev->output);
+
+ free(bufev);
+}
+
+/*
+ * Returns 0 on success;
+ * -1 on failure.
+ */
+
+int
+bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
+{
+ int res;
+
+ res = evbuffer_add(bufev->output, data, size);
+
+ if (res == -1)
+ return (res);
+
+ /* If everything is okay, we need to schedule a write */
+ if (size > 0 && (bufev->enabled & EV_WRITE))
+ bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+
+ return (res);
+}
+
+int
+bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
+{
+ int res;
+
+ res = bufferevent_write(bufev, buf->buffer, buf->off);
+ if (res != -1)
+ evbuffer_drain(buf, buf->off);
+
+ return (res);
+}
+
+size_t
+bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
+{
+ struct evbuffer *buf = bufev->input;
+
+ if (buf->off < size)
+ size = buf->off;
+
+ /* Copy the available data to the user buffer */
+ memcpy(data, buf->buffer, size);
+
+ if (size)
+ evbuffer_drain(buf, size);
+
+ return (size);
+}
+
+int
+bufferevent_enable(struct bufferevent *bufev, short event)
+{
+ if (event & EV_READ) {
+ if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
+ return (-1);
+ }
+ if (event & EV_WRITE) {
+ if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
+ return (-1);
+ }
+
+ bufev->enabled |= event;
+ return (0);
+}
+
+int
+bufferevent_disable(struct bufferevent *bufev, short event)
+{
+ if (event & EV_READ) {
+ if (event_del(&bufev->ev_read) == -1)
+ return (-1);
+ }
+ if (event & EV_WRITE) {
+ if (event_del(&bufev->ev_write) == -1)
+ return (-1);
+ }
+
+ bufev->enabled &= ~event;
+ return (0);
+}
+
+/*
+ * Sets the read and write timeout for a buffered event.
+ */
+
+void
+bufferevent_settimeout(struct bufferevent *bufev,
+ int timeout_read, int timeout_write) {
+ bufev->timeout_read = timeout_read;
+ bufev->timeout_write = timeout_write;
+}
+
+/*
+ * Sets the water marks
+ */
+
+void
+bufferevent_setwatermark(struct bufferevent *bufev, short events,
+ size_t lowmark, size_t highmark)
+{
+ if (events & EV_READ) {
+ bufev->wm_read.low = lowmark;
+ bufev->wm_read.high = highmark;
+ }
+
+ if (events & EV_WRITE) {
+ bufev->wm_write.low = lowmark;
+ bufev->wm_write.high = highmark;
+ }
+
+ /* If the watermarks changed then see if we should call read again */
+ bufferevent_read_pressure_cb(bufev->input,
+ 0, EVBUFFER_LENGTH(bufev->input), bufev);
+}
+
+int
+bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
+{
+ int res;
+
+ res = event_base_set(base, &bufev->ev_read);
+ if (res == -1)
+ return (res);
+
+ res = event_base_set(base, &bufev->ev_write);
+ return (res);
+}
diff --git a/evdns.3 b/evdns.3
new file mode 100644
index 00000000..10414fa2
--- /dev/null
+++ b/evdns.3
@@ -0,0 +1,322 @@
+.\"
+.\" Copyright (c) 2006 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. 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 October 7, 2006
+.Dt EVDNS 3
+.Os
+.Sh NAME
+.Nm evdns_init
+.Nm evdns_shutdown
+.Nm evdns_err_to_string
+.Nm evdns_nameserver_add
+.Nm evdns_count_nameservers
+.Nm evdns_clear_nameservers_and_suspend
+.Nm evdns_resume
+.Nm evdns_nameserver_ip_add
+.Nm evdns_resolve_ipv4
+.Nm evdns_resolve_reverse
+.Nm evdns_resolv_conf_parse
+.Nm evdns_config_windows_nameservers
+.Nm evdns_search_clear
+.Nm evdns_search_add
+.Nm evdns_search_ndots_set
+.Nm evdns_set_log_fn
+.Nd asynchronous functions for DNS resolution.
+.Sh SYNOPSIS
+.Fd #include <sys/time.h>
+.Fd #include <event.h>
+.Fd #include <evdns.h>
+.Ft int
+.Fn evdns_init
+.Ft void
+.Fn evdns_shutdown "int fail_requests"
+.Ft "const char *"
+.Fn evdns_err_to_string "int err"
+.Ft int
+.Fn evdns_nameserver_add "unsigned long int address"
+.Ft int
+.Fn evdns_count_nameservers
+.Ft int
+.Fn evdns_clear_nameservers_and_suspend
+.Ft int
+.Fn evdns_resume
+.Ft int
+.Fn evdns_nameserver_ip_add(const char *ip_as_string);
+.Ft int
+.Fn evdns_resolve_ipv4 "const char *name" "int flags" "evdns_callback_type callback" "void *ptr"
+.Ft int
+.Fn evdns_resolve_reverse "struct in_addr *in" "int flags" "evdns_callback_type callback" "void *ptr"
+.Ft int
+.Fn evdns_resolv_conf_parse "int flags" "const char *"
+.Ft void
+.Fn evdns_search_clear
+.Ft void
+.Fn evdns_search_add "const char *domain"
+.Ft void
+.Fn evdns_search_ndots_set "const int ndots"
+.Ft void
+.Fn evdns_set_log_fn "evdns_debug_log_fn_type fn"
+.Ft int
+.Fn evdns_config_windows_nameservers
+.Sh DESCRIPTION
+Welcome, gentle reader
+.Pp
+Async DNS lookups are really a whole lot harder than they should be,
+mostly stemming from the fact that the libc resolver has never been
+very good at them. Before you use this library you should see if libc
+can do the job for you with the modern async call getaddrinfo_a
+(see http://www.imperialviolet.org/page25.html#e498). Otherwise,
+please continue.
+.Pp
+This code is based on libevent and you must call event_init before
+any of the APIs in this file. You must also seed the OpenSSL random
+source if you are using OpenSSL for ids (see below).
+.Pp
+This library is designed to be included and shipped with your source
+code. You statically link with it. You should also test for the
+existence of strtok_r and define HAVE_STRTOK_R if you have it.
+.Pp
+The DNS protocol requires a good source of id numbers and these
+numbers should be unpredictable for spoofing reasons. There are
+three methods for generating them here and you must define exactly
+one of them. In increasing order of preference:
+.Pp
+.Bl -tag -width "DNS_USE_GETTIMEOFDAY_FOR_ID" -compact -offset indent
+.It DNS_USE_GETTIMEOFDAY_FOR_ID
+Using the bottom 16 bits of the usec result from gettimeofday. This
+is a pretty poor solution but should work anywhere.
+.It DNS_USE_CPU_CLOCK_FOR_ID
+Using the bottom 16 bits of the nsec result from the CPU's time
+counter. This is better, but may not work everywhere. Requires
+POSIX realtime support and you'll need to link against -lrt on
+glibc systems at least.
+.It DNS_USE_OPENSSL_FOR_ID
+Uses the OpenSSL RAND_bytes call to generate the data. You must
+have seeded the pool before making any calls to this library.
+.El
+.Pp
+The library keeps track of the state of nameservers and will avoid
+them when they go down. Otherwise it will round robin between them.
+.Pp
+Quick start guide:
+ #include "evdns.h"
+ void callback(int result, char type, int count, int ttl,
+ void *addresses, void *arg);
+ evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+ evdns_resolve("www.hostname.com", 0, callback, NULL);
+.Pp
+When the lookup is complete the callback function is called. The
+first argument will be one of the DNS_ERR_* defines in evdns.h.
+Hopefully it will be DNS_ERR_NONE, in which case type will be
+DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
+which the data can be cached for (in seconds), addresses will point
+to an array of uint32_t's and arg will be whatever you passed to
+evdns_resolve.
+.Pp
+Searching:
+.Pp
+In order for this library to be a good replacement for glibc's resolver it
+supports searching. This involves setting a list of default domains, in
+which names will be queried for. The number of dots in the query name
+determines the order in which this list is used.
+.Pp
+Searching appears to be a single lookup from the point of view of the API,
+although many DNS queries may be generated from a single call to
+evdns_resolve. Searching can also drastically slow down the resolution
+of names.
+.Pp
+To disable searching:
+.Bl -enum -compact -offset indent
+.It
+Never set it up. If you never call
+.Fn evdns_resolv_conf_parse,
+.Fn evdns_init,
+or
+.Fn evdns_search_add
+then no searching will occur.
+.It
+If you do call
+.Fn evdns_resolv_conf_parse
+then don't pass
+.Va DNS_OPTION_SEARCH
+(or
+.Va DNS_OPTIONS_ALL,
+which implies it).
+.It
+When calling
+.Fn evdns_resolve,
+pass the
+.Va DNS_QUERY_NO_SEARCH
+flag.
+.El
+.Pp
+The order of searches depends on the number of dots in the name. If the
+number is greater than the ndots setting then the names is first tried
+globally. Otherwise each search domain is appended in turn.
+.Pp
+The ndots setting can either be set from a resolv.conf, or by calling
+evdns_search_ndots_set.
+.Pp
+For example, with ndots set to 1 (the default) and a search domain list of
+["myhome.net"]:
+ Query: www
+ Order: www.myhome.net, www.
+.Pp
+ Query: www.abc
+ Order: www.abc., www.abc.myhome.net
+.Pp
+.Sh API reference
+.Pp
+.Bl -tag -width 0123456
+.It Ft int Fn evdns_init
+Initializes support for non-blocking name resolution by calling
+.Fn evdns_resolv_conf_parse
+on UNIX and
+.Fn evdns_config_windows_nameservers
+on Windows.
+.It Ft int Fn evdns_nameserver_add "unsigned long int address"
+Add a nameserver. The address should be an IP address in
+network byte order. The type of address is chosen so that
+it matches in_addr.s_addr.
+Returns non-zero on error.
+.It Ft int Fn evdns_nameserver_ip_add "const char *ip_as_string"
+This wraps the above function by parsing a string as an IP
+address and adds it as a nameserver.
+Returns non-zero on error
+.It Ft int Fn evdns_resolve "const char *name" "int flags" "evdns_callback_type callback" "void *ptr"
+Resolve a name. The name parameter should be a DNS name.
+The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
+which disables searching for this query. (see defn of
+searching above).
+.Pp
+The callback argument is a function which is called when
+this query completes and ptr is an argument which is passed
+to that callback function.
+.Pp
+Returns non-zero on error
+.It Ft void Fn evdns_search_clear
+Clears the list of search domains
+.It Ft void Fn evdns_search_add "const char *domain"
+Add a domain to the list of search domains
+.It Ft void Fn evdns_search_ndots_set "int ndots"
+Set the number of dots which, when found in a name, causes
+the first query to be without any search domain.
+.It Ft int Fn evdns_count_nameservers "void"
+Return the number of configured nameservers (not necessarily the
+number of running nameservers). This is useful for double-checking
+whether our calls to the various nameserver configuration functions
+have been successful.
+.It Ft int Fn evdns_clear_nameservers_and_suspend "void"
+Remove all currently configured nameservers, and suspend all pending
+resolves. Resolves will not necessarily be re-attempted until
+evdns_resume() is called.
+.It Ft int Fn evdns_resume "void"
+Re-attempt resolves left in limbo after an earlier call to
+evdns_clear_nameservers_and_suspend().
+.It Ft int Fn evdns_config_windows_nameservers "void"
+Attempt to configure a set of nameservers based on platform settings on
+a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
+looks in the registry. Returns 0 on success, nonzero on failure.
+.It Ft int Fn evdns_resolv_conf_parse "int flags" "const char *filename"
+Parse a resolv.conf like file from the given filename.
+.Pp
+See the man page for resolv.conf for the format of this file.
+The flags argument determines what information is parsed from
+this file:
+.Bl -tag -width "DNS_OPTION_NAMESERVERS" -offset indent -compact -nested
+.It DNS_OPTION_SEARCH
+domain, search and ndots options
+.It DNS_OPTION_NAMESERVERS
+nameserver lines
+.It DNS_OPTION_MISC
+timeout and attempts options
+.It DNS_OPTIONS_ALL
+all of the above
+.El
+.Pp
+The following directives are not parsed from the file:
+ sortlist, rotate, no-check-names, inet6, debug
+.Pp
+Returns non-zero on error:
+.Bl -tag -width "0" -offset indent -compact -nested
+.It 0
+no errors
+.It 1
+failed to open file
+.It 2
+failed to stat file
+.It 3
+file too large
+.It 4
+out of memory
+.It 5
+short read from file
+.El
+.El
+.Sh Internals:
+Requests are kept in two queues. The first is the inflight queue. In
+this queue requests have an allocated transaction id and nameserver.
+They will soon be transmitted if they haven't already been.
+.Pp
+The second is the waiting queue. The size of the inflight ring is
+limited and all other requests wait in waiting queue for space. This
+bounds the number of concurrent requests so that we don't flood the
+nameserver. Several algorithms require a full walk of the inflight
+queue and so bounding its size keeps thing going nicely under huge
+(many thousands of requests) loads.
+.Pp
+If a nameserver loses too many requests it is considered down and we
+try not to use it. After a while we send a probe to that nameserver
+(a lookup for google.com) and, if it replies, we consider it working
+again. If the nameserver fails a probe we wait longer to try again
+with the next probe.
+.Sh SEE ALSO
+.Xr event 3 ,
+.Xr gethostbyname 3 ,
+.Xr resolv.conf 5
+.Sh HISTORY
+The
+.Nm evdns
+API was developed by Adam Langley on top of the
+.Nm libevent
+API.
+The code was integrate into
+.Nm Tor
+by Nick Mathewson and finally put into
+.Nm libevent
+itself by Niels Provos.
+.Sh AUTHORS
+The
+.Nm evdns
+API and code was written by Adam Langley with significant
+contributions by Nick Mathewson.
+.Sh BUGS
+This documentation is neither complete nor authoritative.
+If you are in doubt about the usage of this API then
+check the source code to find out how it works, write
+up the missing piece of documentation and send it to
+me for inclusion in this man page.
diff --git a/evdns.c b/evdns.c
new file mode 100644
index 00000000..0d852e61
--- /dev/null
+++ b/evdns.c
@@ -0,0 +1,2176 @@
+/* $Id: evdns.c 6979 2006-08-04 18:31:13Z nickm $ */
+
+/* The original version of this module was written by Adam Langley; for
+ * a history of modifications, check out the subversion logs.
+ *
+ * When editing this module, try to keep it re-mergeable by Adam. Don't
+ * reformat the whitespace, add Tor dependencies, or so on.
+ *
+ * TODO:
+ * - Support IPv6 and PTR records.
+ * - Replace all externally visible magic numbers with #defined constants.
+ * - Write doccumentation for APIs of all external functions.
+ */
+
+/* Async DNS Library
+ * Adam Langley <agl@imperialviolet.org>
+ * http://www.imperialviolet.org/eventdns.html
+ * Public Domain code
+ *
+ * This software is Public Domain. To view a copy of the public domain dedication,
+ * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ *
+ * I ask and expect, but do not require, that all derivative works contain an
+ * attribution similar to:
+ * Parts developed by Adam Langley <agl@imperialviolet.org>
+ *
+ * You may wish to replace the word "Parts" with something else depending on
+ * the amount of original code.
+ *
+ * (Derivative works does not include programs which link against, run or include
+ * the source verbatim in their source distributions)
+ *
+ * Version: 0.1b
+ */
+
+#include <sys/types.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//#define NDEBUG
+
+#ifndef DNS_USE_CPU_CLOCK_FOR_ID
+#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
+#ifndef DNS_USE_OPENSSL_FOR_ID
+#error Must configure at least one id generation method.
+#error Please see the documentation.
+#endif
+#endif
+#endif
+
+// #define _POSIX_C_SOURCE 200507
+#define _GNU_SOURCE
+
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#error Multiple id options selected
+#endif
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <time.h>
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <openssl/rand.h>
+#endif
+
+#define _FORTIFY_SOURCE 3
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "evdns.h"
+#include "log.h"
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <iphlpapi.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#define EVDNS_LOG_DEBUG 0
+#define EVDNS_LOG_WARN 1
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#ifdef __USE_ISOC99B
+// libevent doesn't work without this
+typedef uint8_t u_char;
+typedef unsigned int uint;
+#endif
+#include <event.h>
+
+#define u64 uint64_t
+#define u32 uint32_t
+#define u16 uint16_t
+#define u8 uint8_t
+
+#define MAX_ADDRS 4 // maximum number of addresses from a single packet
+// which we bother recording
+
+#define TYPE_A 1
+#define TYPE_CNAME 5
+#define TYPE_PTR 12
+#define TYPE_AAAA 28
+
+#define CLASS_INET 1
+
+struct request {
+ u8 *request; // the dns packet data
+ unsigned int request_len;
+ int reissue_count;
+ int tx_count; // the number of times that this packet has been sent
+ unsigned int request_type; // TYPE_PTR or TYPE_A
+ void *user_pointer; // the pointer given to us for this request
+ evdns_callback_type user_callback;
+ struct nameserver *ns; // the server which we last sent it
+
+ // elements used by the searching code
+ int search_index;
+ struct search_state *search_state;
+ char *search_origname; // needs to be free()ed
+ int search_flags;
+
+ // these objects are kept in a circular list
+ struct request *next, *prev;
+
+ struct event timeout_event;
+
+ u16 trans_id; // the transaction id
+ char request_appended; // true if the request pointer is data which follows this struct
+ char transmit_me; // needs to be transmitted
+};
+
+struct reply {
+ unsigned int type;
+ unsigned int have_answer;
+ union {
+ struct {
+ u32 addrcount;
+ u32 addresses[MAX_ADDRS];
+ } a;
+ struct {
+ char name[HOST_NAME_MAX];
+ } ptr;
+ } data;
+};
+
+struct nameserver {
+ int socket; // a connected UDP socket
+ u32 address;
+ int failed_times; // number of times which we have given this server a chance
+ int timedout; // number of times in a row a request has timed out
+ struct event event;
+ // these objects are kept in a circular list
+ struct nameserver *next, *prev;
+ struct event timeout_event; // used to keep the timeout for
+ // when we next probe this server.
+ // Valid if state == 0
+ char state; // zero if we think that this server is down
+ char choaked; // true if we have an EAGAIN from this server's socket
+ char write_waiting; // true if we are waiting for EV_WRITE events
+};
+
+static struct request *req_head = NULL, *req_waiting_head = NULL;
+static struct nameserver *server_head = NULL;
+
+// The number of good nameservers that we have
+static int global_good_nameservers = 0;
+
+// inflight requests are contained in the req_head list
+// and are actually going out across the network
+static int global_requests_inflight = 0;
+// requests which aren't inflight are in the waiting list
+// and are counted here
+static int global_requests_waiting = 0;
+
+static int global_max_requests_inflight = 64;
+
+static struct timeval global_timeout = {3, 0}; // 3 seconds
+static int global_max_reissues = 1; // a reissue occurs when we get some errors from the server
+static int global_max_retransmits = 3; // number of times we'll retransmit a request which timed out
+// number of timeouts in a row before we consider this server to be down
+static int global_max_nameserver_timeout = 3;
+
+// These are the timeout values for nameservers. If we find a nameserver is down
+// we try to probe it at intervals as given below. Values are in seconds.
+static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}};
+static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval);
+
+const char *const evdns_error_strings[] = {"no error", "The name server was unable to interpret the query", "The name server suffered an internal error", "The requested domain name does not exist", "The name server refused to reply to the request"};
+
+static struct nameserver *nameserver_pick(void);
+static void evdns_request_insert(struct request *req, struct request **head);
+static void nameserver_ready_callback(int fd, short events, void *arg);
+static int evdns_transmit(void);
+static int evdns_request_transmit(struct request *req);
+static void nameserver_send_probe(struct nameserver *const ns);
+static void search_request_finished(struct request *const);
+static int search_try_next(struct request *const req);
+static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg);
+static void evdns_requests_pump_waiting_queue(void);
+static u16 transaction_id_pick(void);
+static struct request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr);
+static void request_submit(struct request *req);
+
+#ifdef MS_WINDOWS
+static int
+last_error(int sock)
+{
+ int optval, optvallen=sizeof(optval);
+ int err = WSAGetLastError();
+ if (err == WSAEWOULDBLOCK && sock >= 0) {
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval,
+ &optvallen))
+ return err;
+ if (optval)
+ return optval;
+ }
+ return err;
+
+}
+static int
+error_is_eagain(int err)
+{
+ return err == EAGAIN || err == WSAEWOULDBLOCK;
+}
+static int
+inet_aton(const char *c, struct in_addr *addr)
+{
+ uint32_t r;
+ if (strcmp(c, "255.255.255.255") == 0) {
+ addr->s_addr = 0xffffffffu;
+ } else {
+ r = inet_addr(c);
+ if (r == INADDR_NONE)
+ return 0;
+ addr->s_addr = r;
+ }
+ return 1;
+}
+#define CLOSE_SOCKET(x) closesocket(x)
+#else
+#define last_error(sock) (errno)
+#define error_is_eagain(err) ((err) == EAGAIN)
+#define CLOSE_SOCKET(x) close(x)
+#endif
+
+#define ISSPACE(c) isspace((int)(unsigned char)(c))
+#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+
+#ifndef NDEBUG
+static const char *
+debug_ntoa(u32 address)
+{
+ static char buf[32];
+ u32 a = ntohl(address);
+ sprintf(buf, "%d.%d.%d.%d",
+ (int)(u8)((a>>24)&0xff),
+ (int)(u8)((a>>16)&0xff),
+ (int)(u8)((a>>8 )&0xff),
+ (int)(u8)((a )&0xff));
+ return buf;
+}
+#endif
+
+static evdns_debug_log_fn_type evdns_log_fn = NULL;
+
+void
+evdns_set_log_fn(evdns_debug_log_fn_type fn)
+{
+ evdns_log_fn = fn;
+}
+
+#ifdef __GNUC__
+#define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3)))
+#else
+#define EVDNS_LOG_CHECK
+#endif
+
+static void _evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK;
+static void
+_evdns_log(int warn, const char *fmt, ...)
+{
+ va_list args;
+ static char buf[512];
+ if (!evdns_log_fn)
+ return;
+ va_start(args,fmt);
+#ifdef MS_WINDOWS
+ _vsnprintf(buf, sizeof(buf), fmt, args);
+#else
+ vsnprintf(buf, sizeof(buf), fmt, args);
+#endif
+ buf[sizeof(buf)-1] = '\0';
+ evdns_log_fn(warn, buf);
+ va_end(args);
+}
+
+#define log _evdns_log
+
+// This walks the list of inflight requests to find the
+// one with a matching transaction id. Returns NULL on
+// failure
+static struct request *
+request_find_from_trans_id(u16 trans_id) {
+ struct request *req = req_head, *const started_at = req_head;
+
+ if (req) {
+ do {
+ if (req->trans_id == trans_id) return req;
+ req = req->next;
+ } while (req != started_at);
+ }
+
+ return NULL;
+}
+
+// a libevent callback function which is called when a nameserver
+// has gone down and we want to test if it has came back to life yet
+static void
+nameserver_prod_callback(int fd, short events, void *arg) {
+ struct nameserver *const ns = (struct nameserver *) arg;
+ (void)fd;
+ (void)events;
+
+ nameserver_send_probe(ns);
+}
+
+// a libevent callback which is called when a nameserver probe (to see if
+// it has come back to life) times out. We increment the count of failed_times
+// and wait longer to send the next probe packet.
+static void
+nameserver_probe_failed(struct nameserver *const ns) {
+ const struct timeval * timeout;
+ (void) evtimer_del(&ns->timeout_event);
+ if (ns->state == 1) {
+ // This can happen if the nameserver acts in a way which makes us mark
+ // it as bad and then starts sending good replies.
+ return;
+ }
+
+ timeout =
+ &global_nameserver_timeouts[MIN(ns->failed_times,
+ global_nameserver_timeouts_length - 1)];
+ ns->failed_times++;
+
+ evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+ if (evtimer_add(&ns->timeout_event, (struct timeval *) timeout) < 0) {
+ log(EVDNS_LOG_WARN,
+ "Error from libevent when adding timer event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+}
+
+// called when a nameserver has been deemed to have failed. For example, too
+// many packets have timed out etc
+static void
+nameserver_failed(struct nameserver *const ns, const char *msg) {
+ struct request *req, *started_at;
+ // if this nameserver has already been marked as failed
+ // then don't do anything
+ if (!ns->state) return;
+
+ log(EVDNS_LOG_WARN, "Nameserver %s has failed: %s",
+ debug_ntoa(ns->address), msg);
+ global_good_nameservers--;
+ assert(global_good_nameservers >= 0);
+ if (global_good_nameservers == 0) {
+ log(EVDNS_LOG_WARN, "All nameservers have failed");
+ }
+
+ ns->state = 0;
+ ns->failed_times = 1;
+
+ evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+ if (evtimer_add(&ns->timeout_event, (struct timeval *) &global_nameserver_timeouts[0]) < 0) {
+ log(EVDNS_LOG_WARN,
+ "Error from libevent when adding timer event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+
+ // walk the list of inflight requests to see if any can be reassigned to
+ // a different server. Requests in the waiting queue don't have a
+ // nameserver assigned yet
+
+ // if we don't have *any* good nameservers then there's no point
+ // trying to reassign requests to one
+ if (!global_good_nameservers) return;
+
+ req = req_head;
+ started_at = req_head;
+ if (req) {
+ do {
+ if (req->tx_count == 0 && req->ns == ns) {
+ // still waiting to go out, can be moved
+ // to another server
+ req->ns = nameserver_pick();
+ }
+ req = req->next;
+ } while (req != started_at);
+ }
+}
+
+static void
+nameserver_up(struct nameserver *const ns) {
+ if (ns->state) return;
+ log(EVDNS_LOG_WARN, "Nameserver %s is back up",
+ debug_ntoa(ns->address));
+ evtimer_del(&ns->timeout_event);
+ ns->state = 1;
+ ns->failed_times = 0;
+ global_good_nameservers++;
+}
+
+static void
+request_trans_id_set(struct request *const req, const u16 trans_id) {
+ req->trans_id = trans_id;
+ *((u16 *) req->request) = htons(trans_id);
+}
+
+// Called to remove a request from a list and dealloc it.
+// head is a pointer to the head of the list it should be
+// removed from or NULL if the request isn't in a list.
+static void
+request_finished(struct request *const req, struct request **head) {
+ if (head) {
+ if (req->next == req) {
+ // only item in the list
+ *head = NULL;
+ } else {
+ req->next->prev = req->prev;
+ req->prev->next = req->next;
+ if (*head == req) *head = req->next;
+ }
+ }
+
+ log(EVDNS_LOG_DEBUG, "Removing timeout for request %lx",
+ (unsigned long) req);
+ evtimer_del(&req->timeout_event);
+
+ search_request_finished(req);
+ global_requests_inflight--;
+
+ if (!req->request_appended) {
+ // need to free the request data on it's own
+ free(req->request);
+ } else {
+ // the request data is appended onto the header
+ // so everything gets free()ed when we:
+ }
+
+ free(req);
+
+ evdns_requests_pump_waiting_queue();
+}
+
+// This is called when a server returns a funny error code.
+// We try the request again with another server.
+//
+// return:
+// 0 ok
+// 1 failed/reissue is pointless
+static int
+request_reissue(struct request *req) {
+ const struct nameserver *const last_ns = req->ns;
+ // the last nameserver should have been marked as failing
+ // by the caller of this function, therefore pick will try
+ // not to return it
+ req->ns = nameserver_pick();
+ if (req->ns == last_ns) {
+ // ... but pick did return it
+ // not a lot of point in trying again with the
+ // same server
+ return 1;
+ }
+
+ req->reissue_count++;
+ req->tx_count = 0;
+ req->transmit_me = 1;
+
+ return 0;
+}
+
+// this function looks for space on the inflight queue and promotes
+// requests from the waiting queue if it can.
+static void
+evdns_requests_pump_waiting_queue(void) {
+ while (global_requests_inflight < global_max_requests_inflight &&
+ global_requests_waiting) {
+ struct request *req;
+ // move a request from the waiting queue to the inflight queue
+ assert(req_waiting_head);
+ if (req_waiting_head->next == req_waiting_head) {
+ // only one item in the queue
+ req = req_waiting_head;
+ req_waiting_head = NULL;
+ } else {
+ req = req_waiting_head;
+ req->next->prev = req->prev;
+ req->prev->next = req->next;
+ req_waiting_head = req->next;
+ }
+
+ global_requests_waiting--;
+ global_requests_inflight++;
+
+ req->ns = nameserver_pick();
+ request_trans_id_set(req, transaction_id_pick());
+
+ evdns_request_insert(req, &req_head);
+ evdns_request_transmit(req);
+ evdns_transmit();
+ }
+}
+
+static void
+reply_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) {
+ switch (req->request_type) {
+ case TYPE_A:
+ if (reply)
+ req->user_callback(DNS_ERR_NONE, DNS_IPv4_A,
+ reply->data.a.addrcount, ttl,
+ reply->data.a.addresses,
+ req->user_pointer);
+ else
+ req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
+ return;
+ case TYPE_PTR:
+ if (reply) {
+ char *name = reply->data.ptr.name;
+ req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl,
+ &name, req->user_pointer);
+ } else {
+ req->user_callback(err, 0, 0, 0, NULL,
+ req->user_pointer);
+ }
+ return;
+ }
+ assert(0);
+}
+
+// this processes a parsed reply packet
+static void
+reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) {
+ int error;
+ static const int error_codes[] = {DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED};
+
+ if (flags & 0x020f || !reply || !reply->have_answer) {
+ // there was an error
+ if (flags & 0x0200) {
+ error = DNS_ERR_TRUNCATED;
+ } else {
+ u16 error_code = (flags & 0x000f) - 1;
+ if (error_code > 4) {
+ error = DNS_ERR_UNKNOWN;
+ } else {
+ error = error_codes[error_code];
+ }
+ }
+
+ switch(error) {
+ case DNS_ERR_SERVERFAILED:
+ case DNS_ERR_NOTIMPL:
+ case DNS_ERR_REFUSED:
+ // we regard these errors as marking a bad nameserver
+ if (req->reissue_count < global_max_reissues) {
+ char msg[64];
+ snprintf(msg, sizeof(msg), "Bad response %d (%s)",
+ error, evdns_err_to_string(error));
+ nameserver_failed(req->ns, msg);
+ if (!request_reissue(req)) return;
+ }
+ break;
+ default:
+ // we got a good reply from the nameserver
+ nameserver_up(req->ns);
+ }
+
+ if (req->search_state && req->request_type != TYPE_PTR) {
+ // if we have a list of domains to search in, try the next one
+ if (!search_try_next(req)) {
+ // a new request was issued so this request is finished and
+ // the user callback will be made when that request (or a
+ // child of it) finishes.
+ request_finished(req, &req_head);
+ return;
+ }
+ }
+
+ // all else failed. Pass the failure up
+ reply_callback(req, 0, error, NULL);
+ request_finished(req, &req_head);
+ } else {
+ // all ok, tell the user
+ reply_callback(req, ttl, 0, reply);
+ nameserver_up(req->ns);
+ request_finished(req, &req_head);
+ }
+}
+
+static inline int
+name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
+ int name_end = -1;
+ int j = *idx;
+#define GET32(x) do { if (j + 4 > length) return -1; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0);
+#define GET16(x) do { if (j + 2 > length) return -1; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0);
+#define GET8(x) do { if (j >= length) return -1; x = packet[j++]; } while(0);
+
+ char *cp = name_out;
+ const char *const end = name_out + name_out_len;
+
+ // Normally, names are a series of length prefixed strings terminated
+ // with a length of 0 (the lengths are u8's < 63).
+ // However, the length can start with a pair of 1 bits and that
+ // means that the next 14 bits are a pointer within the current
+ // packet.
+
+ for(;;) {
+ u8 label_len;
+ if (j >= length) return -1;
+ GET8(label_len);
+ if (!label_len) break;
+ if (label_len & 0xc0) {
+ u8 ptr_low;
+ GET8(ptr_low);
+ if (name_end < 0) name_end = j;
+ j = (((int)label_len & 0x3f) << 8) + ptr_low;
+ if (j < 0 || j >= length) return -1;
+ continue;
+ }
+ if (label_len > 63) return -1;
+ if (cp != name_out) {
+ if (cp + 1 >= end) return -1;
+ *cp++ = '.';
+ }
+ if (cp + label_len >= end) return -1;
+ memcpy(cp, packet + j, label_len);
+ cp += label_len;
+ j += label_len;
+ }
+ if (cp >= end) return -1;
+ *cp = '\0';
+ if (name_end < 0)
+ *idx = j;
+ else
+ *idx = name_end;
+ return 0;
+}
+
+// parses a raw packet from the wire
+static int
+reply_parse(u8 *packet, int length) {
+ int j = 0; // index into packet
+ u16 _t; // used by the macros
+ u32 _t32; // used by the macros
+ char tmp_name[256]; // used by the macros
+
+ u16 trans_id, flags, questions, answers, authority, additional, datalength;
+ u32 ttl, ttl_r = 0xffffffff;
+ struct reply reply;
+ struct request *req;
+ unsigned int i;
+
+ GET16(trans_id);
+ GET16(flags);
+ GET16(questions);
+ GET16(answers);
+ GET16(authority);
+ GET16(additional);
+
+ req = request_find_from_trans_id(trans_id);
+ if (!req) return -1;
+ // XXXX should the other return points also call reply_handle? -NM
+
+ memset(&reply, 0, sizeof(reply));
+
+ if (!(flags & 0x8000)) return -1; // must be an answer
+ if (flags & 0x020f) {
+ // there was an error
+ reply_handle(req, flags, 0, NULL);
+ return -1;
+ }
+ // if (!answers) return; // must have an answer of some form
+
+ // This macro skips a name in the DNS reply.
+#define SKIP_NAME \
+ do { tmp_name[0] = '\0'; \
+ if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
+ return -1; \
+ } while(0);
+
+ reply.type = req->request_type;
+
+ // skip over each question in the reply
+ for (i = 0; i < questions; ++i) {
+ // the question looks like
+ // <label:name><u16:type><u16:class>
+ SKIP_NAME;
+ j += 4;
+ if (j >= length) return -1;
+ }
+
+ // now we have the answer section which looks like
+ // <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
+
+ for (i = 0; i < answers; ++i) {
+ u16 type, class;
+
+ SKIP_NAME;
+ GET16(type);
+ GET16(class);
+ GET32(ttl);
+ GET16(datalength);
+
+ if (type == TYPE_A && class == CLASS_INET) {
+ int addrcount, addrtocopy;
+ if (req->request_type != TYPE_A) {
+ j += datalength; continue;
+ }
+ // XXXX do something sane with malformed A answers.
+ addrcount = datalength >> 2;
+ addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount);
+
+ ttl_r = MIN(ttl_r, ttl);
+ // we only bother with the first four addresses.
+ if (j + 4*addrtocopy > length) return -1;
+ memcpy(&reply.data.a.addresses[reply.data.a.addrcount],
+ packet + j, 4*addrtocopy);
+ j += 4*addrtocopy;
+ reply.data.a.addrcount += addrtocopy;
+ reply.have_answer = 1;
+ if (reply.data.a.addrcount == MAX_ADDRS) break;
+ } else if (type == TYPE_PTR && class == CLASS_INET) {
+ if (req->request_type != TYPE_PTR) {
+ j += datalength; continue;
+ }
+ if (name_parse(packet, length, &j, reply.data.ptr.name,
+ sizeof(reply.data.ptr.name))<0)
+ return -1;
+ reply.have_answer = 1;
+ break;
+ } else {
+ // skip over any other type of resource
+ j += datalength;
+ }
+ }
+
+ reply_handle(req, flags, ttl_r, &reply);
+ return 0;
+#undef SKIP_NAME
+#undef GET32
+#undef GET16
+#undef GET8
+}
+
+// Try to choose a strong transaction id which isn't already in flight
+static u16
+transaction_id_pick(void) {
+ for (;;) {
+ const struct request *req = req_head, *started_at;
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+ struct timespec ts;
+ const u16 trans_id = ts.tv_nsec & 0xffff;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ event_err(1, "clock_gettime");
+#endif
+
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+ struct timeval tv;
+ const u16 trans_id = tv.tv_usec & 0xffff;
+ gettimeofday(&tv, NULL);
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+ u16 trans_id;
+ if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
+ /* // in the case that the RAND call fails we back
+ // down to using gettimeofday.
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ trans_id = tv.tv_usec & 0xffff; */
+ abort();
+ }
+#endif
+
+ if (trans_id == 0xffff) continue;
+ // now check to see if that id is already inflight
+ req = started_at = req_head;
+ if (req) {
+ do {
+ if (req->trans_id == trans_id) break;
+ req = req->next;
+ } while (req != started_at);
+ }
+ // we didn't find it, so this is a good id
+ if (req == started_at) return trans_id;
+ }
+}
+
+// choose a namesever to use. This function will try to ignore
+// nameservers which we think are down and load balance across the rest
+// by updating the server_head global each time.
+static struct nameserver *
+nameserver_pick(void) {
+ struct nameserver *started_at = server_head, *picked;
+ if (!server_head) return NULL;
+
+ // if we don't have any good nameservers then there's no
+ // point in trying to find one.
+ if (!global_good_nameservers) {
+ server_head = server_head->next;
+ return server_head;
+ }
+
+ // remember that nameservers are in a circular list
+ for (;;) {
+ if (server_head->state) {
+ // we think this server is currently good
+ picked = server_head;
+ server_head = server_head->next;
+ return picked;
+ }
+
+ server_head = server_head->next;
+ if (server_head == started_at) {
+ // all the nameservers seem to be down
+ // so we just return this one and hope for the
+ // best
+ assert(global_good_nameservers == 0);
+ picked = server_head;
+ server_head = server_head->next;
+ return picked;
+ }
+ }
+}
+
+// this is called when a namesever socket is ready for reading
+static void
+nameserver_read(struct nameserver *ns) {
+ u8 packet[1500];
+
+ for (;;) {
+ const int r = recv(ns->socket, packet, sizeof(packet), 0);
+ if (r < 0) {
+ int err = last_error(ns->socket);
+ if (error_is_eagain(err)) return;
+ nameserver_failed(ns, strerror(err));
+ return;
+ }
+ reply_parse(packet, r);
+ }
+}
+
+// set if we are waiting for the ability to write to this server.
+// if waiting is true then we ask libevent for EV_WRITE events, otherwise
+// we stop these events.
+static void
+nameserver_write_waiting(struct nameserver *ns, char waiting) {
+ if (ns->write_waiting == waiting) return;
+
+ ns->write_waiting = waiting;
+ (void) event_del(&ns->event);
+ event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST,
+ nameserver_ready_callback, ns);
+ if (event_add(&ns->event, NULL) < 0) {
+ log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+}
+
+// a callback function. Called by libevent when the kernel says that
+// a nameserver socket is ready for writing or reading
+static void
+nameserver_ready_callback(int fd, short events, void *arg) {
+ struct nameserver *ns = (struct nameserver *) arg;
+ (void)fd;
+
+ if (events & EV_WRITE) {
+ ns->choaked = 0;
+ if (!evdns_transmit()) {
+ nameserver_write_waiting(ns, 0);
+ }
+ }
+ if (events & EV_READ) {
+ nameserver_read(ns);
+ }
+}
+
+// Converts a string to a length-prefixed set of DNS labels.
+// @buf must be strlen(name)+2 or longer. name and buf must
+// not overlap. name_len should be the length of name
+//
+// Input: abc.def
+// Output: <3>abc<3>def<0>
+//
+// Returns the length of the data. negative on error
+// -1 label was > 63 bytes
+// -2 name was > 255 bytes
+static int
+dnsname_to_labels(u8 *const buf, const char *name, const int name_len) {
+ const char *end = name + name_len;
+ int j = 0; // current offset into buf
+
+ if (name_len > 255) return -2;
+
+ for (;;) {
+ const char *const start = name;
+ name = strchr(name, '.');
+ if (!name) {
+ const unsigned int label_len = end - start;
+ if (label_len > 63) return -1;
+ buf[j++] = label_len;
+
+ memcpy(buf + j, start, end - start);
+ j += end - start;
+ break;
+ } else {
+ // append length of the label.
+ const unsigned int label_len = name - start;
+ if (label_len > 63) return -1;
+ buf[j++] = label_len;
+
+ memcpy(buf + j, start, name - start);
+ j += name - start;
+ // hop over the '.'
+ name++;
+ }
+ }
+
+ // the labels must be terminated by a 0.
+ // It's possible that the name ended in a .
+ // in which case the zero is already there
+ if (!j || buf[j-1]) buf[j++] = 0;
+ return j;
+}
+
+// Finds the length of a dns request for a DNS name of the given
+// length. The actual request may be smaller than the value returned
+// here
+static int
+evdns_request_len(const int name_len) {
+ return 96 + // length of the DNS standard header
+ name_len + 2 +
+ 4; // space for the resource type
+}
+
+// build a dns request packet into buf. buf should be at least as long
+// as evdns_request_len told you it should be.
+//
+// Returns the amount of space used. Negative on error.
+static int
+evdns_request_data_build(const char *const name, const int name_len,
+ const u16 trans_id, const u16 type, const u16 class,
+ u8 *const buf, size_t buf_len) {
+ int j = 0; // current offset into buf
+ u16 _t; // used by the macros
+ u8 *labels;
+ int labels_len;
+
+#define APPEND16(x) do { \
+ if (j + 2 > buf_len) \
+ return (-1); \
+ _t = htons(x); \
+ memcpy(buf + j, &_t, 2); j += 2; \
+} while(0)
+
+ APPEND16(trans_id);
+ APPEND16(0x0100); // standard query, recusion needed
+ APPEND16(1); // one question
+ APPEND16(0); // no answers
+ APPEND16(0); // no authority
+ APPEND16(0); // no additional
+
+ labels = (u8 *) malloc(name_len + 2);
+ if (labels == NULL)
+ return (-1);
+ labels_len = dnsname_to_labels(labels, name, name_len);
+ if (labels_len < 0) {
+ free(labels);
+ return (labels_len);
+ }
+ if ((size_t)(j + labels_len) > buf_len) {
+ free(labels);
+ return (-1);
+ }
+ memcpy(buf + j, labels, labels_len);
+ j += labels_len;
+ free(labels);
+
+ APPEND16(type);
+ APPEND16(class);
+#undef APPEND16
+
+ return (j);
+}
+
+// this is a libevent callback function which is called when a request
+// has timed out.
+static void
+evdns_request_timeout_callback(int fd, short events, void *arg) {
+ struct request *const req = (struct request *) arg;
+ (void) fd;
+ (void) events;
+
+ log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg);
+
+ req->ns->timedout++;
+ if (req->ns->timedout > global_max_nameserver_timeout) {
+ nameserver_failed(req->ns, "request timed out.");
+ }
+
+ (void) evtimer_del(&req->timeout_event);
+ if (req->tx_count >= global_max_retransmits) {
+ // this request has failed
+ reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
+ request_finished(req, &req_head);
+ } else {
+ // retransmit it
+ evdns_request_transmit(req);
+ }
+}
+
+// try to send a request to a given server.
+//
+// return:
+// 0 ok
+// 1 temporary failure
+// 2 other failure
+static int
+evdns_request_transmit_to(struct request *req, struct nameserver *server) {
+ const int r = send(server->socket, req->request, req->request_len, 0);
+ if (r < 0) {
+ int err = last_error(server->socket);
+ if (error_is_eagain(err)) return 1;
+ nameserver_failed(req->ns, strerror(err));
+ return 2;
+ } else if (r != (int)req->request_len) {
+ return 1; // short write
+ } else {
+ return 0;
+ }
+}
+
+// try to send a request, updating the fields of the request
+// as needed
+//
+// return:
+// 0 ok
+// 1 failed
+static int
+evdns_request_transmit(struct request *req) {
+ int retcode = 0, r;
+
+ // if we fail to send this packet then this flag marks it
+ // for evdns_transmit
+ req->transmit_me = 1;
+ if (req->trans_id == 0xffff) abort();
+
+ if (req->ns->choaked) {
+ // don't bother trying to write to a socket
+ // which we have had EAGAIN from
+ return 1;
+ }
+
+ r = evdns_request_transmit_to(req, req->ns);
+ switch (r) {
+ case 1:
+ // temp failure
+ req->ns->choaked = 1;
+ nameserver_write_waiting(req->ns, 1);
+ return 1;
+ case 2:
+ // failed in some other way
+ retcode = 1;
+ // fall through
+ default:
+ // all ok
+ log(EVDNS_LOG_DEBUG,
+ "Setting timeout for request %lx", (unsigned long) req);
+ evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req);
+ if (evtimer_add(&req->timeout_event, &global_timeout) < 0) {
+ log(EVDNS_LOG_WARN,
+ "Error from libevent when adding timer for request %lx",
+ (unsigned long) req);
+ // ???? Do more?
+ }
+ req->tx_count++;
+ req->transmit_me = 0;
+ return retcode;
+ }
+}
+
+static void
+nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) {
+ struct nameserver *const ns = (struct nameserver *) arg;
+ (void) type;
+ (void) count;
+ (void) ttl;
+ (void) addresses;
+
+ if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) {
+ // this is a good reply
+ nameserver_up(ns);
+ } else nameserver_probe_failed(ns);
+}
+
+static void
+nameserver_send_probe(struct nameserver *const ns) {
+ struct request *req;
+ // here we need to send a probe to a given nameserver
+ // in the hope that it is up now.
+
+ log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address));
+
+ req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns);
+ if (!req) return;
+ // we force this into the inflight queue no matter what
+ request_trans_id_set(req, transaction_id_pick());
+ req->ns = ns;
+ request_submit(req);
+}
+
+// returns:
+// 0 didn't try to transmit anything
+// 1 tried to transmit something
+static int
+evdns_transmit(void) {
+ char did_try_to_transmit = 0;
+
+ if (req_head) {
+ struct request *const started_at = req_head, *req = req_head;
+ // first transmit all the requests which are currently waiting
+ do {
+ if (req->transmit_me) {
+ did_try_to_transmit = 1;
+ evdns_request_transmit(req);
+ }
+
+ req = req->next;
+ } while (req != started_at);
+ }
+
+ return did_try_to_transmit;
+}
+
+// exported function
+int
+evdns_count_nameservers(void)
+{
+ const struct nameserver *server = server_head;
+ int n = 0;
+ if (!server)
+ return 0;
+ do {
+ ++n;
+ server = server->next;
+ } while (server != server_head);
+ return n;
+}
+
+// exported function
+int
+evdns_clear_nameservers_and_suspend(void)
+{
+ struct nameserver *server = server_head, *started_at = server_head;
+ struct request *req = req_head, *req_started_at = req_head;
+
+ if (!server)
+ return 0;
+ while (1) {
+ struct nameserver *next = server->next;
+ (void) event_del(&server->event);
+ (void) evtimer_del(&server->timeout_event);
+ if (server->socket >= 0)
+ CLOSE_SOCKET(server->socket);
+ free(server);
+ if (next == started_at)
+ break;
+ server = next;
+ }
+ server_head = NULL;
+ global_good_nameservers = 0;
+
+ while (req) {
+ struct request *next = req->next;
+ req->tx_count = req->reissue_count = 0;
+ req->ns = NULL;
+ // ???? What to do about searches?
+ (void) evtimer_del(&req->timeout_event);
+ req->trans_id = 0;
+ req->transmit_me = 0;
+
+ global_requests_waiting++;
+ evdns_request_insert(req, &req_waiting_head);
+ /* We want to insert these suspended elements at the front of
+ * the waiting queue, since they were pending before any of
+ * the waiting entries were added. This is a circular list,
+ * so we can just shift the start back by one.*/
+ req_waiting_head = req_waiting_head->prev;
+
+ if (next == req_started_at)
+ break;
+ req = next;
+ }
+ req_head = NULL;
+ global_requests_inflight = 0;
+
+ return 0;
+}
+
+
+// exported function
+int
+evdns_resume(void)
+{
+ evdns_requests_pump_waiting_queue();
+ return 0;
+}
+
+// exported function
+int
+evdns_nameserver_add(unsigned long int address) {
+ // first check to see if we already have this nameserver
+
+ const struct nameserver *server = server_head, *const started_at = server_head;
+ struct nameserver *ns;
+ struct sockaddr_in sin;
+ int err = 0;
+ if (server) {
+ do {
+ if (server->address == address) return 3;
+ server = server->next;
+ } while (server != started_at);
+ }
+
+ ns = (struct nameserver *) malloc(sizeof(struct nameserver));
+ if (!ns) return -1;
+
+ memset(ns, 0, sizeof(struct nameserver));
+
+ ns->socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if (ns->socket < 0) { err = 1; goto out1; }
+#ifdef MS_WINDOWS
+ {
+ u_long nonblocking = 1;
+ ioctlsocket(ns->socket, FIONBIO, &nonblocking);
+ }
+#else
+ fcntl(ns->socket, F_SETFL, O_NONBLOCK);
+#endif
+ sin.sin_addr.s_addr = address;
+ sin.sin_port = htons(53);
+ sin.sin_family = AF_INET;
+ if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
+ err = 2;
+ goto out2;
+ }
+
+ ns->address = address;
+ ns->state = 1;
+ event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
+ if (event_add(&ns->event, NULL) < 0) {
+ err = 2;
+ goto out2;
+ }
+
+ log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address));
+
+ // insert this nameserver into the list of them
+ if (!server_head) {
+ ns->next = ns->prev = ns;
+ server_head = ns;
+ } else {
+ ns->next = server_head->next;
+ ns->prev = server_head;
+ server_head->next = ns;
+ if (server_head->prev == server_head) {
+ server_head->prev = ns;
+ }
+ }
+
+ global_good_nameservers++;
+
+ return 0;
+
+out2:
+ CLOSE_SOCKET(ns->socket);
+out1:
+ free(ns);
+ log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err);
+ return err;
+}
+
+// exported function
+int
+evdns_nameserver_ip_add(const char *ip_as_string) {
+ struct in_addr ina;
+ if (!inet_aton(ip_as_string, &ina)) return 4;
+ return evdns_nameserver_add(ina.s_addr);
+}
+
+// insert into the tail of the queue
+static void
+evdns_request_insert(struct request *req, struct request **head) {
+ if (!*head) {
+ *head = req;
+ req->next = req->prev = req;
+ return;
+ }
+
+ req->prev = (*head)->prev;
+ req->prev->next = req;
+ req->next = *head;
+ (*head)->prev = req;
+}
+
+static int
+string_num_dots(const char *s) {
+ int count = 0;
+ while ((s = strchr(s, '.'))) {
+ s++;
+ count++;
+ }
+ return count;
+}
+
+static struct request *
+request_new(int type, const char *name, int flags,
+ evdns_callback_type callback, void *user_ptr) {
+ const char issuing_now =
+ (global_requests_inflight < global_max_requests_inflight) ? 1 : 0;
+
+ const int name_len = strlen(name);
+ const int request_max_len = evdns_request_len(name_len);
+ const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff;
+ // the request data is alloced in a single block with the header
+ struct request *const req =
+ (struct request *) malloc(sizeof(struct request) + request_max_len);
+ int rlen;
+ (void) flags;
+
+ if (!req) return NULL;
+ memset(req, 0, sizeof(struct request));
+
+ // request data lives just after the header
+ req->request = ((u8 *) req) + sizeof(struct request);
+ // denotes that the request data shouldn't be free()ed
+ req->request_appended = 1;
+ rlen = evdns_request_data_build(name, name_len, trans_id,
+ type, CLASS_INET, req->request, request_max_len);
+ if (rlen < 0)
+ goto err1;
+ req->request_len = rlen;
+ req->trans_id = trans_id;
+ req->tx_count = 0;
+ req->request_type = type;
+ req->user_pointer = user_ptr;
+ req->user_callback = callback;
+ req->ns = issuing_now ? nameserver_pick() : NULL;
+ req->next = req->prev = NULL;
+
+ return req;
+err1:
+ free(req);
+ return NULL;
+}
+
+static void
+request_submit(struct request *const req) {
+ if (req->ns) {
+ // if it has a nameserver assigned then this is going
+ // straight into the inflight queue
+ evdns_request_insert(req, &req_head);
+ global_requests_inflight++;
+ evdns_request_transmit(req);
+ } else {
+ evdns_request_insert(req, &req_waiting_head);
+ global_requests_waiting++;
+ }
+}
+
+// exported function
+int evdns_resolve_ipv4(const char *name, int flags,
+ evdns_callback_type callback, void *ptr) {
+ log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
+ if (flags & DNS_QUERY_NO_SEARCH) {
+ struct request *const req =
+ request_new(TYPE_A, name, flags, callback, ptr);
+ if (req == NULL)
+ return (1);
+ request_submit(req);
+ return (0);
+ } else {
+ return (search_request_new(TYPE_A, name, flags, callback, ptr));
+ }
+}
+
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) {
+ char buf[32];
+ struct request *req;
+ u32 a;
+ assert(in);
+ a = ntohl(in->s_addr);
+ sprintf(buf, "%d.%d.%d.%d.in-addr.arpa",
+ (int)(u8)((a )&0xff),
+ (int)(u8)((a>>8 )&0xff),
+ (int)(u8)((a>>16)&0xff),
+ (int)(u8)((a>>24)&0xff));
+ log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
+ req = request_new(TYPE_PTR, buf, flags, callback, ptr);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// Search support
+//
+// the libc resolver has support for searching a number of domains
+// to find a name. If nothing else then it takes the single domain
+// from the gethostname() call.
+//
+// It can also be configured via the domain and search options in a
+// resolv.conf.
+//
+// The ndots option controls how many dots it takes for the resolver
+// to decide that a name is non-local and so try a raw lookup first.
+
+struct search_domain {
+ int len;
+ struct search_domain *next;
+ // the text string is appended to this structure
+};
+
+struct search_state {
+ int refcount;
+ int ndots;
+ int num_domains;
+ struct search_domain *head;
+};
+
+static struct search_state *global_search_state = NULL;
+
+static void
+search_state_decref(struct search_state *const state) {
+ if (!state) return;
+ state->refcount--;
+ if (!state->refcount) {
+ struct search_domain *next, *dom;
+ for (dom = state->head; dom; dom = next) {
+ next = dom->next;
+ free(dom);
+ }
+ free(state);
+ }
+}
+
+static struct search_state *
+search_state_new(void) {
+ struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state));
+ if (!state) return NULL;
+ memset(state, 0, sizeof(struct search_state));
+ state->refcount = 1;
+ state->ndots = 1;
+
+ return state;
+}
+
+static void
+search_postfix_clear(void) {
+ search_state_decref(global_search_state);
+
+ global_search_state = search_state_new();
+}
+
+// exported function
+void
+evdns_search_clear(void) {
+ search_postfix_clear();
+}
+
+static void
+search_postfix_add(const char *domain) {
+ int domain_len;
+ struct search_domain *sdomain;
+ while (domain[0] == '.') domain++;
+ domain_len = strlen(domain);
+
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->num_domains++;
+
+ sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len);
+ if (!sdomain) return;
+ memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len);
+ sdomain->next = global_search_state->head;
+ sdomain->len = domain_len;
+
+ global_search_state->head = sdomain;
+}
+
+// reverse the order of members in the postfix list. This is needed because,
+// when parsing resolv.conf we push elements in the wrong order
+static void
+search_reverse(void) {
+ struct search_domain *cur, *prev = NULL, *next;
+ cur = global_search_state->head;
+ while (cur) {
+ next = cur->next;
+ cur->next = prev;
+ prev = cur;
+ cur = next;
+ }
+
+ global_search_state->head = prev;
+}
+
+// exported function
+void
+evdns_search_add(const char *domain) {
+ search_postfix_add(domain);
+}
+
+// exported function
+void
+evdns_search_ndots_set(const int ndots) {
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->ndots = ndots;
+}
+
+static void
+search_set_from_hostname(void) {
+ char hostname[HOST_NAME_MAX + 1], *domainname;
+
+ search_postfix_clear();
+ if (gethostname(hostname, sizeof(hostname))) return;
+ domainname = strchr(hostname, '.');
+ if (!domainname) return;
+ search_postfix_add(domainname);
+}
+
+// warning: returns malloced string
+static char *
+search_make_new(const struct search_state *const state, int n, const char *const base_name) {
+ const int base_len = strlen(base_name);
+ const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+ struct search_domain *dom;
+
+ for (dom = state->head; dom; dom = dom->next) {
+ if (!n--) {
+ // this is the postfix we want
+ // the actual postfix string is kept at the end of the structure
+ const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain);
+ const int postfix_len = dom->len;
+ char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1);
+ if (!newname) return NULL;
+ memcpy(newname, base_name, base_len);
+ if (need_to_append_dot) newname[base_len] = '.';
+ memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len);
+ newname[base_len + need_to_append_dot + postfix_len] = 0;
+ return newname;
+ }
+ }
+
+ // we ran off the end of the list and still didn't find the requested string
+ abort();
+}
+
+static int
+search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) {
+ assert(type == TYPE_A);
+ if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) &&
+ global_search_state &&
+ global_search_state->num_domains) {
+ // we have some domains to search
+ struct request *req;
+ if (string_num_dots(name) >= global_search_state->ndots) {
+ req = request_new(type, name, flags, user_callback, user_arg);
+ if (!req) return 1;
+ req->search_index = -1;
+ } else {
+ char *const new_name = search_make_new(global_search_state, 0, name);
+ if (!new_name) return 1;
+ req = request_new(type, new_name, flags, user_callback, user_arg);
+ free(new_name);
+ if (!req) return 1;
+ req->search_index = 0;
+ }
+ req->search_origname = strdup(name);
+ req->search_state = global_search_state;
+ req->search_flags = flags;
+ global_search_state->refcount++;
+ request_submit(req);
+ return 0;
+ } else {
+ struct request *const req = request_new(type, name, flags, user_callback, user_arg);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+ }
+}
+
+// this is called when a request has failed to find a name. We need to check
+// if it is part of a search and, if so, try the next name in the list
+// returns:
+// 0 another request has been submitted
+// 1 no more requests needed
+static int
+search_try_next(struct request *const req) {
+ if (req->search_state) {
+ // it is part of a search
+ char *new_name;
+ struct request *newreq;
+ req->search_index++;
+ if (req->search_index >= req->search_state->num_domains) {
+ // no more postfixes to try, however we may need to try
+ // this name without a postfix
+ if (string_num_dots(req->search_origname) < req->search_state->ndots) {
+ // yep, we need to try it raw
+ struct request *const newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer);
+ log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname);
+ if (newreq) {
+ request_submit(newreq);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ new_name = search_make_new(req->search_state, req->search_index, req->search_origname);
+ if (!new_name) return 1;
+ log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index);
+ newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer);
+ free(new_name);
+ if (!newreq) return 1;
+ newreq->search_origname = req->search_origname;
+ req->search_origname = NULL;
+ newreq->search_state = req->search_state;
+ newreq->search_flags = req->search_flags;
+ newreq->search_index = req->search_index;
+ newreq->search_state->refcount++;
+ request_submit(newreq);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+search_request_finished(struct request *const req) {
+ if (req->search_state) {
+ search_state_decref(req->search_state);
+ req->search_state = NULL;
+ }
+ if (req->search_origname) {
+ free(req->search_origname);
+ req->search_origname = NULL;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Parsing resolv.conf files
+
+static void
+evdns_resolv_set_defaults(int flags) {
+ // if the file isn't found then we assume a local resolver
+ if (flags & DNS_OPTION_SEARCH) search_set_from_hostname();
+ if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1");
+}
+
+#ifndef HAVE_STRTOK_R
+static char *
+strtok_r(char *s, const char *delim, char **state) {
+ return strtok(s, delim);
+}
+#endif
+
+// helper version of atoi which returns -1 on error
+static int
+strtoint(const char *const str) {
+ char *endptr;
+ const int r = strtol(str, &endptr, 10);
+ if (*endptr) return -1;
+ return r;
+}
+
+static void
+resolv_conf_parse_line(char *const start, int flags) {
+ char *strtok_state;
+ static const char *const delims = " \t";
+#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state)
+
+ char *const first_token = strtok_r(start, delims, &strtok_state);
+ if (!first_token) return;
+
+ if (!strcmp(first_token, "nameserver")) {
+ const char *const nameserver = NEXT_TOKEN;
+ struct in_addr ina;
+
+ if (inet_aton(nameserver, &ina)) {
+ // address is valid
+ evdns_nameserver_add(ina.s_addr);
+ }
+ } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) {
+ const char *const domain = NEXT_TOKEN;
+ if (domain) {
+ search_postfix_clear();
+ search_postfix_add(domain);
+ }
+ } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) {
+ const char *domain;
+ search_postfix_clear();
+
+ while ((domain = NEXT_TOKEN)) {
+ search_postfix_add(domain);
+ }
+ search_reverse();
+ } else if (!strcmp(first_token, "options")) {
+ const char *option;
+
+ while ((option = NEXT_TOKEN)) {
+ if (!strncmp(option, "ndots:", 6)) {
+ const int ndots = strtoint(&option[6]);
+ if (ndots == -1) continue;
+ if (!(flags & DNS_OPTION_SEARCH)) continue;
+ log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots);
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->ndots = ndots;
+ } else if (!strncmp(option, "timeout:", 8)) {
+ const int timeout = strtoint(&option[8]);
+ if (timeout == -1) continue;
+ if (!(flags & DNS_OPTION_MISC)) continue;
+ log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout);
+ global_timeout.tv_sec = timeout;
+ } else if (!strncmp(option, "attempts:", 9)) {
+ int retries = strtoint(&option[9]);
+ if (retries == -1) continue;
+ if (retries > 255) retries = 255;
+ if (!(flags & DNS_OPTION_MISC)) continue;
+ log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
+ global_max_retransmits = retries;
+ }
+ }
+ }
+#undef NEXT_TOKEN
+}
+
+// exported function
+// returns:
+// 0 no errors
+// 1 failed to open file
+// 2 failed to stat file
+// 3 file too large
+// 4 out of memory
+// 5 short read from file
+int
+evdns_resolv_conf_parse(int flags, const char *const filename) {
+ struct stat st;
+ int fd;
+ u8 *resolv;
+ char *start;
+ int err = 0;
+
+ log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ evdns_resolv_set_defaults(flags);
+ return 0;
+ }
+
+ if (fstat(fd, &st)) { err = 2; goto out1; }
+ if (!st.st_size) {
+ evdns_resolv_set_defaults(flags);
+ err = 0;
+ goto out1;
+ }
+ if (st.st_size > 65535) { err = 3; goto out1; } // no resolv.conf should be any bigger
+
+ resolv = (u8 *) malloc(st.st_size + 1);
+ if (!resolv) { err = 4; goto out1; }
+
+ if (read(fd, resolv, st.st_size) != st.st_size) { err = 5; goto out2; }
+ resolv[st.st_size] = 0; // we malloced an extra byte
+
+ start = (char *) resolv;
+ for (;;) {
+ char *const newline = strchr(start, '\n');
+ if (!newline) {
+ resolv_conf_parse_line(start, flags);
+ break;
+ } else {
+ *newline = 0;
+ resolv_conf_parse_line(start, flags);
+ start = newline + 1;
+ }
+ }
+
+ if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) {
+ // no nameservers were configured.
+ evdns_nameserver_ip_add("127.0.0.1");
+ }
+ if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) {
+ search_set_from_hostname();
+ }
+
+out2:
+ free(resolv);
+out1:
+ close(fd);
+ return err;
+}
+
+#ifdef MS_WINDOWS
+// Add multiple nameservers from a space-or-comma-separated list.
+static int
+evdns_nameserver_ip_add_line(const char *ips) {
+ const char *addr;
+ char *buf;
+ int r;
+ while (*ips) {
+ while (ISSPACE(*ips) || *ips == ',' || *ips == '\t')
+ ++ips;
+ addr = ips;
+ while (ISDIGIT(*ips) || *ips == '.')
+ ++ips;
+ buf = malloc(ips-addr+1);
+ if (!buf) return 4;
+ memcpy(buf, addr, ips-addr);
+ buf[ips-addr] = '\0';
+ r = evdns_nameserver_ip_add(buf);
+ free(buf);
+ if (r) return r;
+ }
+ return 0;
+}
+
+typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*);
+
+// Use the windows GetNetworkParams interface in iphlpapi.dll to
+// figure out what our nameservers are.
+static int
+load_nameservers_with_getnetworkparams(void)
+{
+ // Based on MSDN examples and inspection of c-ares code.
+ FIXED_INFO *fixed;
+ HMODULE handle = 0;
+ ULONG size = sizeof(FIXED_INFO);
+ void *buf = NULL;
+ int status = 0, r, added_any;
+ IP_ADDR_STRING *ns;
+ GetNetworkParams_fn_t fn;
+
+ if (!(handle = LoadLibrary("iphlpapi.dll"))) {
+ log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll");
+ status = -1;
+ goto done;
+ }
+ if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) {
+ log(EVDNS_LOG_WARN, "Could not get address of function.");
+ status = -1;
+ goto done;
+ }
+
+ buf = malloc(size);
+ if (!buf) { status = 4; goto done; }
+ fixed = buf;
+ r = fn(fixed, &size);
+ if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) {
+ status = -1;
+ goto done;
+ }
+ if (r != ERROR_SUCCESS) {
+ free(buf);
+ buf = malloc(size);
+ if (!buf) { status = 4; goto done; }
+ fixed = buf;
+ r = fn(fixed, &size);
+ if (r != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG, "fn() failed.");
+ status = -1;
+ goto done;
+ }
+ }
+
+ assert(fixed);
+ added_any = 0;
+ ns = &(fixed->DnsServerList);
+ while (ns) {
+ r = evdns_nameserver_ip_add_line(ns->IpAddress.String);
+ if (r) {
+ log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d",
+ (ns->IpAddress.String),(int)GetLastError());
+ status = r;
+ goto done;
+ } else {
+ log(EVDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String);
+ }
+
+ added_any++;
+ ns = ns->Next;
+ }
+
+ if (!added_any) {
+ log(EVDNS_LOG_DEBUG, "No nameservers added.");
+ status = -1;
+ }
+
+ done:
+ if (buf)
+ free(buf);
+ if (handle)
+ FreeLibrary(handle);
+ return status;
+}
+
+static int
+config_nameserver_from_reg_key(HKEY key, const char *subkey)
+{
+ char *buf;
+ DWORD bufsz = 0, type = 0;
+ int status = 0;
+
+ if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz)
+ != ERROR_MORE_DATA)
+ return -1;
+ if (!(buf = malloc(bufsz)))
+ return -1;
+
+ if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz)
+ == ERROR_SUCCESS && bufsz > 1) {
+ status = evdns_nameserver_ip_add_line(buf);
+ }
+
+ free(buf);
+ return status;
+}
+
+#define SERVICES_KEY "System\\CurrentControlSet\\Services\\"
+#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP"
+#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters"
+
+static int
+load_nameservers_from_registry(void)
+{
+ int found = 0;
+ int r;
+#define TRY(k, name) \
+ if (!found && config_nameserver_from_reg_key(k,name) == 0) { \
+ log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \
+ found = 1; \
+ } else if (!found) { \
+ log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \
+ #k,#name); \
+ }
+
+ if (((int)GetVersion()) > 0) { /* NT */
+ HKEY nt_key = 0, interfaces_key = 0;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
+ KEY_READ, &nt_key) != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError());
+ return -1;
+ }
+ r = RegOpenKeyEx(nt_key, "Interfaces", 0,
+ KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,
+ &interfaces_key);
+ if (r != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError());
+ return -1;
+ }
+ TRY(nt_key, "NameServer");
+ TRY(nt_key, "DhcpNameServer");
+ TRY(interfaces_key, "NameServer");
+ TRY(interfaces_key, "DhcpNameServer");
+ RegCloseKey(interfaces_key);
+ RegCloseKey(nt_key);
+ } else {
+ HKEY win_key = 0;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0,
+ KEY_READ, &win_key) != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError());
+ return -1;
+ }
+ TRY(win_key, "NameServer");
+ RegCloseKey(win_key);
+ }
+
+ if (found == 0) {
+ log(EVDNS_LOG_WARN,"Didn't find any nameservers.");
+ }
+
+ return found ? 0 : -1;
+#undef TRY
+}
+
+int
+evdns_config_windows_nameservers(void)
+{
+ if (load_nameservers_with_getnetworkparams() == 0)
+ return 0;
+ return load_nameservers_from_registry();
+}
+#endif
+
+int
+evdns_init()
+{
+ int res = 0;
+#ifdef MS_WINDOWS
+ evdns_config_windows_nameservers(void);
+#else
+ res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+#endif
+
+ return (res);
+}
+
+const char *
+evdns_err_to_string(int err)
+{
+ switch (err) {
+ case DNS_ERR_NONE: return "no error";
+ case DNS_ERR_FORMAT: return "misformatted query";
+ case DNS_ERR_SERVERFAILED: return "server failed";
+ case DNS_ERR_NOTEXIST: return "name does not exist";
+ case DNS_ERR_NOTIMPL: return "query not implemented";
+ case DNS_ERR_REFUSED: return "refused";
+
+ case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed";
+ case DNS_ERR_UNKNOWN: return "unknown";
+ case DNS_ERR_TIMEOUT: return "request timed out";
+ case DNS_ERR_SHUTDOWN: return "dns subsystem shut down";
+ default: return "[Unknown error code]";
+ }
+}
+
+void
+evdns_shutdown(int fail_requests)
+{
+ struct nameserver *server, *server_next;
+ struct search_domain *dom, *dom_next;
+
+ while (req_head) {
+ if (fail_requests)
+ reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL);
+ request_finished(req_head, &req_head);
+ }
+ while (req_waiting_head) {
+ if (fail_requests)
+ reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL);
+ request_finished(req_waiting_head, &req_waiting_head);
+ }
+ global_requests_inflight = global_requests_waiting = 0;
+
+ for (server = server_head; server; server = server_next) {
+ server_next = server->next;
+ if (server->socket >= 0)
+ CLOSE_SOCKET(server->socket);
+ (void) event_del(&server->event);
+ free(server);
+ if (server_next == server_head)
+ break;
+ }
+ server_head = NULL;
+ global_good_nameservers = 0;
+
+ if (global_search_state) {
+ for (dom = global_search_state->head; dom; dom = dom_next) {
+ dom_next = dom->next;
+ free(dom);
+ }
+ free(global_search_state);
+ global_search_state = NULL;
+ }
+ evdns_log_fn = NULL;
+}
+
+#ifdef EVDNS_MAIN
+void
+main_callback(int result, char type, int count, int ttl,
+ void *addrs, void *orig) {
+ char *n = (char*)orig;
+ int i;
+ for (i = 0; i < count; ++i) {
+ if (type == DNS_IPv4_A) {
+ printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
+ } else if (type == DNS_PTR) {
+ printf("%s: %s\n", n, ((char**)addrs)[i]);
+ }
+ }
+ if (!count) {
+ printf("%s: No answer (%d)\n", n, result);
+ }
+ fflush(stdout);
+}
+
+void
+logfn(const char *msg) {
+ fprintf(stderr, "%s\n", msg);
+}
+int
+main(int c, char **v) {
+ int idx;
+ int reverse = 0, verbose = 1;
+ if (c<2) {
+ fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
+ return 1;
+ }
+ idx = 1;
+ while (idx < c && v[idx][0] == '-') {
+ if (!strcmp(v[idx], "-x"))
+ reverse = 1;
+ else if (!strcmp(v[idx], "-v"))
+ verbose = 1;
+ else
+ fprintf(stderr, "Unknown option %s\n", v[idx]);
+ ++idx;
+ }
+ event_init();
+ if (verbose)
+ evdns_set_log_fn(logfn);
+ evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
+ for (; idx < c; ++idx) {
+ if (reverse) {
+ struct in_addr addr;
+ if (!inet_aton(v[idx], &addr)) {
+ fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
+ continue;
+ }
+ fprintf(stderr, "resolving %s...\n",v[idx]);
+ evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
+ } else {
+ fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
+ evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
+ }
+ }
+ fflush(stdout);
+ event_dispatch();
+ return 0;
+}
+#endif
diff --git a/evdns.h b/evdns.h
new file mode 100644
index 00000000..ee272d20
--- /dev/null
+++ b/evdns.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2006 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. 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.
+ */
+
+/*
+ * The original DNS code is due to Adam Langley with heavy
+ * modifications by Nick Mathewson. Adam put his DNS software in the
+ * public domain. You can find his original copyright below. Please,
+ * aware that the code as part of libevent is governed by the 3-clause
+ * BSD license above.
+ *
+ * This software is Public Domain. To view a copy of the public domain dedication,
+ * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ *
+ * I ask and expect, but do not require, that all derivative works contain an
+ * attribution similar to:
+ * Parts developed by Adam Langley <agl@imperialviolet.org>
+ *
+ * You may wish to replace the word "Parts" with something else depending on
+ * the amount of original code.
+ *
+ * (Derivative works does not include programs which link against, run or include
+ * the source verbatim in their source distributions)
+ */
+
+/*
+ * Welcome, gentle reader
+ *
+ * Async DNS lookups are really a whole lot harder than they should be,
+ * mostly stemming from the fact that the libc resolver has never been
+ * very good at them. Before you use this library you should see if libc
+ * can do the job for you with the modern async call getaddrinfo_a
+ * (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
+ * please continue.
+ *
+ * This code is based on libevent and you must call event_init before
+ * any of the APIs in this file. You must also seed the OpenSSL random
+ * source if you are using OpenSSL for ids (see below).
+ *
+ * This library is designed to be included and shipped with your source
+ * code. You statically link with it. You should also test for the
+ * existence of strtok_r and define HAVE_STRTOK_R if you have it.
+ *
+ * The DNS protocol requires a good source of id numbers and these
+ * numbers should be unpredictable for spoofing reasons. There are
+ * three methods for generating them here and you must define exactly
+ * one of them. In increasing order of preference:
+ *
+ * DNS_USE_GETTIMEOFDAY_FOR_ID:
+ * Using the bottom 16 bits of the usec result from gettimeofday. This
+ * is a pretty poor solution but should work anywhere.
+ * DNS_USE_CPU_CLOCK_FOR_ID:
+ * Using the bottom 16 bits of the nsec result from the CPU's time
+ * counter. This is better, but may not work everywhere. Requires
+ * POSIX realtime support and you'll need to link against -lrt on
+ * glibc systems at least.
+ * DNS_USE_OPENSSL_FOR_ID:
+ * Uses the OpenSSL RAND_bytes call to generate the data. You must
+ * have seeded the pool before making any calls to this library.
+ *
+ * The library keeps track of the state of nameservers and will avoid
+ * them when they go down. Otherwise it will round robin between them.
+ *
+ * Quick start guide:
+ * #include "evdns.h"
+ * void callback(int result, char type, int count, int ttl,
+ * void *addresses, void *arg);
+ * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+ * evdns_resolve("www.hostname.com", 0, callback, NULL);
+ *
+ * When the lookup is complete the callback function is called. The
+ * first argument will be one of the DNS_ERR_* defines in evdns.h.
+ * Hopefully it will be DNS_ERR_NONE, in which case type will be
+ * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
+ * which the data can be cached for (in seconds), addresses will point
+ * to an array of uint32_t's and arg will be whatever you passed to
+ * evdns_resolve.
+ *
+ * Searching:
+ *
+ * In order for this library to be a good replacement for glibc's resolver it
+ * supports searching. This involves setting a list of default domains, in
+ * which names will be queried for. The number of dots in the query name
+ * determines the order in which this list is used.
+ *
+ * Searching appears to be a single lookup from the point of view of the API,
+ * although many DNS queries may be generated from a single call to
+ * evdns_resolve. Searching can also drastically slow down the resolution
+ * of names.
+ *
+ * To disable searching:
+ * 1. Never set it up. If you never call evdns_resolv_conf_parse or
+ * evdns_search_add then no searching will occur.
+ *
+ * 2. If you do call evdns_resolv_conf_parse then don't pass
+ * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it).
+ *
+ * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag.
+ *
+ * The order of searches depends on the number of dots in the name. If the
+ * number is greater than the ndots setting then the names is first tried
+ * globally. Otherwise each search domain is appended in turn.
+ *
+ * The ndots setting can either be set from a resolv.conf, or by calling
+ * evdns_search_ndots_set.
+ *
+ * For example, with ndots set to 1 (the default) and a search domain list of
+ * ["myhome.net"]:
+ * Query: www
+ * Order: www.myhome.net, www.
+ *
+ * Query: www.abc
+ * Order: www.abc., www.abc.myhome.net
+ *
+ * API reference:
+ *
+ * int evdns_nameserver_add(unsigned long int address)
+ * Add a nameserver. The address should be an IP address in
+ * network byte order. The type of address is chosen so that
+ * it matches in_addr.s_addr.
+ * Returns non-zero on error.
+ *
+ * int evdns_nameserver_ip_add(const char *ip_as_string)
+ * This wraps the above function by parsing a string as an IP
+ * address and adds it as a nameserver.
+ * Returns non-zero on error
+ *
+ * int evdns_resolve(const char *name, int flags,
+ * evdns_callback_type callback,
+ * void *ptr)
+ * Resolve a name. The name parameter should be a DNS name.
+ * The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
+ * which disables searching for this query. (see defn of
+ * searching above).
+ *
+ * The callback argument is a function which is called when
+ * this query completes and ptr is an argument which is passed
+ * to that callback function.
+ *
+ * Returns non-zero on error
+ *
+ * void evdns_search_clear()
+ * Clears the list of search domains
+ *
+ * void evdns_search_add(const char *domain)
+ * Add a domain to the list of search domains
+ *
+ * void evdns_search_ndots_set(int ndots)
+ * Set the number of dots which, when found in a name, causes
+ * the first query to be without any search domain.
+ *
+ * int evdns_count_nameservers(void)
+ * Return the number of configured nameservers (not necessarily the
+ * number of running nameservers). This is useful for double-checking
+ * whether our calls to the various nameserver configuration functions
+ * have been successful.
+ *
+ * int evdns_clear_nameservers_and_suspend(void)
+ * Remove all currently configured nameservers, and suspend all pending
+ * resolves. Resolves will not necessarily be re-attempted until
+ * evdns_resume() is called.
+ *
+ * int evdns_resume(void)
+ * Re-attempt resolves left in limbo after an earlier call to
+ * evdns_clear_nameservers_and_suspend().
+ *
+ * int evdns_config_windows_nameservers(void)
+ * Attempt to configure a set of nameservers based on platform settings on
+ * a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
+ * looks in the registry. Returns 0 on success, nonzero on failure.
+ *
+ * int evdns_resolv_conf_parse(int flags, const char *filename)
+ * Parse a resolv.conf like file from the given filename.
+ *
+ * See the man page for resolv.conf for the format of this file.
+ * The flags argument determines what information is parsed from
+ * this file:
+ * DNS_OPTION_SEARCH - domain, search and ndots options
+ * DNS_OPTION_NAMESERVERS - nameserver lines
+ * DNS_OPTION_MISC - timeout and attempts options
+ * DNS_OPTIONS_ALL - all of the above
+ * The following directives are not parsed from the file:
+ * sortlist, rotate, no-check-names, inet6, debug
+ *
+ * Returns non-zero on error:
+ * 0 no errors
+ * 1 failed to open file
+ * 2 failed to stat file
+ * 3 file too large
+ * 4 out of memory
+ * 5 short read from file
+ *
+ * Internals:
+ *
+ * Requests are kept in two queues. The first is the inflight queue. In
+ * this queue requests have an allocated transaction id and nameserver.
+ * They will soon be transmitted if they haven't already been.
+ *
+ * The second is the waiting queue. The size of the inflight ring is
+ * limited and all other requests wait in waiting queue for space. This
+ * bounds the number of concurrent requests so that we don't flood the
+ * nameserver. Several algorithms require a full walk of the inflight
+ * queue and so bounding its size keeps thing going nicely under huge
+ * (many thousands of requests) loads.
+ *
+ * If a nameserver loses too many requests it is considered down and we
+ * try not to use it. After a while we send a probe to that nameserver
+ * (a lookup for google.com) and, if it replies, we consider it working
+ * again. If the nameserver fails a probe we wait longer to try again
+ * with the next probe.
+ */
+
+#ifndef EVENTDNS_H
+#define EVENTDNS_H
+
+/* Error codes 0-5 are as described in RFC 1035. */
+#define DNS_ERR_NONE 0
+/* The name server was unable to interpret the query */
+#define DNS_ERR_FORMAT 1
+/* The name server was unable to process this query due to a problem with the
+ * name server */
+#define DNS_ERR_SERVERFAILED 2
+/* The domain name does not exist */
+#define DNS_ERR_NOTEXIST 3
+/* The name server does not support the requested kind of query */
+#define DNS_ERR_NOTIMPL 4
+/* The name server refuses to reform the specified operation for policy
+ * reasons */
+#define DNS_ERR_REFUSED 5
+/* The reply was truncated or ill-formated */
+#define DNS_ERR_TRUNCATED 65
+/* An unknown error occurred */
+#define DNS_ERR_UNKNOWN 66
+/* Communication with the server timed out */
+#define DNS_ERR_TIMEOUT 67
+/* The request was canceled because the DNS subsystem was shut down. */
+#define DNS_ERR_SHUTDOWN 68
+
+#define DNS_IPv4_A 1
+#define DNS_PTR 2
+
+#define DNS_QUERY_NO_SEARCH 1
+
+#define DNS_OPTION_SEARCH 1
+#define DNS_OPTION_NAMESERVERS 2
+#define DNS_OPTION_MISC 4
+#define DNS_OPTIONS_ALL 7
+
+/*
+ * The callback that contains the results from a lookup.
+ * - type is either DNS_IPv4_A or DNS_PTR
+ * - count contains the number of addresses of form type
+ * - ttl is the number of seconds the resolution may be cached for.
+ * - addresses needs to be cast according to type
+ */
+typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);
+
+int evdns_init(void);
+void evdns_shutdown(int fail_requests);
+const char *evdns_err_to_string(int err);
+int evdns_nameserver_add(unsigned long int address);
+int evdns_count_nameservers(void);
+int evdns_clear_nameservers_and_suspend(void);
+int evdns_resume(void);
+int evdns_nameserver_ip_add(const char *ip_as_string);
+int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr);
+struct in_addr;
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr);
+int evdns_resolv_conf_parse(int flags, const char *);
+#ifdef MS_WINDOWS
+int evdns_config_windows_nameservers(void);
+#endif
+void evdns_search_clear(void);
+void evdns_search_add(const char *domain);
+void evdns_search_ndots_set(const int ndots);
+
+typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
+void evdns_set_log_fn(evdns_debug_log_fn_type fn);
+
+#define DNS_NO_SEARCH 1
+
+#endif // !EVENTDNS_H
diff --git a/event-internal.h b/event-internal.h
new file mode 100644
index 00000000..becb6691
--- /dev/null
+++ b/event-internal.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2000-2004 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. 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_INTERNAL_H_
+#define _EVENT_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct event_base {
+ const struct eventop *evsel;
+ void *evbase;
+ int event_count; /* counts number of total events */
+ int event_count_active; /* counts number of active events */
+
+ int event_gotterm; /* Set to terminate loop */
+
+ /* active event management */
+ struct event_list **activequeues;
+ int nactivequeues;
+
+ struct event_list eventqueue;
+ struct timeval event_tv;
+
+ RB_HEAD(event_tree, event) timetree;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_INTERNAL_H_ */
diff --git a/event.3 b/event.3
new file mode 100644
index 00000000..132c30f0
--- /dev/null
+++ b/event.3
@@ -0,0 +1,578 @@
+.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos 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 August 8, 2000
+.Dt EVENT 3
+.Os
+.Sh NAME
+.Nm event_init ,
+.Nm event_dispatch ,
+.Nm event_loop ,
+.Nm event_loopexit ,
+.Nm event_set ,
+.Nm event_base_dispatch ,
+.Nm event_base_loop ,
+.Nm event_base_loopexit ,
+.Nm event_base_set ,
+.Nm event_add ,
+.Nm event_del ,
+.Nm event_once ,
+.Nm event_pending ,
+.Nm event_initialized ,
+.Nm event_priority_init ,
+.Nm event_priority_set ,
+.Nm evtimer_set ,
+.Nm evtimer_add ,
+.Nm evtimer_del ,
+.Nm evtimer_pending ,
+.Nm evtimer_initialized ,
+.Nm signal_set ,
+.Nm signal_add ,
+.Nm signal_del ,
+.Nm signal_pending ,
+.Nm signal_initialized ,
+.Nm bufferevent_new ,
+.Nm bufferevent_free ,
+.Nm bufferevent_write ,
+.Nm bufferevent_write_buffer ,
+.Nm bufferevent_read ,
+.Nm bufferevent_enable ,
+.Nm bufferevent_disable ,
+.Nm bufferevent_settimeout ,
+.Nm bufferevent_base_set ,
+.Nm evbuffer_new ,
+.Nm evbuffer_free ,
+.Nm evbuffer_add ,
+.Nm evbuffer_add_buffer ,
+.Nm evbuffer_add_printf ,
+.Nm evbuffer_add_vprintf ,
+.Nm evbuffer_drain ,
+.Nm evbuffer_write ,
+.Nm evbuffer_read ,
+.Nm evbuffer_find ,
+.Nm evbuffer_readline ,
+.Nm evhttp_start ,
+.Nm evhttp_free
+.Nd execute a function when a specific event occurs
+.Sh SYNOPSIS
+.Fd #include <sys/time.h>
+.Fd #include <event.h>
+.Ft "struct event_base *"
+.Fn "event_init"
+.Ft int
+.Fn "event_dispatch"
+.Ft int
+.Fn "event_loop" "int flags"
+.Ft int
+.Fn "event_loopexit" "struct timeval *tv"
+.Ft void
+.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
+.Ft int
+.Fn "event_base_dispatch" "struct event_base *base"
+.Ft int
+.Fn "event_base_loop" "struct event_base *base" "int flags"
+.Ft int
+.Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv"
+.Ft int
+.Fn "event_base_set" "struct event_base *base" "struct event *"
+.Ft int
+.Fn "event_add" "struct event *ev" "struct timeval *tv"
+.Ft int
+.Fn "event_del" "struct event *ev"
+.Ft int
+.Fn "event_once" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv"
+.Ft int
+.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv"
+.Ft int
+.Fn "event_initialized" "struct event *ev"
+.Ft int
+.Fn "event_priority_init" "int npriorities"
+.Ft int
+.Fn "event_priority_set" "struct event *ev" "int priority"
+.Ft void
+.Fn "evtimer_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg"
+.Ft void
+.Fn "evtimer_add" "struct event *ev" "struct timeval *"
+.Ft void
+.Fn "evtimer_del" "struct event *ev"
+.Ft int
+.Fn "evtimer_pending" "struct event *ev" "struct timeval *tv"
+.Ft int
+.Fn "evtimer_initialized" "struct event *ev"
+.Ft void
+.Fn "signal_set" "struct event *ev" "int signal" "void (*fn)(int, short, void *)" "void *arg"
+.Ft void
+.Fn "signal_add" "struct event *ev" "struct timeval *"
+.Ft void
+.Fn "signal_del" "struct event *ev"
+.Ft int
+.Fn "signal_pending" "struct event *ev" "struct timeval *tv"
+.Ft int
+.Fn "signal_initialized" "struct event *ev"
+.Ft "struct bufferevent *"
+.Fn "bufferevent_new" "int fd" "evbuffercb readcb" "evbuffercb writecb" "everrorcb" "void *cbarg"
+.Ft void
+.Fn "bufferevent_free" "struct bufferevent *bufev"
+.Ft int
+.Fn "bufferevent_write" "struct bufferevent *bufev" "void *data" "size_t size"
+.Ft int
+.Fn "bufferevent_write_buffer" "struct bufferevent *bufev" "struct evbuffer *buf"
+.Ft size_t
+.Fn "bufferevent_read" "struct bufferevent *bufev" "void *data" "size_t size"
+.Ft int
+.Fn "bufferevent_enable" "struct bufferevent *bufev" "short event"
+.Ft int
+.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event"
+.Ft void
+.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write"
+.Ft int
+.Fn "bufferevent_base_set" "struct event_base *base" "struct bufferevent *bufev"
+.Ft "struct evbuffer *"
+.Fn "evbuffer_new" "void"
+.Ft void
+.Fn "evbuffer_free" "struct evbuffer *buf"
+.Ft int
+.Fn "evbuffer_add" "struct evbuffer *buf" "u_char *data" "size_t size"
+.Ft int
+.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src"
+.Ft int
+.Fn "evbuffer_add_printf" "struct evbuffer *buf" "char *fmt" "..."
+.Ft int
+.Fn "evbuffer_add_vprintf" "struct evbuffer *buf" "const char *fmt" "va_list ap"
+.Ft void
+.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size"
+.Ft int
+.Fn "evbuffer_write" "struct evbuffer *buf" "int fd"
+.Ft int
+.Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size"
+.Ft "u_char *"
+.Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size"
+.Ft "char *"
+.Fn "evbuffer_readline" "struct evbuffer *buf"
+.Ft "struct evhttp *"
+.Fn "evhttp_start" "const char *address" "u_short port"
+.Ft "void"
+.Fn "evhttp_free" "struct evhttp* http"
+.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 a given time has passed.
+.Pp
+The
+.Nm event
+API needs to be initialized with
+.Fn event_init
+before it can be used.
+.Pp
+In order to process events, an application needs to call
+.Fn event_dispatch .
+This function 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.
+The
+.Nm event_loopexit
+function allows the loop to be terminated after some amount of time
+has passed.
+The parameter indicates the time after which the loop should terminate.
+.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, 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_SIGNAL ,
+.Va EV_READ ,
+or
+.Va EV_WRITE .
+The additional flag
+.Va EV_PERSIST
+makes an
+.Fn event_add
+persistent until
+.Fn event_del
+has been called.
+.Pp
+Once initialized, the
+.Fa ev
+structure can be used repeatedly with
+.Fn event_add
+and
+.Fn event_del
+and does not need to be reinitialized unless the function called and/or
+the argument to it are to be changed.
+However, when an
+.Fa ev
+structure has been added to libevent using
+.Fn event_add
+the structure must persist until the event occurs (assuming
+.Fa EV_PERSIST
+is not set) or is removed
+using
+.Fn event_del .
+You may not reuse the same
+.Fa ev
+structure for multiple monitored descriptors; each descriptor
+needs its own
+.Fa ev .
+.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
+.Dv 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 already has 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 function
+.Fn event_once
+is similar to
+.Fn event_set .
+However, it schedules a callback to be called exactly once and does not
+require the caller to prepare an
+.Fa event
+structure.
+This function supports
+.Fa EV_TIMEOUT ,
+.Fa EV_READ ,
+and
+.Fa EV_WRITE .
+.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
+.Dv 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 evtimer_set ,
+.Fn evtimer_add ,
+.Fn evtimer_del ,
+.Fn evtimer_initialized ,
+and
+.Fn evtimer_pending
+are abbreviations for common situations where only a timeout is required.
+The file descriptor passed will be \-1, and the event type will be
+.Va EV_TIMEOUT .
+.Pp
+The functions
+.Fn signal_set ,
+.Fn signal_add ,
+.Fn signal_del ,
+.Fn signal_initialized ,
+and
+.Fn signal_pending
+are abbreviations.
+The event type will be a persistent
+.Va EV_SIGNAL .
+That means
+.Fn signal_set
+adds
+.Va EV_PERSIST .
+.Pp
+It is possible to disable support for
+.Va epoll , kqueue , devpoll , poll
+or
+.Va select
+by setting the environment variable
+.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL , EVENT_NOPOLL
+or
+.Va EVENT_NOSELECT ,
+respectively.
+By setting the environment variable
+.Va EVENT_SHOW_METHOD ,
+.Nm libevent
+displays the kernel notification method that it uses.
+.Sh EVENT PRIORITIES
+By default
+.Nm libevent
+schedules all active events with the same priority.
+However, sometimes it is desirable to process some events with a higher
+priority than others.
+For that reason,
+.Nm libevent
+supports strict priority queues.
+Active events with a lower priority are always processed before events
+with a higher priority.
+.Pp
+The number of different priorities can be set initially with the
+.Fn event_priority_init
+function.
+This function should be called before the first call to
+.Fn event_dispatch .
+The
+.Fn event_priority_set
+function can be used to assign a priority to an event.
+By default,
+.Nm libevent
+assigns the middle priority to all events unless their priority
+is explicitly set.
+.Sh THREAD SAFE EVENTS
+.Nm Libevent
+has experimental support for thread-safe events.
+When initializing the library via
+.Fn event_init ,
+an event base is returned.
+This event base can be used in conjunction with calls to
+.Fn event_base_set ,
+.Fn event_base_dispatch ,
+.Fn event_base_loop ,
+.Fn event_base_loopexit ,
+and
+.Fn bufferevent_base_set .
+.Fn event_base_set
+should be called after preparing an event with
+.Fn event_set ,
+as
+.Fn event_set
+assigns the provided event to the most recently created event base.
+.Fn bufferevent_base_set
+should be called after preparing a bufferevent with
+.Fn bufferevent_new .
+.Sh BUFFERED EVENTS
+.Nm libevent
+provides an abstraction on top of the regular event callbacks.
+This abstraction is called a
+.Va "buffered event" .
+A buffered event provides input and output buffers that get filled
+and drained automatically.
+The user of a buffered event no longer deals directly with the IO,
+but instead is reading from input and writing to output buffers.
+.Pp
+A new bufferevent is created by
+.Fn bufferevent_new .
+The parameter
+.Fa fd
+specifies the file descriptor from which data is read and written to.
+This file descriptor is not allowed to be a
+.Xr pipe 2 .
+The next three parameters are callbacks.
+The read and write callback have the following form:
+.Ft void
+.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" .
+The error callback has the following form:
+.Ft void
+.Fn "(*cb)" "struct bufferevent *bufev" "short what" "void *arg" .
+The argument is specified by the fourth parameter
+.Fa "cbarg" .
+A
+.Fa bufferevent struct
+pointer is returned on success, NULL on error.
+Both the read and the write callback may be NULL.
+The error callback has to be always provided.
+.Pp
+Once initialized, the bufferevent structure can be used repeatedly with
+bufferevent_enable() and bufferevent_disable(). The flags parameter can
+be a combination of
+.Va EV_READ
+and
+.Va EV_WRITE .
+When read enabled the bufferevent will try to read from the file
+descriptor and call the read callback. The write callback is executed
+whenever the output buffer is drained below the write low watermark,
+which is
+.Va 0
+by default.
+.Pp
+The
+.Fn bufferevent_write
+function can be used to write data to the file descriptor.
+The data is appended to the output buffer and written to the descriptor
+automatically as it becomes available for writing.
+The
+.Fn bufferevent_read
+function is used to read data from the input buffer.
+Both functions return the amount of data written or read.
+.Pp
+If multiple bases are in use, bufferevent_base_set() must be called before
+enabling the bufferevent for the first time.
+.Sh NON-BLOCKING HTTP SUPPORT
+.Nm libevent
+provides a very thin HTTP layer that can be used both to host an HTTP
+server and also to make HTTP requests.
+An HTTP server can be created by calling
+.Fn evhttp_start .
+When the HTTP server is no longer used, it can be freed via
+.Fn evhttp_free .
+.Pp
+To be notified of HTTP requests, a user needs to register callbacks with the
+HTTP server.
+This can be done by calling
+.Fn evhttp_set_cb .
+The second argument is the URI for which a callback is being registered.
+The corresponding callback will receive an
+.Va struct evhttp_request
+object that contains all information about the request.
+.Pp
+This section does not document all the possible function calls, please
+check
+.Va event.h
+for the public interfaces.
+.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 kqueue 2 ,
+.Xr poll 2 ,
+.Xr select 2 ,
+.Xr timeout 9
+.Sh HISTORY
+The
+.Nm event
+API manpage is based on the
+.Xr timeout 9
+manpage by Artur Grabowski.
+The port of
+.Nm libevent
+to Windows is due to Michael A. Davis.
+Support for real-time signals is due to Taral.
+.Sh AUTHORS
+The
+.Nm event
+library was written by Niels Provos.
+.Sh BUGS
+This documentation is neither complete nor authoritative.
+If you are in doubt about the usage of this API then
+check the source code to find out how it works, write
+up the missing piece of documentation and send it to
+me for inclusion in this man page.
diff --git a/event.c b/event.c
new file mode 100644
index 00000000..c7ece90f
--- /dev/null
+++ b/event.c
@@ -0,0 +1,865 @@
+/*
+ * Copyright (c) 2000-2004 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include "misc.h"
+#endif
+#include <sys/types.h>
+#include <sys/tree.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <assert.h>
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+
+#ifdef HAVE_EVENT_PORTS
+extern const struct eventop evportops;
+#endif
+#ifdef HAVE_SELECT
+extern const struct eventop selectops;
+#endif
+#ifdef HAVE_POLL
+extern const struct eventop pollops;
+#endif
+#ifdef HAVE_RTSIG
+extern const struct eventop rtsigops;
+#endif
+#ifdef HAVE_EPOLL
+extern const struct eventop epollops;
+#endif
+#ifdef HAVE_WORKING_KQUEUE
+extern const struct eventop kqops;
+#endif
+#ifdef HAVE_DEVPOLL
+extern const struct eventop devpollops;
+#endif
+#ifdef WIN32
+extern const struct eventop win32ops;
+#endif
+
+/* In order of preference */
+const struct eventop *eventops[] = {
+#ifdef HAVE_EVENT_PORTS
+ &evportops,
+#endif
+#ifdef HAVE_WORKING_KQUEUE
+ &kqops,
+#endif
+#ifdef HAVE_EPOLL
+ &epollops,
+#endif
+#ifdef HAVE_DEVPOLL
+ &devpollops,
+#endif
+#ifdef HAVE_RTSIG
+ &rtsigops,
+#endif
+#ifdef HAVE_POLL
+ &pollops,
+#endif
+#ifdef HAVE_SELECT
+ &selectops,
+#endif
+#ifdef WIN32
+ &win32ops,
+#endif
+ NULL
+};
+
+/* Global state */
+struct event_list signalqueue;
+
+struct event_base *current_base = NULL;
+
+/* Handle signals - This is a deprecated interface */
+int (*event_sigcb)(void); /* Signal callback when gotsig is set */
+volatile sig_atomic_t event_gotsig; /* Set in signal handler */
+
+/* Prototypes */
+static void event_queue_insert(struct event_base *, struct event *, int);
+static void event_queue_remove(struct event_base *, struct event *, int);
+static int event_haveevents(struct event_base *);
+
+static void event_process_active(struct event_base *);
+
+static int timeout_next(struct event_base *, struct timeval *);
+static void timeout_process(struct event_base *);
+static void timeout_correct(struct event_base *, struct timeval *);
+
+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);
+ if (a < b)
+ return (-1);
+ else if (a > b)
+ return (1);
+ return (0);
+}
+
+static int
+gettime(struct timeval *tp)
+{
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+ return (-1);
+ tp->tv_sec = ts.tv_sec;
+ tp->tv_usec = ts.tv_nsec / 1000;
+#else
+ gettimeofday(tp, NULL);
+#endif
+
+ 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;
+
+ if ((current_base = calloc(1, sizeof(struct event_base))) == NULL)
+ event_err(1, "%s: calloc");
+
+ event_sigcb = NULL;
+ event_gotsig = 0;
+ gettime(&current_base->event_tv);
+
+ RB_INIT(&current_base->timetree);
+ TAILQ_INIT(&current_base->eventqueue);
+ TAILQ_INIT(&signalqueue);
+
+ current_base->evbase = NULL;
+ for (i = 0; eventops[i] && !current_base->evbase; i++) {
+ current_base->evsel = eventops[i];
+
+ current_base->evbase = current_base->evsel->init();
+ }
+
+ if (current_base->evbase == NULL)
+ event_errx(1, "%s: no event mechanism available", __func__);
+
+ if (getenv("EVENT_SHOW_METHOD"))
+ event_msgx("libevent using: %s\n",
+ current_base->evsel->name);
+
+ /* allocate a single active event queue */
+ event_base_priority_init(current_base, 1);
+
+ return (current_base);
+}
+
+void
+event_base_free(struct event_base *base)
+{
+ int i;
+
+ if (base == NULL && current_base)
+ base = current_base;
+ if (base == current_base)
+ current_base = NULL;
+
+ assert(base);
+ assert(TAILQ_EMPTY(&base->eventqueue));
+ for (i=0; i < base->nactivequeues; ++i)
+ assert(TAILQ_EMPTY(base->activequeues[i]));
+
+ assert(RB_EMPTY(&base->timetree));
+
+ for (i = 0; i < base->nactivequeues; ++i)
+ free(base->activequeues[i]);
+ free(base->activequeues);
+
+ if (base->evsel->dealloc != NULL)
+ base->evsel->dealloc(base->evbase);
+
+ free(base);
+}
+
+int
+event_priority_init(int npriorities)
+{
+ return event_base_priority_init(current_base, npriorities);
+}
+
+int
+event_base_priority_init(struct event_base *base, int npriorities)
+{
+ int i;
+
+ if (base->event_count_active)
+ return (-1);
+
+ if (base->nactivequeues && npriorities != base->nactivequeues) {
+ for (i = 0; i < base->nactivequeues; ++i) {
+ free(base->activequeues[i]);
+ }
+ free(base->activequeues);
+ }
+
+ /* Allocate our priority queues */
+ base->nactivequeues = npriorities;
+ base->activequeues = (struct event_list **)calloc(base->nactivequeues,
+ npriorities * sizeof(struct event_list *));
+ if (base->activequeues == NULL)
+ event_err(1, "%s: calloc", __func__);
+
+ for (i = 0; i < base->nactivequeues; ++i) {
+ base->activequeues[i] = malloc(sizeof(struct event_list));
+ if (base->activequeues[i] == NULL)
+ event_err(1, "%s: malloc", __func__);
+ TAILQ_INIT(base->activequeues[i]);
+ }
+
+ return (0);
+}
+
+int
+event_haveevents(struct event_base *base)
+{
+ return (base->event_count > 0);
+}
+
+/*
+ * Active events are stored in priority queues. Lower priorities are always
+ * process before higher priorities. Low priority events can starve high
+ * priority ones.
+ */
+
+static void
+event_process_active(struct event_base *base)
+{
+ struct event *ev;
+ struct event_list *activeq = NULL;
+ int i;
+ short ncalls;
+
+ if (!base->event_count_active)
+ return;
+
+ for (i = 0; i < base->nactivequeues; ++i) {
+ if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
+ activeq = base->activequeues[i];
+ break;
+ }
+ }
+
+ for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
+
+ /* Allows deletes to work */
+ ncalls = ev->ev_ncalls;
+ ev->ev_pncalls = &ncalls;
+ while (ncalls) {
+ ncalls--;
+ ev->ev_ncalls = ncalls;
+ (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
+ }
+ }
+}
+
+/*
+ * Wait continously for events. We exit only if no events are left.
+ */
+
+int
+event_dispatch(void)
+{
+ return (event_loop(0));
+}
+
+int
+event_base_dispatch(struct event_base *event_base)
+{
+ return (event_base_loop(event_base, 0));
+}
+
+static void
+event_loopexit_cb(int fd, short what, void *arg)
+{
+ struct event_base *base = arg;
+ base->event_gotterm = 1;
+}
+
+/* not thread safe */
+
+int
+event_loopexit(struct timeval *tv)
+{
+ return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
+ current_base, tv));
+}
+
+int
+event_base_loopexit(struct event_base *event_base, struct timeval *tv)
+{
+ return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
+ event_base, tv));
+}
+
+/* not thread safe */
+
+int
+event_loop(int flags)
+{
+ return event_base_loop(current_base, flags);
+}
+
+int
+event_base_loop(struct event_base *base, int flags)
+{
+ const struct eventop *evsel = base->evsel;
+ void *evbase = base->evbase;
+ struct timeval tv;
+ int res, done;
+
+ done = 0;
+ while (!done) {
+ /* Calculate the initial events that we are waiting for */
+ if (evsel->recalc(base, evbase, 0) == -1)
+ return (-1);
+
+ /* Terminate the loop if we have been asked to */
+ if (base->event_gotterm) {
+ base->event_gotterm = 0;
+ break;
+ }
+
+ /* You cannot use this interface for multi-threaded apps */
+ 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 */
+ gettime(&tv);
+ if (timercmp(&tv, &base->event_tv, <)) {
+ struct timeval off;
+ event_debug(("%s: time is running backwards, corrected",
+ __func__));
+ timersub(&base->event_tv, &tv, &off);
+ timeout_correct(base, &off);
+ }
+ base->event_tv = tv;
+
+ if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK))
+ timeout_next(base, &tv);
+ else
+ timerclear(&tv);
+
+ /* If we have no events, we just exit */
+ if (!event_haveevents(base)) {
+ event_debug(("%s: no events registered.", __func__));
+ return (1);
+ }
+
+ res = evsel->dispatch(base, evbase, &tv);
+
+ if (res == -1)
+ return (-1);
+
+ timeout_process(base);
+
+ if (base->event_count_active) {
+ event_process_active(base);
+ if (!base->event_count_active && (flags & EVLOOP_ONCE))
+ done = 1;
+ } else if (flags & EVLOOP_NONBLOCK)
+ done = 1;
+ }
+
+ event_debug(("%s: asked to terminate loop.", __func__));
+ return (0);
+}
+
+/* Sets up an event for processing once */
+
+struct event_once {
+ struct event ev;
+
+ void (*cb)(int, short, void *);
+ void *arg;
+};
+
+/* One-time callback, it deletes itself */
+
+static void
+event_once_cb(int fd, short events, void *arg)
+{
+ struct event_once *eonce = arg;
+
+ (*eonce->cb)(fd, events, eonce->arg);
+ free(eonce);
+}
+
+/* Schedules an event once */
+
+int
+event_once(int fd, short events,
+ void (*callback)(int, short, void *), void *arg, struct timeval *tv)
+{
+ struct event_once *eonce;
+ struct timeval etv;
+
+ /* We cannot support signals that just fire once */
+ if (events & EV_SIGNAL)
+ return (-1);
+
+ if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
+ return (-1);
+
+ eonce->cb = callback;
+ eonce->arg = arg;
+
+ if (events == EV_TIMEOUT) {
+ if (tv == NULL) {
+ timerclear(&etv);
+ tv = &etv;
+ }
+
+ evtimer_set(&eonce->ev, event_once_cb, eonce);
+ } else if (events & (EV_READ|EV_WRITE)) {
+ events &= EV_READ|EV_WRITE;
+
+ event_set(&eonce->ev, fd, events, event_once_cb, eonce);
+ } else {
+ /* Bad event combination */
+ free(eonce);
+ return (-1);
+ }
+
+ event_add(&eonce->ev, tv);
+
+ return (0);
+}
+
+void
+event_set(struct event *ev, int fd, short events,
+ void (*callback)(int, short, void *), void *arg)
+{
+ /* Take the current base - caller needs to set the real base later */
+ ev->ev_base = current_base;
+
+ ev->ev_callback = callback;
+ ev->ev_arg = arg;
+ ev->ev_fd = fd;
+ ev->ev_events = events;
+ ev->ev_flags = EVLIST_INIT;
+ ev->ev_ncalls = 0;
+ ev->ev_pncalls = NULL;
+
+ /* by default, we put new events into the middle priority */
+ ev->ev_pri = current_base->nactivequeues/2;
+}
+
+int
+event_base_set(struct event_base *base, struct event *ev)
+{
+ /* Only innocent events may be assigned to a different base */
+ if (ev->ev_flags != EVLIST_INIT)
+ return (-1);
+
+ ev->ev_base = base;
+ ev->ev_pri = base->nactivequeues/2;
+
+ return (0);
+}
+
+/*
+ * Set's the priority of an event - if an event is already scheduled
+ * changing the priority is going to fail.
+ */
+
+int
+event_priority_set(struct event *ev, int pri)
+{
+ if (ev->ev_flags & EVLIST_ACTIVE)
+ return (-1);
+ if (pri < 0 || pri >= ev->ev_base->nactivequeues)
+ return (-1);
+
+ ev->ev_pri = pri;
+
+ return (0);
+}
+
+/*
+ * Checks if a specific event is pending or scheduled.
+ */
+
+int
+event_pending(struct event *ev, short event, struct timeval *tv)
+{
+ struct timeval now, res;
+ 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;
+ if (ev->ev_flags & EVLIST_SIGNAL)
+ flags |= EV_SIGNAL;
+
+ event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
+
+ /* See if there is a timeout that we should report */
+ if (tv != NULL && (flags & event & EV_TIMEOUT)) {
+ gettime(&now);
+ timersub(&ev->ev_timeout, &now, &res);
+ /* correctly remap to real time */
+ gettimeofday(&now, NULL);
+ timeradd(&now, &res, tv);
+ }
+
+ return (flags & event);
+}
+
+int
+event_add(struct event *ev, struct timeval *tv)
+{
+ struct event_base *base = ev->ev_base;
+ const struct eventop *evsel = base->evsel;
+ void *evbase = base->evbase;
+
+ event_debug((
+ "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;
+
+ if (ev->ev_flags & EVLIST_TIMEOUT)
+ event_queue_remove(base, ev, EVLIST_TIMEOUT);
+
+ /* Check if it is active due to a timeout. Rescheduling
+ * this timeout before the callback can be executed
+ * removes it from the active list. */
+ if ((ev->ev_flags & EVLIST_ACTIVE) &&
+ (ev->ev_res & EV_TIMEOUT)) {
+ /* See if we are just active executing this
+ * event in a loop
+ */
+ if (ev->ev_ncalls && ev->ev_pncalls) {
+ /* Abort loop */
+ *ev->ev_pncalls = 0;
+ }
+
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
+ }
+
+ gettime(&now);
+ timeradd(&now, tv, &ev->ev_timeout);
+
+ event_debug((
+ "event_add: timeout in %d seconds, call %p",
+ tv->tv_sec, ev->ev_callback));
+
+ event_queue_insert(base, ev, EVLIST_TIMEOUT);
+ }
+
+ if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
+ !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
+ event_queue_insert(base, ev, EVLIST_INSERTED);
+
+ return (evsel->add(evbase, ev));
+ } else if ((ev->ev_events & EV_SIGNAL) &&
+ !(ev->ev_flags & EVLIST_SIGNAL)) {
+ event_queue_insert(base, ev, EVLIST_SIGNAL);
+
+ return (evsel->add(evbase, ev));
+ }
+
+ return (0);
+}
+
+int
+event_del(struct event *ev)
+{
+ struct event_base *base;
+ const struct eventop *evsel;
+ void *evbase;
+
+ event_debug(("event_del: %p, callback %p",
+ ev, ev->ev_callback));
+
+ /* An event without a base has not been added */
+ if (ev->ev_base == NULL)
+ return (-1);
+
+ base = ev->ev_base;
+ evsel = base->evsel;
+ evbase = base->evbase;
+
+ assert(!(ev->ev_flags & ~EVLIST_ALL));
+
+ /* See if we are just active executing this event in a loop */
+ if (ev->ev_ncalls && ev->ev_pncalls) {
+ /* Abort loop */
+ *ev->ev_pncalls = 0;
+ }
+
+ if (ev->ev_flags & EVLIST_TIMEOUT)
+ event_queue_remove(base, ev, EVLIST_TIMEOUT);
+
+ if (ev->ev_flags & EVLIST_ACTIVE)
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
+
+ if (ev->ev_flags & EVLIST_INSERTED) {
+ event_queue_remove(base, ev, EVLIST_INSERTED);
+ return (evsel->del(evbase, ev));
+ } else if (ev->ev_flags & EVLIST_SIGNAL) {
+ event_queue_remove(base, ev, EVLIST_SIGNAL);
+ return (evsel->del(evbase, ev));
+ }
+
+ return (0);
+}
+
+void
+event_active(struct event *ev, int res, short ncalls)
+{
+ /* We get different kinds of events, add them together */
+ if (ev->ev_flags & EVLIST_ACTIVE) {
+ ev->ev_res |= res;
+ return;
+ }
+
+ ev->ev_res = res;
+ ev->ev_ncalls = ncalls;
+ ev->ev_pncalls = NULL;
+ event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
+}
+
+int
+timeout_next(struct event_base *base, struct timeval *tv)
+{
+ struct timeval dflt = TIMEOUT_DEFAULT;
+
+ struct timeval now;
+ struct event *ev;
+
+ if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) {
+ *tv = dflt;
+ return (0);
+ }
+
+ if (gettime(&now) == -1)
+ return (-1);
+
+ if (timercmp(&ev->ev_timeout, &now, <=)) {
+ timerclear(tv);
+ return (0);
+ }
+
+ timersub(&ev->ev_timeout, &now, tv);
+
+ assert(tv->tv_sec >= 0);
+ assert(tv->tv_usec >= 0);
+
+ event_debug(("timeout_next: in %d seconds", tv->tv_sec));
+ return (0);
+}
+
+static void
+timeout_correct(struct event_base *base, 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, &base->timetree)
+ timersub(&ev->ev_timeout, off, &ev->ev_timeout);
+}
+
+void
+timeout_process(struct event_base *base)
+{
+ struct timeval now;
+ struct event *ev, *next;
+
+ gettime(&now);
+
+ for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
+ if (timercmp(&ev->ev_timeout, &now, >))
+ break;
+ next = RB_NEXT(event_tree, &base->timetree, ev);
+
+ event_queue_remove(base, ev, EVLIST_TIMEOUT);
+
+ /* delete this event from the I/O queues */
+ event_del(ev);
+
+ event_debug(("timeout_process: call %p",
+ ev->ev_callback));
+ event_active(ev, EV_TIMEOUT, 1);
+ }
+}
+
+void
+event_queue_remove(struct event_base *base, struct event *ev, int queue)
+{
+ int docount = 1;
+
+ if (!(ev->ev_flags & queue))
+ event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
+ ev, ev->ev_fd, queue);
+
+ if (ev->ev_flags & EVLIST_INTERNAL)
+ docount = 0;
+
+ if (docount)
+ base->event_count--;
+
+ ev->ev_flags &= ~queue;
+ switch (queue) {
+ case EVLIST_ACTIVE:
+ if (docount)
+ base->event_count_active--;
+ TAILQ_REMOVE(base->activequeues[ev->ev_pri],
+ ev, ev_active_next);
+ break;
+ case EVLIST_SIGNAL:
+ TAILQ_REMOVE(&signalqueue, ev, ev_signal_next);
+ break;
+ case EVLIST_TIMEOUT:
+ RB_REMOVE(event_tree, &base->timetree, ev);
+ break;
+ case EVLIST_INSERTED:
+ TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
+ break;
+ default:
+ event_errx(1, "%s: unknown queue %x", __func__, queue);
+ }
+}
+
+void
+event_queue_insert(struct event_base *base, struct event *ev, int queue)
+{
+ int docount = 1;
+
+ if (ev->ev_flags & queue) {
+ /* Double insertion is possible for active events */
+ if (queue & EVLIST_ACTIVE)
+ return;
+
+ event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
+ ev, ev->ev_fd, queue);
+ }
+
+ if (ev->ev_flags & EVLIST_INTERNAL)
+ docount = 0;
+
+ if (docount)
+ base->event_count++;
+
+ ev->ev_flags |= queue;
+ switch (queue) {
+ case EVLIST_ACTIVE:
+ if (docount)
+ base->event_count_active++;
+ TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
+ ev,ev_active_next);
+ break;
+ case EVLIST_SIGNAL:
+ TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
+ break;
+ case EVLIST_TIMEOUT: {
+ struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev);
+ assert(tmp == NULL);
+ break;
+ }
+ case EVLIST_INSERTED:
+ TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
+ break;
+ default:
+ event_errx(1, "%s: unknown queue %x", __func__, queue);
+ }
+}
+
+/* Functions for debugging */
+
+const char *
+event_get_version(void)
+{
+ return (VERSION);
+}
+
+/*
+ * No thread-safe interface needed - the information should be the same
+ * for all threads.
+ */
+
+const char *
+event_get_method(void)
+{
+ return (current_base->evsel->name);
+}
diff --git a/event.h b/event.h
new file mode 100644
index 00000000..bd370f84
--- /dev/null
+++ b/event.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2000-2004 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. 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
+
+#include <stdarg.h>
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+typedef unsigned char u_char;
+#endif
+
+#define EVLIST_TIMEOUT 0x01
+#define EVLIST_INSERTED 0x02
+#define EVLIST_SIGNAL 0x04
+#define EVLIST_ACTIVE 0x08
+#define EVLIST_INTERNAL 0x10
+#define EVLIST_INIT 0x80
+
+/* EVLIST_X_ Private space: 0x1000-0xf000 */
+#define EVLIST_ALL (0xf000 | 0x9f)
+
+#define EV_TIMEOUT 0x01
+#define EV_READ 0x02
+#define EV_WRITE 0x04
+#define EV_SIGNAL 0x08
+#define EV_PERSIST 0x10 /* Persistant event */
+
+/* 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_base;
+struct event {
+ TAILQ_ENTRY (event) ev_next;
+ TAILQ_ENTRY (event) ev_active_next;
+ TAILQ_ENTRY (event) ev_signal_next;
+ RB_ENTRY (event) ev_timeout_node;
+
+ struct event_base *ev_base;
+ int ev_fd;
+ short ev_events;
+ short ev_ncalls;
+ short *ev_pncalls; /* Allows deletes in callback */
+
+ struct timeval ev_timeout;
+
+ int ev_pri; /* smaller numbers are higher priority */
+
+ void (*ev_callback)(int, short, void *arg);
+ void *ev_arg;
+
+ int ev_res; /* result passed to event callback */
+ int ev_flags;
+};
+
+#define EVENT_SIGNAL(ev) (int)(ev)->ev_fd
+#define EVENT_FD(ev) (int)(ev)->ev_fd
+
+/*
+ * Key-Value pairs. Can be used for HTTP headers but also for
+ * query argument parsing.
+ */
+struct evkeyval {
+ TAILQ_ENTRY(evkeyval) next;
+
+ char *key;
+ char *value;
+};
+
+#ifdef _EVENT_DEFINED_TQENTRY
+#undef TAILQ_ENTRY
+struct event_list;
+struct evkeyvalq;
+#undef _EVENT_DEFINED_TQENTRY
+#else
+TAILQ_HEAD (event_list, event);
+TAILQ_HEAD (evkeyvalq, evkeyval);
+#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)(struct event_base *, void *, int);
+ int (*dispatch)(struct event_base *, void *, struct timeval *);
+ void (*dealloc)(void *);
+};
+
+#define TIMEOUT_DEFAULT {5, 0}
+
+void *event_init(void);
+int event_dispatch(void);
+int event_base_dispatch(struct event_base *);
+void event_base_free(struct event_base *);
+
+#define _EVENT_LOG_DEBUG 0
+#define _EVENT_LOG_MSG 1
+#define _EVENT_LOG_WARN 2
+#define _EVENT_LOG_ERR 3
+typedef void (*event_log_cb)(int severity, const char *msg);
+void event_set_log_callback(event_log_cb cb);
+
+/* Associate a different event base with an event */
+int event_base_set(struct event_base *, struct event *);
+
+#define EVLOOP_ONCE 0x01
+#define EVLOOP_NONBLOCK 0x02
+int event_loop(int);
+int event_base_loop(struct event_base *, int);
+int event_loopexit(struct timeval *); /* Causes the loop to exit */
+int event_base_loopexit(struct event_base *, struct timeval *);
+
+#define evtimer_add(ev, tv) event_add(ev, tv)
+#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
+#define evtimer_del(ev) event_del(ev)
+#define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
+#define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
+
+#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)
+
+#define signal_add(ev, tv) event_add(ev, tv)
+#define signal_set(ev, x, cb, arg) \
+ event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
+#define signal_del(ev) event_del(ev)
+#define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv)
+#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
+
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
+
+int event_add(struct event *, struct timeval *);
+int event_del(struct event *);
+void event_active(struct event *, int, short);
+
+int event_pending(struct event *, short, struct timeval *);
+
+#ifdef WIN32
+#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != INVALID_HANDLE_VALUE)
+#else
+#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
+#endif
+
+/* Some simple debugging functions */
+const char *event_get_version(void);
+const char *event_get_method(void);
+
+/* These functions deal with event priorities */
+
+int event_priority_init(int);
+int event_base_priority_init(struct event_base *, int);
+int event_priority_set(struct event *, int);
+
+/* These functions deal with buffering input and output */
+
+struct evbuffer {
+ u_char *buffer;
+ u_char *orig_buffer;
+
+ size_t misalign;
+ size_t totallen;
+ size_t off;
+
+ void (*cb)(struct evbuffer *, size_t, size_t, void *);
+ void *cbarg;
+};
+
+/* Just for error reporting - use other constants otherwise */
+#define EVBUFFER_READ 0x01
+#define EVBUFFER_WRITE 0x02
+#define EVBUFFER_EOF 0x10
+#define EVBUFFER_ERROR 0x20
+#define EVBUFFER_TIMEOUT 0x40
+
+struct bufferevent;
+typedef void (*evbuffercb)(struct bufferevent *, void *);
+typedef void (*everrorcb)(struct bufferevent *, short what, void *);
+
+struct event_watermark {
+ size_t low;
+ size_t high;
+};
+
+struct bufferevent {
+ struct event ev_read;
+ struct event ev_write;
+
+ struct evbuffer *input;
+ struct evbuffer *output;
+
+ struct event_watermark wm_read;
+ struct event_watermark wm_write;
+
+ evbuffercb readcb;
+ evbuffercb writecb;
+ everrorcb errorcb;
+ void *cbarg;
+
+ int timeout_read; /* in seconds */
+ int timeout_write; /* in seconds */
+
+ short enabled; /* events that are currently enabled */
+};
+
+struct bufferevent *bufferevent_new(int fd,
+ evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
+int bufferevent_priority_set(struct bufferevent *bufev, int pri);
+void bufferevent_free(struct bufferevent *bufev);
+int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
+int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
+size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
+int bufferevent_enable(struct bufferevent *bufev, short event);
+int bufferevent_disable(struct bufferevent *bufev, short event);
+void bufferevent_settimeout(struct bufferevent *bufev,
+ int timeout_read, int timeout_write);
+
+#define EVBUFFER_LENGTH(x) (x)->off
+#define EVBUFFER_DATA(x) (x)->buffer
+#define EVBUFFER_INPUT(x) (x)->input
+#define EVBUFFER_OUTPUT(x) (x)->output
+
+struct evbuffer *evbuffer_new(void);
+void evbuffer_free(struct evbuffer *);
+int evbuffer_expand(struct evbuffer *, size_t);
+int evbuffer_add(struct evbuffer *, void *, size_t);
+int evbuffer_remove(struct evbuffer *, void *, size_t);
+char *evbuffer_readline(struct evbuffer *);
+int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
+int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
+int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
+void evbuffer_drain(struct evbuffer *, size_t);
+int evbuffer_write(struct evbuffer *, int);
+int evbuffer_read(struct evbuffer *, int, int);
+u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
+void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
+
+/*
+ * Marshaling tagged data - We assume that all tags are inserted in their
+ * numeric order - so that unknown tags will always be higher than the
+ * known ones - and we can just ignore the end of an event buffer.
+ */
+
+void evtag_init(void);
+
+void evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, const void *data,
+ u_int16_t len);
+
+void encode_int(struct evbuffer *evbuf, u_int32_t number);
+
+void evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag,
+ u_int32_t integer);
+
+void evtag_marshal_string(struct evbuffer *buf, u_int8_t tag,
+ const char *string);
+
+void evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag,
+ struct timeval *tv);
+
+void evtag_test(void);
+
+int evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag,
+ struct evbuffer *dst);
+int evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag);
+int evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength);
+int evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength);
+int evtag_consume(struct evbuffer *evbuf);
+
+int evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
+ u_int32_t *pinteger);
+
+int evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
+ size_t len);
+
+int evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
+ char **pstring);
+
+int evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
+ struct timeval *ptv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_H_ */
diff --git a/event_rpcgen.py b/event_rpcgen.py
new file mode 100755
index 00000000..d9265120
--- /dev/null
+++ b/event_rpcgen.py
@@ -0,0 +1,1378 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2005 Niels Provos <provos@citi.umich.edu>
+# All rights reserved.
+#
+# Generates marshalling code based on libevent.
+
+import sys
+import re
+
+#
+_NAME = "event_rpcgen.py"
+_VERSION = "0.1"
+_STRUCT_RE = '[a-z][a-z_0-9]*'
+
+# Globals
+line_count = 0
+
+leading = re.compile(r'^\s+')
+trailing = re.compile(r'\s+$')
+white = re.compile(r'^\s+')
+cppcomment = re.compile(r'\/\/.*$')
+cppdirect = []
+
+# Holds everything that makes a struct
+class Struct:
+ def __init__(self, name):
+ self._name = name
+ self._entries = []
+ self._tags = {}
+ print >>sys.stderr, ' Created struct: %s' % name
+
+ def AddEntry(self, entry):
+ if self._tags.has_key(entry.Tag()):
+ print >>sys.stderr, ( 'Entry "%s" duplicates tag number '
+ '%d from "%s" around line %d' ) % (
+ entry.Name(), entry.Tag(),
+ self._tags[entry.Tag()], line_count)
+ sys.exit(1)
+ self._entries.append(entry)
+ self._tags[entry.Tag()] = entry.Name()
+ print >>sys.stderr, ' Added entry: %s' % entry.Name()
+
+ def Name(self):
+ return self._name
+
+ def EntryTagName(self, entry):
+ """Creates the name inside an enumeration for distinguishing data
+ types."""
+ name = "%s_%s" % (self._name, entry.Name())
+ return name.upper()
+
+ def PrintIdented(self, file, ident, code):
+ """Takes an array, add indentation to each entry and prints it."""
+ for entry in code:
+ print >>file, '%s%s' % (ident, entry)
+
+ def PrintTags(self, file):
+ """Prints the tag definitions for a structure."""
+ print >>file, '/* Tag definition for %s */' % self._name
+ print >>file, 'enum %s_ {' % self._name.lower()
+ for entry in self._entries:
+ print >>file, ' %s=%d,' % (self.EntryTagName(entry),
+ entry.Tag())
+ print >>file, ' %s_MAX_TAGS' % (self._name.upper())
+ print >>file, '};\n'
+
+ def PrintForwardDeclaration(self, file):
+ print >>file, 'struct %s;' % self._name
+
+ def PrintDeclaration(self, file):
+ print >>file, '/* Structure declaration for %s */' % self._name
+ print >>file, 'struct %s {' % self._name
+ for entry in self._entries:
+ dcl = entry.Declaration()
+ dcl.extend(
+ entry.AssignDeclaration('(*%s_assign)' % entry.Name()))
+ dcl.extend(
+ entry.GetDeclaration('(*%s_get)' % entry.Name()))
+ if entry.Array():
+ dcl.extend(
+ entry.AddDeclaration('(*%s_add)' % entry.Name()))
+ self.PrintIdented(file, ' ', dcl)
+ print >>file, ''
+ for entry in self._entries:
+ print >>file, ' u_int8_t %s_set;' % entry.Name()
+ print >>file, '};\n'
+
+ print >>file, (
+ 'struct %s *%s_new();\n' % (self._name, self._name) +
+ 'void %s_free(struct %s *);\n' % (self._name, self._name) +
+ 'void %s_clear(struct %s *);\n' % (self._name, self._name) +
+ 'void %s_marshal(struct evbuffer *, const struct %s *);\n' % (
+ self._name, self._name) +
+ 'int %s_unmarshal(struct %s *, struct evbuffer *);\n' % (
+ self._name, self._name) +
+ 'int %s_complete(struct %s *);' % (self._name, self._name)
+ )
+ print >>file, ('void evtag_marshal_%s(struct evbuffer *, u_int8_t, '
+ 'const struct %s *);') % ( self._name, self._name)
+ print >>file, ('int evtag_unmarshal_%s(struct evbuffer *, u_int8_t, '
+ 'struct %s *);') % ( self._name, self._name)
+
+ # Write a setting function of every variable
+ for entry in self._entries:
+ self.PrintIdented(file, '', entry.AssignDeclaration(
+ entry.AssignFuncName()))
+ self.PrintIdented(file, '', entry.GetDeclaration(
+ entry.GetFuncName()))
+ if entry.Array():
+ self.PrintIdented(file, '', entry.AddDeclaration(
+ entry.AddFuncName()))
+
+ print >>file, '/* --- %s done --- */\n' % self._name
+
+ def PrintCode(self, file):
+ print >>file, ('/*\n'
+ ' * Implementation of %s\n'
+ ' */\n') % self._name
+
+ # Creation
+ print >>file, ( 'struct %s *\n' % self._name +
+ '%s_new()\n' % self._name +
+ '{\n'
+ ' struct %s *tmp;\n' % self._name +
+ ' if ((tmp = malloc(sizeof(struct %s))) == NULL) {\n'
+ ' event_warn("%%s: malloc", __func__);\n'
+ ' return (NULL);\n' % self._name +
+ ' }'
+ )
+ for entry in self._entries:
+ self.PrintIdented(file, ' ', entry.CodeNew('tmp'))
+ print >>file, ' tmp->%s_set = 0;\n' % entry.Name()
+
+ print >>file, (' return (tmp);\n'
+ '}\n')
+
+ # Adding
+ for entry in self._entries:
+ if entry.Array():
+ self.PrintIdented(file, '', entry.CodeAdd())
+ print >>file, ''
+
+ # Assigning
+ for entry in self._entries:
+ self.PrintIdented(file, '', entry.CodeAssign())
+ print >>file, ''
+
+ # Getting
+ for entry in self._entries:
+ self.PrintIdented(file, '', entry.CodeGet())
+ print >>file, ''
+
+ # Clearing
+ print >>file, ( 'void\n'
+ '%s_clear(struct %s *tmp)\n' % (
+ self._name, self._name)+
+ '{'
+ )
+ for entry in self._entries:
+ self.PrintIdented(file, ' ', entry.CodeClear('tmp'))
+
+ print >>file, '}\n'
+
+ # Freeing
+ print >>file, ( 'void\n'
+ '%s_free(struct %s *tmp)\n' % (
+ self._name, self._name)+
+ '{'
+ )
+ for entry in self._entries:
+ self.PrintIdented(file, ' ', entry.CodeFree('tmp'))
+
+ print >>file, (' free(tmp);\n'
+ '}\n')
+
+ # Marshaling
+ print >>file, ('void\n'
+ '%s_marshal(struct evbuffer *evbuf, '
+ 'const struct %s *tmp)' % (self._name, self._name) +
+ '{')
+ for entry in self._entries:
+ indent = ' '
+ # Optional entries do not have to be set
+ if entry.Optional():
+ indent += ' '
+ print >>file, ' if (tmp->%s_set) {' % entry.Name()
+ self.PrintIdented(
+ file, indent,
+ entry.CodeMarshal('evbuf', self.EntryTagName(entry), 'tmp'))
+ if entry.Optional():
+ print >>file, ' }'
+
+ print >>file, '}\n'
+
+ # Unmarshaling
+ print >>file, ('int\n'
+ '%s_unmarshal(struct %s *tmp, '
+ ' struct evbuffer *evbuf)\n' % (
+ self._name, self._name) +
+ '{\n'
+ ' u_int8_t tag;\n'
+ ' while (EVBUFFER_LENGTH(evbuf) > 0) {\n'
+ ' if (evtag_peek(evbuf, &tag) == -1)\n'
+ ' return (-1);\n'
+ ' switch (tag) {\n'
+ )
+ for entry in self._entries:
+ print >>file, ' case %s:\n' % self.EntryTagName(entry)
+ if not entry.Array():
+ print >>file, (
+ ' if (tmp->%s_set)\n'
+ ' return (-1);'
+ ) % (entry.Name())
+
+ self.PrintIdented(
+ file, ' ',
+ entry.CodeUnmarshal('evbuf',
+ self.EntryTagName(entry), 'tmp'))
+
+ print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() +
+ ' break;\n' )
+ print >>file, ( ' default:\n'
+ ' return -1;\n'
+ ' }\n'
+ ' }\n' )
+ # Check if it was decoded completely
+ print >>file, ( ' if (%s_complete(tmp) == -1)\n' % self._name +
+ ' return (-1);')
+
+ # Successfully decoded
+ print >>file, ( ' return (0);\n'
+ '}\n')
+
+ # Checking if a structure has all the required data
+ print >>file, (
+ 'int\n'
+ '%s_complete(struct %s *msg)\n' % (self._name, self._name) +
+ '{' )
+ for entry in self._entries:
+ self.PrintIdented(
+ file, ' ',
+ entry.CodeComplete('msg'))
+ print >>file, (
+ ' return (0);\n'
+ '}\n' )
+
+ # Complete message unmarshaling
+ print >>file, (
+ 'int\n'
+ 'evtag_unmarshal_%s(struct evbuffer *evbuf, u_int8_t need_tag, '
+ ' struct %s *msg)'
+ ) % (self._name, self._name)
+ print >>file, (
+ '{\n'
+ ' u_int8_t tag;\n'
+ ' int res = -1;\n'
+ '\n'
+ ' struct evbuffer *tmp = evbuffer_new();\n'
+ '\n'
+ ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1'
+ ' || tag != need_tag)\n'
+ ' goto error;\n'
+ '\n'
+ ' if (%s_unmarshal(msg, tmp) == -1)\n'
+ ' goto error;\n'
+ '\n'
+ ' res = 0;\n'
+ '\n'
+ ' error:\n'
+ ' evbuffer_free(tmp);\n'
+ ' return (res);\n'
+ '}\n' ) % self._name
+
+ # Complete message marshaling
+ print >>file, (
+ 'void\n'
+ 'evtag_marshal_%s(struct evbuffer *evbuf, u_int8_t tag, '
+ 'const struct %s *msg)\n' % (self._name, self._name) +
+ '{\n'
+ ' struct evbuffer *_buf = evbuffer_new();\n'
+ ' assert(_buf != NULL);\n'
+ ' evbuffer_drain(_buf, -1);\n'
+ ' %s_marshal(_buf, msg);\n' % self._name +
+ ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), '
+ 'EVBUFFER_LENGTH(_buf));\n'
+ ' evbuffer_free(_buf);\n'
+ '}\n' )
+
+class Entry:
+ def __init__(self, type, name, tag):
+ self._type = type
+ self._name = name
+ self._tag = int(tag)
+ self._ctype = type
+ self._optional = 0
+ self._can_be_array = 0
+ self._array = 0
+ self._line_count = -1
+ self._struct = None
+
+ def SetStruct(self, struct):
+ self._struct = struct
+
+ def LineCount(self):
+ assert self._line_count != -1
+ return self._line_count
+
+ def SetLineCount(self, number):
+ self._line_count = number
+
+ def Array(self):
+ return self._array
+
+ def Optional(self):
+ return self._optional
+
+ def Tag(self):
+ return self._tag
+
+ def Name(self):
+ return self._name
+
+ def Type(self):
+ return self._type
+
+ def MakeArray(self, yes=1):
+ self._array = yes
+
+ def MakeOptional(self):
+ self._optional = 1
+
+ def GetFuncName(self):
+ return '%s_%s_get' % (self._struct.Name(), self._name)
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeGet(self):
+ code = [ 'int',
+ '%s_%s_get(struct %s *msg, %s *value)' % (
+ self._struct.Name(), self._name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % self._name,
+ ' return (-1);',
+ ' *value = msg->%s_data;' % self._name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def AssignFuncName(self):
+ return '%s_%s_assign' % (self._struct.Name(), self._name)
+
+ def AddFuncName(self):
+ return '%s_%s_add' % (self._struct.Name(), self._name)
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeAssign(self):
+ code = [ 'int',
+ '%s_%s_assign(struct %s *msg, const %s value)' % (
+ self._struct.Name(), self._name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' msg->%s_set = 1;' % self._name,
+ ' msg->%s_data = value;' % self._name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ '%s->%s_set = 0;' % (structname, self.Name()) ]
+
+ return code
+
+ def CodeComplete(self, structname):
+ if self.Optional():
+ return []
+
+ code = [ 'if (!%s->%s_set)' % (structname, self.Name()),
+ ' return (-1);' ]
+
+ return code
+
+ def CodeFree(self, name):
+ return []
+
+ def CodeNew(self, name):
+ code = [ '%s->%s_assign = %s_%s_assign;' % (
+ name, self._name, self._struct.Name(), self._name ),
+ '%s->%s_get = %s_%s_get;' % (
+ name, self._name, self._struct.Name(), self._name ),
+ ]
+ if self.Array():
+ code.append(
+ '%s->%s_add = %s_%s_add;' % (
+ name, self._name, self._struct.Name(), self._name ) )
+ return code
+
+ def Verify(self):
+ if self.Array() and not self._can_be_array:
+ print >>sys.stderr, (
+ 'Entry "%s" cannot be created as an array '
+ 'around line %d' ) % (self._name, self.LineCount())
+ sys.exit(1)
+ if not self._struct:
+ print >>sys.stderr, (
+ 'Entry "%s" does not know which struct it belongs to '
+ 'around line %d' ) % (self._name, self.LineCount())
+ sys.exit(1)
+ if self._optional and self._array:
+ print >>sys.stderr, ( 'Entry "%s" has illegal combination of '
+ 'optional and array around line %d' ) % (
+ self._name, self.LineCount() )
+ sys.exit(1)
+
+class EntryBytes(Entry):
+ def __init__(self, type, name, tag, length):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._length = length
+ self._ctype = 'u_int8_t'
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s **);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def Declaration(self):
+ dcl = ['u_int8_t %s_data[%s];' % (self._name, self._length)]
+
+ return dcl
+
+ def CodeGet(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_get(struct %s *msg, %s **value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' *value = msg->%s_data;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_assign(struct %s *msg, const %s *value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' msg->%s_set = 1;' % name,
+ ' memcpy(msg->%s_data, value, %s);' % (
+ name, self._length),
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [ 'if (evtag_unmarshal_fixed(%s, %s, ' % (buf, tag_name) +
+ '%s->%s_data, ' % (var_name, self._name) +
+ 'sizeof(%s->%s_data)) == -1) {' % (
+ var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal(%s, %s, %s->%s_data, sizeof(%s->%s_data));' % (
+ buf, tag_name, var_name, self._name, var_name, self._name )]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ '%s->%s_set = 0;' % (structname, self.Name()),
+ 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % (
+ structname, self._name, structname, self._name)]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % (
+ name, self._name, name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def Verify(self):
+ if not self._length:
+ print >>sys.stderr, 'Entry "%s" needs a length around line %d' % (
+ self._name, self.LineCount() )
+ sys.exit(1)
+
+ Entry.Verify(self)
+
+class EntryInt(Entry):
+ def __init__(self, type, name, tag):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._ctype = 'u_int32_t'
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = ['if (evtag_unmarshal_int(%s, %s, &%s->%s_data) == -1) {' % (
+ buf, tag_name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}' ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal_int(%s, %s, %s->%s_data);' % (
+ buf, tag_name, var_name, self._name)]
+ return code
+
+ def Declaration(self):
+ dcl = ['u_int32_t %s_data;' % self._name]
+
+ return dcl
+
+class EntryString(Entry):
+ def __init__(self, type, name, tag):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._ctype = 'char *'
+
+ def CodeAssign(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_assign(struct %s *msg, const %s value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_data != NULL)' % name,
+ ' free(msg->%s_data);' % name,
+ ' if ((msg->%s_data = strdup(value)) == NULL)' % name,
+ ' return (-1);',
+ ' msg->%s_set = 1;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = ['if (evtag_unmarshal_string(%s, %s, &%s->%s_data) == -1) {' % (
+ buf, tag_name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal_string(%s, %s, %s->%s_data);' % (
+ buf, tag_name, var_name, self._name)]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' free (%s->%s_data);' % (structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['%s->%s_data = NULL;' % (name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = ['if (%s->%s_data != NULL)' % (name, self._name),
+ ' free (%s->%s_data); ' % (name, self._name)]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['char *%s_data;' % self._name]
+
+ return dcl
+
+class EntryStruct(Entry):
+ def __init__(self, type, name, tag, refname):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._can_be_array = 1
+ self._refname = refname
+ self._ctype = 'struct %s' % refname
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s **);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeGet(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_get(struct %s *msg, %s **value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1) {' % name,
+ ' msg->%s_data = %s_new();' % (name, self._refname),
+ ' if (msg->%s_data == NULL)' % name,
+ ' return (-1);',
+ ' msg->%s_set = 1;' % name,
+ ' }',
+ ' *value = msg->%s_data;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_assign(struct %s *msg, const %s *value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' struct evbuffer *tmp = NULL;',
+ ' if (msg->%s_set) {' % name,
+ ' %s_clear(msg->%s_data);' % (self._refname, name),
+ ' msg->%s_set = 0;' % name,
+ ' } else {',
+ ' msg->%s_data = %s_new();' % (name, self._refname),
+ ' if (msg->%s_data == NULL) {' % name,
+ ' event_warn("%%s: %s_new()", __func__);' % (
+ self._refname),
+ ' goto error;',
+ ' }',
+ ' }',
+ ' if ((tmp = evbuffer_new()) == NULL) {',
+ ' event_warn("%s: evbuffer_new()", __func__);',
+ ' goto error;',
+ ' }',
+ ' %s_marshal(tmp, value); ' % self._refname,
+ ' if (%s_unmarshal(msg->%s_data, tmp) == -1) {' % (
+ self._refname, name ),
+ ' event_warnx("%%s: %s_unmarshal", __func__);' % (
+ self._refname),
+ ' goto error;',
+ ' }',
+ ' msg->%s_set = 1;' % name,
+ ' evbuffer_free(tmp);',
+ ' return (0);',
+ ' error:',
+ ' if (tmp != NULL)',
+ ' evbuffer_free(tmp);',
+ ' if (msg->%s_data != NULL) {' % name,
+ ' %s_free(msg->%s_data);' % (self._refname, name),
+ ' msg->%s_data = NULL;' % name,
+ ' }',
+ ' return (-1);',
+ '}' ]
+ return code
+
+ def CodeComplete(self, structname):
+ if self.Optional():
+ code = [ 'if (%s->%s_set && %s_complete(%s->%s_data) == -1)' % (
+ structname, self.Name(),
+ self._refname, structname, self.Name()),
+ ' return (-1);' ]
+ else:
+ code = [ 'if (%s_complete(%s->%s_data) == -1)' % (
+ self._refname, structname, self.Name()),
+ ' return (-1);' ]
+
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = ['%s->%s_data = %s_new();' % (
+ var_name, self._name, self._refname),
+ 'if (%s->%s_data == NULL)' % (var_name, self._name),
+ ' return (-1);',
+ 'if (evtag_unmarshal_%s(%s, %s, %s->%s_data) == -1) {' % (
+ self._refname, buf, tag_name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal_%s(%s, %s, %s->%s_data);' % (
+ self._refname, buf, tag_name, var_name, self._name)]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' %s_free(%s->%s_data);' % (
+ self._refname, structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['%s->%s_data = NULL;' % (name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = ['if (%s->%s_data != NULL)' % (name, self._name),
+ ' %s_free(%s->%s_data); ' % (
+ self._refname, name, self._name)]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['struct %s *%s_data;' % (self._refname, self._name)]
+
+ return dcl
+
+class EntryVarBytes(Entry):
+ def __init__(self, type, name, tag):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._ctype = 'u_int8_t *'
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s *, u_int32_t *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s, u_int32_t);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_assign(struct %s *msg, '
+ 'const %s value, u_int32_t len)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_data != NULL)' % name,
+ ' free (msg->%s_data);' % name,
+ ' msg->%s_data = malloc(len);' % name,
+ ' if (msg->%s_data == NULL)' % name,
+ ' return (-1);',
+ ' msg->%s_set = 1;' % name,
+ ' msg->%s_length = len;' % name,
+ ' memcpy(msg->%s_data, value, len);' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeGet(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_get(struct %s *msg, %s *value, u_int32_t *plen)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' *value = msg->%s_data;' % name,
+ ' *plen = msg->%s_length;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = ['if (evtag_payload_length(%s, &%s->%s_length) == -1)' % (
+ buf, var_name, self._name),
+ ' return (-1);',
+ # We do not want DoS opportunities
+ 'if (%s->%s_length > EVBUFFER_LENGTH(%s))' % (
+ var_name, self._name, buf),
+ ' return (-1);',
+ 'if ((%s->%s_data = malloc(%s->%s_length)) == NULL)' % (
+ var_name, self._name, var_name, self._name),
+ ' return (-1);',
+ 'if (evtag_unmarshal_fixed(%s, %s, %s->%s_data, '
+ '%s->%s_length) == -1) {' % (
+ buf, tag_name, var_name, self._name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal(%s, %s, %s->%s_data, %s->%s_length);' % (
+ buf, tag_name, var_name, self._name, var_name, self._name)]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' free (%s->%s_data);' % (structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_length = 0;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['%s->%s_data = NULL;' % (name, self._name),
+ '%s->%s_length = 0;' % (name, self._name) ]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = ['if (%s->%s_data != NULL)' % (name, self._name),
+ ' free (%s->%s_data); ' % (name, self._name)]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['u_int8_t *%s_data;' % self._name,
+ 'u_int32_t %s_length;' % self._name]
+
+ return dcl
+
+class EntryArray(Entry):
+ def __init__(self, entry):
+ # Init base class
+ Entry.__init__(self, entry._type, entry._name, entry._tag)
+
+ self._entry = entry
+ self._refname = entry._refname
+ self._ctype = 'struct %s' % self._refname
+
+ def GetDeclaration(self, funcname):
+ """Allows direct access to elements of the array."""
+ code = [ 'int %s(struct %s *, int, %s **);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, int, const %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AddDeclaration(self, funcname):
+ code = [ '%s *%s(struct %s *);' % (
+ self._ctype, funcname, self._struct.Name() ) ]
+ return code
+
+ def CodeGet(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_get(struct %s *msg, int offset, %s **value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' if (offset >= msg->%s_length)' % name,
+ ' return (-1);',
+ ' *value = msg->%s_data[offset];' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [ 'int',
+ '%s_%s_assign(struct %s *msg, int off, const %s *value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' struct evbuffer *tmp = NULL;',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' if (off >= msg->%s_length)' % name,
+ ' return (-1);',
+ '',
+ ' %s_clear(msg->%s_data[off]);' % (self._refname, name),
+ ' if ((tmp = evbuffer_new()) == NULL) {',
+ ' event_warn("%s: evbuffer_new()", __func__);',
+ ' goto error;',
+ ' }',
+ ' %s_marshal(tmp, value); ' % self._refname,
+ ' if (%s_unmarshal(msg->%s_data[off], tmp) == -1) {' % (
+ self._refname, name ),
+ ' event_warnx("%%s: %s_unmarshal", __func__);' % (
+ self._refname),
+ ' goto error;',
+ ' }',
+ ' evbuffer_free(tmp);',
+ ' return (0);',
+ ' error:',
+ ' if (tmp != NULL)',
+ ' evbuffer_free(tmp);',
+ ' %s_clear(msg->%s_data[off]);' % (self._refname, name),
+ ' return (-1);',
+ '}' ]
+ return code
+
+ def CodeAdd(self):
+ name = self._name
+ code = [
+ '%s *' % self._ctype,
+ '%s_%s_add(struct %s *msg)' % (
+ self._struct.Name(), name, self._struct.Name()),
+ '{',
+ ' msg->%s_length++;' % name,
+ ' msg->%s_data = (struct %s**)realloc(msg->%s_data, '
+ ' msg->%s_length * sizeof(struct %s*));' % (
+ name, self._refname, name, name, self._refname ),
+ ' if (msg->%s_data == NULL)' % name,
+ ' return (NULL);',
+ ' msg->%s_data[msg->%s_length - 1] = %s_new();' % (
+ name, name, self._refname),
+ ' if (msg->%s_data[msg->%s_length - 1] == NULL) {' % (name, name),
+ ' msg->%s_length--; ' % name,
+ ' return (NULL);',
+ ' }',
+ ' msg->%s_set = 1;' % name,
+ ' return (msg->%s_data[msg->%s_length - 1]);' % (name, name),
+ '}'
+ ]
+ return code
+
+ def CodeComplete(self, structname):
+ code = []
+ if self.Optional():
+ code.append( 'if (%s->%s_set)' % (structname, self.Name()))
+
+ code.extend(['{',
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ structname, self.Name()),
+ ' if (%s_complete(%s->%s_data[i]) == -1)' % (
+ self._refname, structname, self.Name()),
+ ' return (-1);',
+ ' }',
+ '}'
+ ])
+
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = ['if (%s_%s_add(%s) == NULL)' % (
+ self._struct.Name(), self._name, var_name),
+ ' return (-1);',
+ 'if (evtag_unmarshal_%s(%s, %s, '
+ '%s->%s_data[%s->%s_length - 1]) == -1) {' % (
+ self._refname, buf, tag_name, var_name, self._name,
+ var_name, self._name),
+ ' %s->%s_length--; ' % (var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['{',
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ var_name, self._name),
+ ' evtag_marshal_%s(%s, %s, %s->%s_data[i]);' % (
+ self._refname, buf, tag_name, var_name, self._name),
+ ' }',
+ '}'
+ ]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ structname, self.Name()),
+ ' %s_free(%s->%s_data[i]);' % (
+ self._refname, structname, self.Name()),
+ ' }',
+ ' free(%s->%s_data);' % (structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ ' %s->%s_length = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['%s->%s_data = NULL;' % (name, self._name),
+ '%s->%s_length = 0;' % (name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = ['if (%s->%s_data != NULL) {' % (name, self._name),
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ name, self._name),
+ ' %s_free(%s->%s_data[i]); ' % (
+ self._refname, name, self._name),
+ ' %s->%s_data[i] = NULL;' % (name, self._name),
+ ' }',
+ ' free(%s->%s_data);' % (name, self._name),
+ ' %s->%s_data = NULL;' % (name, self._name),
+ ' %s->%s_length = 0;' % (name, self._name),
+ '}'
+ ]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['struct %s **%s_data;' % (self._refname, self._name),
+ 'int %s_length;' % self._name]
+
+ return dcl
+
+def NormalizeLine(line):
+ global leading
+ global trailing
+ global white
+ global cppcomment
+
+ line = cppcomment.sub('', line)
+ line = leading.sub('', line)
+ line = trailing.sub('', line)
+ line = white.sub(' ', line)
+
+ return line
+
+def ProcessOneEntry(newstruct, entry):
+ optional = 0
+ array = 0
+ type = ''
+ name = ''
+ tag = ''
+ tag_set = None
+ separator = ''
+ fixed_length = ''
+
+ tokens = entry.split(' ')
+ while tokens:
+ token = tokens[0]
+ tokens = tokens[1:]
+
+ if not type:
+ if not optional and token == 'optional':
+ optional = 1
+ continue
+
+ if not array and token == 'array':
+ array = 1
+ continue
+
+ if not type:
+ type = token
+ continue
+
+ if not name:
+ res = re.match(r'^([^\[\]]+)(\[.*\])?$', token)
+ if not res:
+ print >>sys.stderr, 'Cannot parse name: \"%s\" around %d' % (
+ entry, line_count)
+ sys.exit(1)
+ name = res.group(1)
+ fixed_length = res.group(2)
+ if fixed_length:
+ fixed_length = fixed_length[1:-1]
+ continue
+
+ if not separator:
+ separator = token
+ if separator != '=':
+ print >>sys.stderr, 'Expected "=" after name \"%s\" got %s' % (
+ name, token)
+ sys.exit(1)
+ continue
+
+ if not tag_set:
+ tag_set = 1
+ if not re.match(r'^[0-9]+$', token):
+ print >>sys.stderr, 'Expected tag number: \"%s\"' % entry
+ sys.exit(1)
+ tag = int(token)
+ continue
+
+ print >>sys.stderr, 'Cannot parse \"%s\"' % entry
+ sys.exit(1)
+
+ if not tag_set:
+ print >>sys.stderr, 'Need tag number: \"%s\"' % entry
+ sys.exit(1)
+
+ # Create the right entry
+ if type == 'bytes':
+ if fixed_length:
+ newentry = EntryBytes(type, name, tag, fixed_length)
+ else:
+ newentry = EntryVarBytes(type, name, tag)
+ elif type == 'int' and not fixed_length:
+ newentry = EntryInt(type, name, tag)
+ elif type == 'string' and not fixed_length:
+ newentry = EntryString(type, name, tag)
+ else:
+ res = re.match(r'^struct\[(%s)\]$' % _STRUCT_RE, type, re.IGNORECASE)
+ if res:
+ # References another struct defined in our file
+ newentry = EntryStruct(type, name, tag, res.group(1))
+ else:
+ print >>sys.stderr, 'Bad type: "%s" in "%s"' % (type, entry)
+ sys.exit(1)
+
+ structs = []
+
+ if optional:
+ newentry.MakeOptional()
+ if array:
+ newentry.MakeArray()
+
+ newentry.SetStruct(newstruct)
+ newentry.SetLineCount(line_count)
+ newentry.Verify()
+
+ if array:
+ # We need to encapsulate this entry into a struct
+ newname = newentry.Name()+ '_array'
+
+ # Now borgify the new entry.
+ newentry = EntryArray(newentry)
+ newentry.SetStruct(newstruct)
+ newentry.SetLineCount(line_count)
+ newentry.MakeArray()
+
+ newstruct.AddEntry(newentry)
+
+ return structs
+
+def ProcessStruct(data):
+ tokens = data.split(' ')
+
+ # First three tokens are: 'struct' 'name' '{'
+ newstruct = Struct(tokens[1])
+
+ inside = ' '.join(tokens[3:-1])
+
+ tokens = inside.split(';')
+
+ structs = []
+
+ for entry in tokens:
+ entry = NormalizeLine(entry)
+ if not entry:
+ continue
+
+ # It's possible that new structs get defined in here
+ structs.extend(ProcessOneEntry(newstruct, entry))
+
+ structs.append(newstruct)
+ return structs
+
+def GetNextStruct(file):
+ global line_count
+ global cppdirect
+
+ got_struct = 0
+
+ processed_lines = []
+
+ have_c_comment = 0
+ data = ''
+ for line in file:
+ line_count += 1
+ line = line[:-1]
+
+ if not have_c_comment and re.search(r'/\*', line):
+ if re.search(r'/\*.*\*/', line):
+ line = re.sub(r'/\*.*\*/', '', line)
+ else:
+ line = re.sub(r'/\*.*$', '', line)
+ have_c_comment = 1
+
+ if have_c_comment:
+ if not re.search(r'\*/', line):
+ continue
+ have_c_comment = 0
+ line = re.sub(r'^.*\*/', '', line)
+
+ line = NormalizeLine(line)
+
+ if not line:
+ continue
+
+ if not got_struct:
+ if re.match(r'#include ["<].*[>"]', line):
+ cppdirect.append(line)
+ continue
+
+ if re.match(r'^#(if( |def)|endif)', line):
+ cppdirect.append(line)
+ continue
+
+ if not re.match(r'^struct %s {$' % _STRUCT_RE,
+ line, re.IGNORECASE):
+ print >>sys.stderr, 'Missing struct on line %d: %s' % (
+ line_count, line)
+ sys.exit(1)
+ else:
+ got_struct = 1
+ data += line
+ continue
+
+ # We are inside the struct
+ tokens = line.split('}')
+ if len(tokens) == 1:
+ data += ' ' + line
+ continue
+
+ if len(tokens[1]):
+ print >>sys.stderr, 'Trailing garbage after struct on line %d' % (
+ line_count )
+ sys.exit(1)
+
+ # We found the end of the struct
+ data += ' %s}' % tokens[0]
+ break
+
+ # Remove any comments, that might be in there
+ data = re.sub(r'/\*.*\*/', '', data)
+
+ return data
+
+
+def Parse(file):
+ """Parses the input file and returns C code and corresponding header
+ file."""
+
+ entities = []
+
+ while 1:
+ # Just gets the whole struct nicely formatted
+ data = GetNextStruct(file)
+
+ if not data:
+ break
+
+ entities.extend(ProcessStruct(data))
+
+ return entities
+
+def GuardName(name):
+ name = '_'.join(name.split('.'))
+ name = '_'.join(name.split('/'))
+ guard = '_'+name.upper()+'_'
+
+ return guard
+
+def HeaderPreamble(name):
+ guard = GuardName(name)
+ pre = (
+ '/*\n'
+ ' * Automatically generated from %s\n'
+ ' */\n\n'
+ '#ifndef %s\n'
+ '#define %s\n\n' ) % (
+ name, guard, guard)
+ pre += (
+ '#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1)\n'
+ '#define EVTAG_ASSIGN(msg, member, args...) '
+ '(*(msg)->member##_assign)(msg, ## args)\n'
+ '#define EVTAG_GET(msg, member, args...) '
+ '(*(msg)->member##_get)(msg, ## args)\n'
+ '#define EVTAG_ADD(msg, member) (*(msg)->member##_add)(msg)\n'
+ '#define EVTAG_LEN(msg, member) ((msg)->member##_length)\n'
+ )
+
+ return pre
+
+
+def HeaderPostamble(name):
+ guard = GuardName(name)
+ return '#endif /* %s */' % guard
+
+def BodyPreamble(name):
+ global _NAME
+ global _VERSION
+
+ header_file = '.'.join(name.split('.')[:-1]) + '.gen.h'
+
+ pre = ( '/*\n'
+ ' * Automatically generated from %s\n'
+ ' * by %s/%s. DO NOT EDIT THIS FILE.\n'
+ ' */\n\n' ) % (name, _NAME, _VERSION)
+ pre += ( '#include <sys/types.h>\n'
+ '#include <sys/time.h>\n'
+ '#include <stdlib.h>\n'
+ '#include <string.h>\n'
+ '#include <assert.h>\n'
+ '#include <event.h>\n\n' )
+
+ for include in cppdirect:
+ pre += '%s\n' % include
+
+ pre += '\n#include "%s"\n\n' % header_file
+
+ pre += 'void event_err(int eval, const char *fmt, ...);\n'
+ pre += 'void event_warn(const char *fmt, ...);\n'
+ pre += 'void event_errx(int eval, const char *fmt, ...);\n'
+ pre += 'void event_warnx(const char *fmt, ...);\n\n'
+
+ return pre
+
+def main(argv):
+ filename = argv[1]
+
+ if filename.split('.')[-1] != 'rpc':
+ ext = filename.split('.')[-1]
+ print >>sys.stderr, 'Unrecognized file extension: %s' % ext
+ sys.exit(1)
+
+ print >>sys.stderr, 'Reading \"%s\"' % filename
+
+ fp = open(filename, 'r')
+ entities = Parse(fp)
+ fp.close()
+
+ header_file = '.'.join(filename.split('.')[:-1]) + '.gen.h'
+ impl_file = '.'.join(filename.split('.')[:-1]) + '.gen.c'
+
+ print >>sys.stderr, '... creating "%s"' % header_file
+ header_fp = open(header_file, 'w')
+ print >>header_fp, HeaderPreamble(filename)
+
+ # Create forward declarations: allows other structs to reference
+ # each other
+ for entry in entities:
+ entry.PrintForwardDeclaration(header_fp)
+ print >>header_fp, ''
+
+ for entry in entities:
+ entry.PrintTags(header_fp)
+ entry.PrintDeclaration(header_fp)
+ print >>header_fp, HeaderPostamble(filename)
+ header_fp.close()
+
+ print >>sys.stderr, '... creating "%s"' % impl_file
+ impl_fp = open(impl_file, 'w')
+ print >>impl_fp, BodyPreamble(filename)
+ for entry in entities:
+ entry.PrintCode(impl_fp)
+ impl_fp.close()
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/event_tagging.c b/event_tagging.c
new file mode 100644
index 00000000..b60d722e
--- /dev/null
+++ b/event_tagging.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2003, 2004 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. 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 <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "event.h"
+#include "log.h"
+
+int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf);
+
+static struct evbuffer *_buf;
+
+void
+evtag_init()
+{
+ if ((_buf = evbuffer_new()) == NULL)
+ event_err(1, "%s: malloc", __func__);
+}
+
+/*
+ * We encode integer's by nibbles; the first nibble contains the number
+ * of significant nibbles - 1; this allows us to encode up to 64-bit
+ * integers. This function is byte-order independent.
+ */
+
+void
+encode_int(struct evbuffer *evbuf, u_int32_t number)
+{
+ int off = 1, nibbles = 0;
+ u_int8_t data[5];
+
+ memset(data, 0, sizeof(data));
+ while (number) {
+ if (off & 0x1)
+ data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f);
+ else
+ data[off/2] = (data[off/2] & 0x0f) |
+ ((number & 0x0f) << 4);
+ number >>= 4;
+ off++;
+ }
+
+ if (off > 2)
+ nibbles = off - 2;
+
+ /* Off - 1 is the number of encoded nibbles */
+ data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4);
+
+ evbuffer_add(evbuf, data, (off + 1) / 2);
+}
+
+/*
+ * Marshal a data type, the general format is as follows:
+ *
+ * tag number: one byte; length: var bytes; payload: var bytes
+ */
+
+void
+evtag_marshal(struct evbuffer *evbuf, u_int8_t tag,
+ const void *data, u_int16_t len)
+{
+ evbuffer_add(evbuf, &tag, sizeof(tag));
+ encode_int(evbuf, len);
+ evbuffer_add(evbuf, (void *)data, len);
+}
+
+/* Marshaling for integers */
+void
+evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag, u_int32_t integer)
+{
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ encode_int(_buf, integer);
+
+ evbuffer_add(evbuf, &tag, sizeof(tag));
+ encode_int(evbuf, EVBUFFER_LENGTH(_buf));
+ evbuffer_add_buffer(evbuf, _buf);
+}
+
+void
+evtag_marshal_string(struct evbuffer *buf, u_int8_t tag, const char *string)
+{
+ evtag_marshal(buf, tag, string, strlen(string));
+}
+
+void
+evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag, struct timeval *tv)
+{
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+ encode_int(_buf, tv->tv_sec);
+ encode_int(_buf, tv->tv_usec);
+
+ evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf),
+ EVBUFFER_LENGTH(_buf));
+}
+
+static int __inline
+decode_int_internal(u_int32_t *pnumber, struct evbuffer *evbuf, int dodrain)
+{
+ u_int32_t number = 0;
+ u_int8_t *data = EVBUFFER_DATA(evbuf);
+ int len = EVBUFFER_LENGTH(evbuf);
+ int nibbles = 0, off;
+
+ if (!len)
+ return (-1);
+
+ nibbles = ((data[0] & 0xf0) >> 4) + 1;
+ if (nibbles > 8 || (nibbles >> 1) > len - 1)
+ return (-1);
+
+ off = nibbles;
+ while (off > 0) {
+ number <<= 4;
+ if (off & 0x1)
+ number |= data[off >> 1] & 0x0f;
+ else
+ number |= (data[off >> 1] & 0xf0) >> 4;
+ off--;
+ }
+
+ len = (nibbles >> 1) + 1;
+ if (dodrain)
+ evbuffer_drain(evbuf, len);
+
+ *pnumber = number;
+
+ return (len);
+}
+
+int
+decode_int(u_int32_t *pnumber, struct evbuffer *evbuf)
+{
+ return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
+}
+
+int
+evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag)
+{
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+ *ptag = EVBUFFER_DATA(evbuf)[0];
+
+ return (0);
+}
+
+int
+evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength)
+{
+ struct evbuffer tmp;
+ int res;
+
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += 1;
+ tmp.off -= 1;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ *plength += res + 1;
+
+ return (0);
+}
+
+int
+evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength)
+{
+ struct evbuffer tmp;
+ int res;
+
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += 1;
+ tmp.off -= 1;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+evtag_consume(struct evbuffer *evbuf)
+{
+ u_int32_t len;
+ evbuffer_drain(evbuf, 1);
+ if (decode_int(&len, evbuf) == -1)
+ return (-1);
+ evbuffer_drain(evbuf, len);
+
+ return (0);
+}
+
+/* Reads the data type from an event buffer */
+
+int
+evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag, struct evbuffer *dst)
+{
+ u_int8_t tag;
+ u_int16_t len;
+ u_int32_t integer;
+
+ if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag))
+ return (-1);
+ if (decode_int(&integer, src) == -1)
+ return (-1);
+ len = integer;
+
+ if (EVBUFFER_LENGTH(src) < len)
+ return (-1);
+
+ if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1)
+ return (-1);
+
+ evbuffer_drain(src, len);
+
+ *ptag = tag;
+ return (len);
+}
+
+/* Marshaling for integers */
+
+int
+evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
+ u_int32_t *pinteger)
+{
+ u_int8_t tag;
+ u_int16_t len;
+ u_int32_t integer;
+
+ if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) ||
+ tag != need_tag)
+ return (-1);
+ if (decode_int(&integer, evbuf) == -1)
+ return (-1);
+ len = integer;
+
+ if (EVBUFFER_LENGTH(evbuf) < len)
+ return (-1);
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1)
+ return (-1);
+
+ evbuffer_drain(evbuf, len);
+
+ return (decode_int(pinteger, _buf));
+}
+
+/* Unmarshal a fixed length tag */
+
+int
+evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
+ size_t len)
+{
+ u_int8_t tag;
+
+ /* Initialize this event buffer so that we can read into it */
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+ /* Now unmarshal a tag and check that it matches the tag we want */
+ if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ if (EVBUFFER_LENGTH(_buf) != len)
+ return (-1);
+
+ memcpy(data, EVBUFFER_DATA(_buf), len);
+ return (0);
+}
+
+int
+evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
+ char **pstring)
+{
+ u_int8_t tag;
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+ if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ *pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1);
+ if (*pstring == NULL)
+ event_err(1, "%s: calloc", __func__);
+ evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf));
+
+ return (0);
+}
+
+int
+evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
+ struct timeval *ptv)
+{
+ u_int8_t tag;
+ u_int32_t integer;
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ if (decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_sec = integer;
+ if (decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_usec = integer;
+
+ return (0);
+}
diff --git a/evhttp.h b/evhttp.h
new file mode 100644
index 00000000..cb3b2cf4
--- /dev/null
+++ b/evhttp.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2000-2004 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. 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 _EVHTTP_H_
+#define _EVHTTP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+typedef unsigned char u_char;
+#endif
+
+/*
+ * Basic support for HTTP serving.
+ *
+ * As libevent is a library for dealing with event notification and most
+ * interesting applications are networked today, I have often found the
+ * need to write HTTP code. The following prototypes and definitions provide
+ * an application with a minimal interface for making HTTP requests and for
+ * creating a very simple HTTP server.
+ */
+
+/* Response codes */
+#define HTTP_OK 200
+#define HTTP_MOVEPERM 301
+#define HTTP_MOVETEMP 302
+#define HTTP_NOTFOUND 404
+
+struct evhttp;
+struct evhttp_request;
+struct evkeyvalq;
+
+/* Start an HTTP server on the specified address and port */
+struct evhttp *evhttp_start(const char *address, u_short port);
+
+/*
+ * Free the previously create HTTP server. Works only if no requests are
+ * currently being served.
+ */
+void evhttp_free(struct evhttp* http);
+
+/* Set a callback for a specified URI */
+void evhttp_set_cb(struct evhttp *, const char *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+/* Set a callback for all requests that are not caught by specific callbacks */
+void evhttp_set_gencb(struct evhttp *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+void evhttp_send_error(struct evhttp_request *, int, const char *);
+void evhttp_send_reply(struct evhttp_request *, int, const char *,
+ struct evbuffer *);
+
+/* Interfaces for making requests */
+enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
+
+/*
+ * Creates a new request object that needs to be filled in with the request
+ * parameters. The callback is executed when the request completed or an
+ * error occurred.
+ */
+struct evhttp_request *evhttp_request_new(
+ void (*cb)(struct evhttp_request *, void *), void *arg);
+
+/* Frees the request object and removes associated events. */
+void evhttp_request_free(struct evhttp_request *req);
+
+/*
+ * A connection object that can be used to for making HTTP requests. The
+ * connection object tries to establish the connection when it is given an
+ * http request object.
+ */
+struct evhttp_connection *evhttp_connection_new(
+ const char *address, unsigned short port);
+
+/* Frees an http connection */
+void evhttp_connection_free(struct evhttp_connection *evcon);
+
+/* The connection gets ownership of the request */
+int evhttp_make_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req,
+ enum evhttp_cmd_type type, const char *uri);
+
+const char *evhttp_request_uri(struct evhttp_request *req);
+
+/* Interfaces for dealing with HTTP headers */
+
+const char *evhttp_find_header(struct evkeyvalq *, const char *);
+int evhttp_remove_header(struct evkeyvalq *, const char *);
+int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
+void evhttp_clear_headers(struct evkeyvalq *);
+
+/* Miscellaneous utility functions */
+void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+char *evhttp_htmlescape(const char *html);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVHTTP_H_ */
diff --git a/evport.c b/evport.c
new file mode 100644
index 00000000..8c6dc286
--- /dev/null
+++ b/evport.c
@@ -0,0 +1,509 @@
+/*
+ * Submitted by David Pacheco (dp.spambait@gmail.com)
+ *
+ * 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 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.
+ */
+
+/*
+ * Copyright (c) 2006 Sun Microsystems. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * evport.c: event backend using Solaris 10 event ports. See port_create(3C).
+ * This implementation is loosely modeled after the one used for select(2) (in
+ * select.c).
+ *
+ * The outstanding events are tracked in a data structure called evport_data.
+ * Each entry in the ed_fds array corresponds to a file descriptor, and contains
+ * pointers to the read and write events that correspond to that fd. (That is,
+ * when the file is readable, the "read" event should handle it, etc.)
+ *
+ * evport_add and evport_del update this data structure. evport_dispatch uses it
+ * to determine where to callback when an event occurs (which it gets from
+ * port_getn).
+ *
+ * Helper functions are used: grow() grows the file descriptor array as
+ * necessary when large fd's come in. reassociate() takes care of maintaining
+ * the proper file-descriptor/event-port associations.
+ *
+ * As in the select(2) implementation, signals are handled by evsignal, and
+ * evport_recalc does almost nothing.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <assert.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <errno.h>
+#include <poll.h>
+#include <port.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+#include "evsignal.h"
+
+extern volatile sig_atomic_t evsignal_caught;
+
+
+/*
+ * Default value for ed_nevents, which is the maximum file descriptor number we
+ * can handle. If an event comes in for a file descriptor F > nevents, we will
+ * grow the array of file descriptors, doubling its size.
+ */
+#define DEFAULT_NFDS 16
+
+
+/*
+ * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
+ * any particular call. You can speed things up by increasing this, but it will
+ * (obviously) require more memory.
+ */
+#define EVENTS_PER_GETN 8
+
+/*
+ * Per-file-descriptor information about what events we're subscribed to. These
+ * fields are NULL if no event is subscribed to either of them.
+ */
+
+struct fd_info {
+ struct event* fdi_revt; /* the event responsible for the "read" */
+ struct event* fdi_wevt; /* the event responsible for the "write" */
+};
+
+#define FDI_HAS_READ(fdi) ((fdi)->fdi_revt != NULL)
+#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL)
+#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
+#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
+ (FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
+
+struct evport_data {
+ int ed_port; /* event port for system events */
+ sigset_t ed_sigmask; /* for evsignal */
+ int ed_nevents; /* number of allocated fdi's */
+ struct fd_info *ed_fds; /* allocated fdi table */
+ /* fdi's that we need to reassoc */
+ struct fd_info *ed_pending[EVENTS_PER_GETN];
+};
+
+static void* evport_init (void);
+static int evport_add (void *, struct event *);
+static int evport_del (void *, struct event *);
+static int evport_recalc (struct event_base *, void *, int);
+static int evport_dispatch (struct event_base *, void *, struct timeval *);
+
+const struct eventop evportops = {
+ "event ports",
+ evport_init,
+ evport_add,
+ evport_del,
+ evport_recalc,
+ evport_dispatch
+};
+
+/*
+ * Initialize the event port implementation.
+ */
+
+static void*
+evport_init(void)
+{
+ struct evport_data *evpd;
+ /*
+ * Disable event ports when this environment variable is set
+ */
+ if (getenv("EVENT_NOEVPORT"))
+ return (NULL);
+
+ if (!(evpd = calloc(1, sizeof(struct evport_data))))
+ return (NULL);
+
+ if ((evpd->ed_port = port_create()) == -1) {
+ free(evpd);
+ return (NULL);
+ }
+
+ /*
+ * Initialize file descriptor structure
+ */
+ evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info));
+ if (evpd->ed_fds == NULL) {
+ close(evpd->ed_port);
+ free(evpd);
+ return (NULL);
+ }
+ evpd->ed_nevents = DEFAULT_NFDS;
+ memset(&evpd->ed_pending, 0, EVENTS_PER_GETN * sizeof(struct fd_info*));
+
+ evsignal_init(&evpd->ed_sigmask);
+
+ return (evpd);
+}
+
+#ifdef CHECK_INVARIANTS
+/*
+ * Checks some basic properties about the evport_data structure. Because it
+ * checks all file descriptors, this function can be expensive when the maximum
+ * file descriptor ever used is rather large.
+ */
+
+static void
+check_evportop(struct evport_data *evpd)
+{
+ assert(evpd);
+ assert(evpd->ed_nevents > 0);
+ assert(evpd->ed_port > 0);
+ assert(evpd->ed_fds > 0);
+
+ /*
+ * Verify the integrity of the fd_info struct as well as the events to
+ * which it points (at least, that they're valid references and correct
+ * for their position in the structure).
+ */
+ int i;
+ for (i = 0; i < evpd->ed_nevents; ++i) {
+ struct event *ev;
+ struct fd_info *fdi;
+
+ fdi = &evpd->ed_fds[i];
+ if ((ev = fdi->fdi_revt) != NULL) {
+ assert(ev->ev_fd == i);
+ }
+ if ((ev = fdi->fdi_wevt) != NULL) {
+ assert(ev->ev_fd == i);
+ }
+ }
+}
+
+/*
+ * Verifies very basic integrity of a given port_event.
+ */
+static void
+check_event(port_event_t* pevt)
+{
+ /*
+ * We've only registered for PORT_SOURCE_FD events. The only
+ * other thing we can legitimately receive is PORT_SOURCE_ALERT,
+ * but since we're not using port_alert either, we can assume
+ * PORT_SOURCE_FD.
+ */
+ assert(pevt->portev_source == PORT_SOURCE_FD);
+ assert(pevt->portev_user == NULL);
+}
+
+#else
+#define check_evportop(epop)
+#define check_event(pevt)
+#endif /* CHECK_INVARIANTS */
+
+/*
+ * Doubles the size of the allocated file descriptor array.
+ */
+static int
+grow(struct evport_data *epdp, int factor)
+{
+ struct fd_info *tmp;
+ int oldsize = epdp->ed_nevents;
+ int newsize = factor * oldsize;
+ assert(factor > 1);
+
+ check_evportop(epdp);
+
+ tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
+ if (NULL == tmp)
+ return -1;
+ epdp->ed_fds = tmp;
+ memset((char*) (epdp->ed_fds + oldsize), 0,
+ (newsize - oldsize)*sizeof(struct fd_info));
+ epdp->ed_nevents = newsize;
+
+ check_evportop(epdp);
+
+ return 0;
+}
+
+
+/*
+ * (Re)associates the given file descriptor with the event port. The OS events
+ * are specified (implicitly) from the fd_info struct.
+ */
+static int
+reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
+{
+ int sysevents = FDI_TO_SYSEVENTS(fdip);
+
+ if (sysevents != 0) {
+ if ((-1 == port_associate(epdp->ed_port, PORT_SOURCE_FD,
+ fd, sysevents, NULL))) {
+ perror("port_associate");
+ return (-1);
+ }
+ } else {
+ if (-1 == port_dissociate(epdp->ed_port, PORT_SOURCE_FD, fd)) {
+ perror("port_dissociate");
+ return (-1);
+ }
+ }
+
+ check_evportop(epdp);
+
+ return (0);
+}
+
+/*
+ * Main event loop - polls port_getn for some number of events, and processes
+ * them.
+ */
+
+static int
+evport_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ int i, res;
+ struct evport_data *epdp = arg;
+ port_event_t pevtlist[EVENTS_PER_GETN];
+
+ /*
+ * port_getn will block until it has at least nevents events. It will
+ * also return how many it's given us (which may be more than we asked
+ * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
+ * nevents.
+ */
+ int nevents = 1;
+
+ /*
+ * We have to convert a struct timeval to a struct timespec
+ * (only difference is nanoseconds vs. microseconds)
+ */
+ struct timespec ts = {tv->tv_sec, tv->tv_usec * 1000};
+
+ /*
+ * Before doing anything else, we need to reassociate the events we hit
+ * last time which need reassociation. See comment at the end of the
+ * loop below.
+ */
+ for (i = 0; i < EVENTS_PER_GETN; ++i) {
+ struct fd_info *fdi = epdp->ed_pending[i];
+
+ if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
+ int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd :
+ fdi->fdi_wevt->ev_fd;
+ reassociate(epdp, fdi, fd);
+ epdp->ed_pending[i] = NULL;
+ }
+ }
+
+
+
+ if (evsignal_deliver(&epdp->ed_sigmask) == -1)
+ return (-1);
+
+ if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
+ &nevents, &ts)) == -1) {
+ if (errno == EINTR) {
+ evsignal_process();
+ return (0);
+ } else if (errno == ETIME) {
+ if (nevents == 0)
+ return (0);
+ } else {
+ perror("port_getn");
+ return (-1);
+ }
+ } else if (evsignal_caught) {
+ evsignal_process();
+ }
+
+ event_debug(("%s: port_getn reports %d events", __func__, nevents));
+
+ for (i = 0; i < nevents; ++i) {
+ struct event *ev;
+ struct fd_info *fdi;
+ port_event_t *pevt = &pevtlist[i];
+ int fd = (int) pevt->portev_object;
+
+ check_evportop(epdp);
+ check_event(pevt);
+
+ /*
+ * Figure out what kind of event it was
+ * (because we have to pass this to the callback)
+ */
+ res = 0;
+ if (pevt->portev_events & POLLIN)
+ res |= EV_READ;
+ if (pevt->portev_events & POLLOUT)
+ res |= EV_WRITE;
+
+ assert(epdp->ed_nevents > fd);
+ fdi = &(epdp->ed_fds[fd]);
+
+ /*
+ * We now check for each of the possible events (READ or WRITE).
+ * If the event is not persistent, then we delete it. Then, we
+ * activate the event (which will cause its callback to be
+ * executed).
+ */
+
+ if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) {
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, res, 1);
+ }
+
+ if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) {
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, res, 1);
+ }
+
+ /*
+ * If there are still events (they haven't been deleted), then
+ * we must reassociate the port, since the event port interface
+ * dissociates them automatically.
+ *
+ * But we can't do it right away, because the event hasn't
+ * handled this event yet, so of course there's still data
+ * waiting!
+ */
+ if(FDI_HAS_EVENTS(fdi)) {
+ epdp->ed_pending[i] = fdi;
+ }
+ } /* end of all events gotten */
+
+ check_evportop(epdp);
+
+ if (evsignal_recalc(&epdp->ed_sigmask) == -1)
+ return (-1);
+
+ return (0);
+}
+
+
+/*
+ * Copied from the version in select.c
+ */
+
+static int
+evport_recalc(struct event_base *base, void *arg, int max)
+{
+ struct evport_data *evpd = arg;
+ check_evportop(evpd);
+ return (evsignal_recalc(&evpd->ed_sigmask));
+}
+
+
+/*
+ * Adds the given event (so that you will be notified when it happens via
+ * the callback function).
+ */
+
+static int
+evport_add(void *arg, struct event *ev)
+{
+ struct evport_data *evpd = arg;
+ struct fd_info *fdi;
+ int factor;
+
+ check_evportop(evpd);
+
+ /*
+ * Delegate, if it's not ours to handle.
+ */
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(&evpd->ed_sigmask, ev));
+
+ /*
+ * If necessary, grow the file descriptor info table
+ */
+
+ factor = 1;
+ while (ev->ev_fd >= factor * evpd->ed_nevents)
+ factor *= 2;
+
+ if (factor > 1) {
+ if (-1 == grow(evpd, factor)) {
+ return (-1);
+ }
+ }
+
+ fdi = &evpd->ed_fds[ev->ev_fd];
+ if (ev->ev_events & EV_READ)
+ fdi->fdi_revt = ev;
+ if (ev->ev_events & EV_WRITE)
+ fdi->fdi_wevt = ev;
+
+ return reassociate(evpd, fdi, ev->ev_fd);
+}
+
+/*
+ * Removes the given event from the list of events to wait for.
+ */
+
+static int
+evport_del(void *arg, struct event *ev)
+{
+ struct evport_data *evpd = arg;
+ struct fd_info *fdi;
+
+ check_evportop(evpd);
+
+ /*
+ * Delegate, if it's not ours to handle
+ */
+ if (ev->ev_events & EV_SIGNAL) {
+ return (evsignal_del(&evpd->ed_sigmask, ev));
+ }
+
+ if (evpd->ed_nevents < ev->ev_fd) {
+ return (-1);
+ }
+
+
+ fdi = &evpd->ed_fds[ev->ev_fd];
+ if (ev->ev_events & EV_READ)
+ fdi->fdi_revt = NULL;
+ if (ev->ev_events & EV_WRITE)
+ fdi->fdi_wevt = NULL;
+
+ return reassociate(evpd, fdi, ev->ev_fd);
+}
+
+
diff --git a/evsignal.h b/evsignal.h
new file mode 100644
index 00000000..8de27a5c
--- /dev/null
+++ b/evsignal.h
@@ -0,0 +1,37 @@
+/*
+ * 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. 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 _EVSIGNAL_H_
+#define _EVSIGNAL_H_
+
+void evsignal_init(sigset_t *);
+void evsignal_process(void);
+int evsignal_recalc(sigset_t *);
+int evsignal_deliver(sigset_t *);
+int evsignal_add(sigset_t *, struct event *);
+int evsignal_del(sigset_t *, struct event *);
+
+#endif /* _EVSIGNAL_H_ */
diff --git a/http-internal.h b/http-internal.h
new file mode 100644
index 00000000..f5ad2861
--- /dev/null
+++ b/http-internal.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2001 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * This header file contains definitions for dealing with HTTP requests
+ * that are internal to libevent. As user of the library, you should not
+ * need to know about these.
+ */
+
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+#define HTTP_CONNECT_TIMEOUT 45
+#define HTTP_WRITE_TIMEOUT 50
+#define HTTP_READ_TIMEOUT 50
+
+#define HTTP_PREFIX "http://"
+#define HTTP_DEFAULTPORT 80
+
+struct evbuffer;
+struct addrinfo;
+struct evhttp_request;
+
+/* A stupid connection object - maybe make this a bufferevent later */
+
+enum evhttp_connection_state {
+ EVCON_DISCONNECTED, /* not currently connected not trying either */
+ EVCON_CONNECTING, /* tries to currently connect */
+ EVCON_CONNECTED /* connection is established */
+};
+
+struct evhttp_connection {
+ int fd;
+ struct event ev;
+ struct evbuffer *input_buffer;
+ struct evbuffer *output_buffer;
+
+ char *address;
+ u_short port;
+
+ int flags;
+#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
+#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
+
+ enum evhttp_connection_state state;
+
+ TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
+
+ void (*cb)(struct evhttp_connection *, void *);
+ void *cb_arg;
+};
+
+enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
+
+struct evhttp_request {
+ TAILQ_ENTRY(evhttp_request) next;
+
+ /* the connection object that this request belongs to */
+ struct evhttp_connection *evcon;
+ int flags;
+#define EVHTTP_REQ_OWN_CONNECTION 0x0001
+
+ struct evkeyvalq *input_headers;
+ struct evkeyvalq *output_headers;
+
+ /* xxx: do we still need these? */
+ char *remote_host;
+ u_short remote_port;
+
+ enum evhttp_request_kind kind;
+ enum evhttp_cmd_type type;
+
+ char *uri; /* uri after HTTP request was parsed */
+
+ char major; /* HTTP Major number */
+ char minor; /* HTTP Minor number */
+
+ int got_firstline;
+ int response_code; /* HTTP Response code */
+ char *response_code_line; /* Readable response */
+
+ struct evbuffer *input_buffer; /* read data */
+ int ntoread;
+
+ struct evbuffer *output_buffer; /* outgoing post or data */
+
+ /* Callback */
+ void (*cb)(struct evhttp_request *, void *);
+ void *cb_arg;
+};
+
+struct evhttp_cb {
+ TAILQ_ENTRY(evhttp_cb) next;
+
+ char *what;
+
+ void (*cb)(struct evhttp_request *req, void *);
+ void *cbarg;
+};
+
+struct evhttp {
+ struct event bind_ev;
+
+ TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
+
+ void (*gencb)(struct evhttp_request *req, void *);
+ void *gencbarg;
+};
+
+/* resets the connection; can be reused for more requests */
+void evhttp_connection_reset(struct evhttp_connection *);
+
+/* connects if necessary */
+int evhttp_connection_connect(struct evhttp_connection *);
+
+/* notifies the current request that it failed; resets connection */
+void evhttp_connection_fail(struct evhttp_connection *);
+
+void evhttp_get_request(int, struct sockaddr *, socklen_t,
+ void (*)(struct evhttp_request *, void *), void *);
+
+int evhttp_hostportfile(char *, char **, u_short *, char **);
+
+int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*);
+
+void evhttp_start_read(struct evhttp_connection *);
+void evhttp_read_header(int, short, void *);
+void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
+
+void evhttp_write_buffer(struct evhttp_connection *,
+ void (*)(struct evhttp_connection *, void *), void *);
+
+/* response sending HTML the data in the buffer */
+void evhttp_response_code(struct evhttp_request *, int, const char *);
+void evhttp_send_page(struct evhttp_request *, struct evbuffer *);
+
+#endif /* _HTTP_H */
diff --git a/http.c b/http.c
new file mode 100644
index 00000000..42d609b4
--- /dev/null
+++ b/http.c
@@ -0,0 +1,1632 @@
+/*
+ * Copyright (c) 2002-2006 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. 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 <sys/param.h>
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/tree.h>
+#include <sys/wait.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <unistd.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+#undef timeout_pending
+#undef timeout_initialized
+
+#include "event.h"
+#include "evhttp.h"
+#include "log.h"
+#include "http-internal.h"
+
+extern int debug;
+
+static int make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t),
+ struct addrinfo *);
+static int make_socket(int (*)(int, const struct sockaddr *, socklen_t),
+ const char *, short);
+static void name_from_addr(struct sockaddr *, socklen_t, char **, char **);
+
+void evhttp_write(int, short, void *);
+
+static const char *
+html_replace(char ch)
+{
+ static char buf[2];
+
+ switch (ch) {
+ case '<':
+ return "&lt;";
+ case '>':
+ return "&gt;";
+ case '"':
+ return "&quot;";
+ case '\'':
+ return "&#039;";
+ case '&':
+ return "&amp;";
+ default:
+ break;
+ }
+
+ /* Echo the character back */
+ buf[0] = ch;
+ buf[1] = '\0';
+
+ return buf;
+}
+
+/*
+ * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
+ * &#039; and &amp; correspondingly.
+ *
+ * The returned string needs to be freed by the caller.
+ */
+
+char *
+evhttp_htmlescape(const char *html)
+{
+ int i, new_size = 0;
+ char *escaped_html, *p;
+
+ for (i = 0; i < strlen(html); ++i)
+ new_size += strlen(html_replace(html[i]));
+
+ p = escaped_html = malloc(new_size + 1);
+ if (escaped_html == NULL)
+ event_err(1, "%s: malloc(%d)", __func__, new_size + 1);
+ for (i = 0; i < strlen(html); ++i) {
+ const char *replaced = html_replace(html[i]);
+ /* this is length checked */
+ strcpy(p, replaced);
+ p += strlen(replaced);
+ }
+
+ *p = '\0';
+
+ return (escaped_html);
+}
+
+const char *
+evhttp_method(enum evhttp_cmd_type type)
+{
+ const char *method;
+
+ switch (type) {
+ case EVHTTP_REQ_GET:
+ method = "GET";
+ break;
+ case EVHTTP_REQ_POST:
+ method = "POST";
+ break;
+ case EVHTTP_REQ_HEAD:
+ method = "HEAD";
+ break;
+ default:
+ method = NULL;
+ break;
+ }
+
+ return (method);
+}
+
+void
+evhttp_write_buffer(struct evhttp_connection *evcon,
+ void (*cb)(struct evhttp_connection *, void *), void *arg)
+{
+ struct timeval tv;
+
+ event_debug(("%s: preparing to write buffer\n", __func__));
+
+ /* Set call back */
+ evcon->cb = cb;
+ evcon->cb_arg = arg;
+
+ /* xxx: maybe check if the event is still pending? */
+ event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon);
+ timerclear(&tv);
+ tv.tv_sec = HTTP_WRITE_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+}
+
+/*
+ * Create the headers need for an HTTP reply
+ */
+static void
+evhttp_make_header_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ static char line[1024];
+ const char *method;
+
+ evhttp_remove_header(req->output_headers, "Accept-Encoding");
+ evhttp_remove_header(req->output_headers, "Proxy-Connection");
+ evhttp_remove_header(req->output_headers, "Connection");
+ evhttp_add_header(req->output_headers, "Connection", "close");
+ req->minor = 0;
+
+ /* Generate request line */
+ method = evhttp_method(req->type);
+ snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n",
+ method, req->uri, req->major, req->minor);
+ evbuffer_add(evcon->output_buffer, line, strlen(line));
+
+ /* Add the content length on a post request if missing */
+ if (req->type == EVHTTP_REQ_POST &&
+ evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+ char size[12];
+ snprintf(size, sizeof(size), "%ld",
+ EVBUFFER_LENGTH(req->output_buffer));
+ evhttp_add_header(req->output_headers, "Content-Length", size);
+ }
+}
+
+/*
+ * Create the headers needed for an HTTP reply
+ */
+static void
+evhttp_make_header_response(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ static char line[1024];
+ snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
+ req->major, req->minor, req->response_code,
+ req->response_code_line);
+ evbuffer_add(evcon->output_buffer, line, strlen(line));
+
+ /* Potentially add headers */
+ if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
+ evhttp_add_header(req->output_headers,
+ "Content-Type", "text/html; charset=ISO-8859-1");
+ }
+}
+
+void
+evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+ static char line[1024];
+ struct evkeyval *header;
+
+ /*
+ * Depending if this is a HTTP request or response, we might need to
+ * add some new headers or remove existing headers.
+ */
+ if (req->kind == EVHTTP_REQUEST) {
+ evhttp_make_header_request(evcon, req);
+ } else {
+ evhttp_make_header_response(evcon, req);
+ }
+
+ TAILQ_FOREACH(header, req->output_headers, next) {
+ snprintf(line, sizeof(line), "%s: %s\r\n",
+ header->key, header->value);
+ evbuffer_add(evcon->output_buffer, line, strlen(line));
+ }
+ evbuffer_add(evcon->output_buffer, "\r\n", 2);
+
+ if (EVBUFFER_LENGTH(req->output_buffer) >= 0) {
+ /*
+ * For a request, we add the POST data, for a reply, this
+ * is the regular data.
+ */
+ evbuffer_add_buffer(evcon->output_buffer, req->output_buffer);
+ }
+}
+
+/* Separated host, port and file from URI */
+
+int
+evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile)
+{
+ static char host[1024];
+ static char file[1024];
+ char *p, *p2;
+ int len;
+ u_short port;
+
+ len = strlen(HTTP_PREFIX);
+ if (strncasecmp(url, HTTP_PREFIX, len))
+ return (-1);
+
+ url += len;
+
+ /* We might overrun */
+ if (strlcpy(host, url, sizeof (host)) >= sizeof(host))
+ return (-1);
+
+ p = strchr(host, '/');
+ if (p != NULL) {
+ *p = '\0';
+ p2 = p + 1;
+ } else
+ p2 = NULL;
+
+ if (pfile != NULL) {
+ /* Generate request file */
+ if (p2 == NULL)
+ p2 = "";
+ snprintf(file, sizeof(file), "/%s", p2);
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL) {
+ *p = '\0';
+ port = atoi(p + 1);
+
+ if (port == 0)
+ return (-1);
+ } else
+ port = HTTP_DEFAULTPORT;
+
+ if (phost != NULL)
+ *phost = host;
+ if (pport != NULL)
+ *pport = port;
+ if (pfile != NULL)
+ *pfile = file;
+
+ return (0);
+}
+
+void
+evhttp_connection_fail(struct evhttp_connection *evcon)
+{
+ struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
+ assert(req != NULL);
+
+ /* reset the connection */
+ evhttp_connection_reset(evcon);
+
+ if (req->cb != NULL) {
+ /* xxx: maybe we need to pass the request here? */
+ (*req->cb)(NULL, req->cb_arg);
+ }
+
+ TAILQ_REMOVE(&evcon->requests, req, next);
+ evhttp_request_free(req);
+
+ /* xxx: maybe we should fail all requests??? */
+
+ /* We are trying the next request that was queued on us */
+ if (TAILQ_FIRST(&evcon->requests) != NULL)
+ evhttp_connection_connect(evcon);
+}
+
+void
+evhttp_write(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ struct timeval tv;
+ int n;
+
+ if (what == EV_TIMEOUT) {
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ n = evbuffer_write(evcon->output_buffer, fd);
+ if (n == -1) {
+ event_warn("%s: evbuffer_write", __func__);
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ if (n == 0) {
+ event_warnx("%s: write nothing\n", __func__);
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) {
+ timerclear(&tv);
+ tv.tv_sec = HTTP_WRITE_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+ return;
+ }
+
+ /* Activate our call back */
+ (*evcon->cb)(evcon, evcon->cb_arg);
+}
+
+void
+evhttp_connection_done(struct evhttp_connection *evcon)
+{
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+ /*
+ * if this is an incoming connection, we need to leave the request
+ * on the connection, so that we can reply to it.
+ */
+ if (evcon->flags & EVHTTP_CON_OUTGOING) {
+ TAILQ_REMOVE(&evcon->requests, req, next);
+ req->evcon = NULL;
+
+ if (TAILQ_FIRST(&evcon->requests) != NULL) {
+ /*
+ * We have more requests; reset the connection
+ * and deal with the next request. xxx: no
+ * persistent connection right now
+ */
+ evhttp_connection_connect(evcon);
+ }
+ }
+
+ /* hand what ever we read over to the request */
+ evbuffer_add_buffer(req->input_buffer, evcon->input_buffer);
+
+ /* notify the user of the request */
+ (*req->cb)(req, req->cb_arg);
+
+ /* if this was an outgoing request, we own and it's done. so free it */
+ if (evcon->flags & EVHTTP_CON_OUTGOING) {
+ evhttp_request_free(req);
+ }
+}
+
+/*
+ * Reads data into a buffer structure until no more data
+ * can be read on the file descriptor or we have read all
+ * the data that we wanted to read.
+ * Execute callback when done.
+ */
+
+void
+evhttp_read(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ struct timeval tv;
+ int n;
+
+ if (what == EV_TIMEOUT) {
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ n = evbuffer_read(req->input_buffer, fd, req->ntoread);
+ event_debug(("%s: got %d on %d\n", __func__, n, req->fd));
+
+ if (n == -1) {
+ event_warn("%s: evbuffer_read", __func__);
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ /* Adjust the amount of data that we have left to read */
+ if (req->ntoread > 0)
+ req->ntoread -= n;
+
+ if (n == 0 || req->ntoread == 0) {
+ evhttp_connection_done(evcon);
+ return;
+ }
+
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+}
+
+void
+evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
+{
+ /* This is after writing the request to the server */
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ assert(req != NULL);
+
+ /* We are done writing our header and are now expecting the response */
+ req->kind = EVHTTP_RESPONSE;
+
+ evhttp_start_read(evcon);
+}
+
+/*
+ * Clean up a connection object
+ */
+
+void
+evhttp_connection_free(struct evhttp_connection *evcon)
+{
+ if (event_initialized(&evcon->ev))
+ event_del(&evcon->ev);
+
+ if (evcon->fd != -1)
+ close(evcon->fd);
+
+ if (evcon->address != NULL)
+ free(evcon->address);
+
+ if (evcon->input_buffer != NULL)
+ evbuffer_free(evcon->input_buffer);
+
+ if (evcon->output_buffer != NULL)
+ evbuffer_free(evcon->output_buffer);
+
+ free(evcon);
+}
+
+void
+evhttp_request_dispatch(struct evhttp_connection* evcon)
+{
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+ /* this should not usually happy but it's possible */
+ if (req == NULL)
+ return;
+
+ /* we assume that the connection is connected already */
+ assert(evcon->state = EVCON_CONNECTED);
+
+ /* Create the header from the store arguments */
+ evhttp_make_header(evcon, req);
+
+ evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
+}
+
+/* Reset our connection state */
+void
+evhttp_connection_reset(struct evhttp_connection *evcon)
+{
+ if (event_initialized(&evcon->ev))
+ event_del(&evcon->ev);
+
+ if (evcon->fd != -1) {
+ close(evcon->fd);
+ evcon->fd = -1;
+ }
+ evcon->state = EVCON_DISCONNECTED;
+}
+
+/*
+ * Call back for asynchronous connection attempt.
+ */
+
+void
+evhttp_connectioncb(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ int error;
+ socklen_t errsz = sizeof(error);
+
+ if (what == EV_TIMEOUT) {
+ event_warnx("%s: connection timeout for \"%s:%d\" on %d\n",
+ __func__, evcon->address, evcon->port, evcon->fd);
+ goto cleanup;
+ }
+
+ /* Check if the connection completed */
+ if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, &error,
+ &errsz) == -1) {
+ event_warn("%s: getsockopt for \"%s:%d\" on %d",
+ __func__, evcon->address, evcon->port, evcon->fd);
+ goto cleanup;
+ }
+
+ if (error) {
+ event_warnx("%s: connect failed for \"%s:%d\" on %d: %s\n",
+ __func__, evcon->address, evcon->port, evcon->fd,
+ strerror(error));
+ goto cleanup;
+ }
+
+ /* We are connected to the server now */
+ event_debug(("%s: connected to \"%s:%d\" on %d\n",
+ __func__, evcon->address, evcon->port, evcon->fd));
+
+ evcon->state = EVCON_CONNECTED;
+
+ /* try to start requests that have queued up on this connection */
+ evhttp_request_dispatch(evcon);
+ return;
+
+ cleanup:
+ evhttp_connection_reset(evcon);
+
+ /* for now, we just signal all requests by executing their callbacks */
+ while (TAILQ_FIRST(&evcon->requests) != NULL) {
+ struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
+ TAILQ_REMOVE(&evcon->requests, request, next);
+ request->evcon = NULL;
+
+ /* we might want to set an error here */
+ request->cb(request, request->cb_arg);
+ }
+}
+
+/*
+ * Check if we got a valid response code.
+ */
+
+int
+evhttp_valid_response_code(int code)
+{
+ if (code == 0)
+ return (0);
+
+ return (1);
+}
+
+/* Parses the status line of a web server */
+
+int
+evhttp_parse_response_line(struct evhttp_request *req, char *line)
+{
+ char *protocol;
+ char *number;
+ char *readable;
+
+ protocol = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ number = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ readable = line;
+
+ if (strcmp(protocol, "HTTP/1.0") == 0) {
+ req->major = 1;
+ req->minor = 0;
+ } else if (strcmp(protocol, "HTTP/1.1") == 0) {
+ req->major = 1;
+ req->minor = 1;
+ } else {
+ event_warnx("%s: bad protocol \"%s\"\n",
+ __func__, protocol);
+ return (-1);
+ }
+
+ req->response_code = atoi(number);
+ if (!evhttp_valid_response_code(req->response_code)) {
+ event_warnx("%s: bad response code \"%s\"\n",
+ __func__, number);
+ return (-1);
+ }
+
+ if ((req->response_code_line = strdup(readable)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+ return (0);
+}
+
+/* Parse the first line of a HTTP request */
+
+int
+evhttp_parse_request_line(struct evhttp_request *req, char *line)
+{
+ char *method;
+ char *uri;
+ char *version;
+
+ /* Parse the request line */
+ method = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ uri = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ version = strsep(&line, " ");
+ if (line != NULL)
+ return (-1);
+
+ /* First line */
+ if (strcmp(method, "GET") == 0) {
+ req->type = EVHTTP_REQ_GET;
+ } else if (strcmp(method, "POST") == 0) {
+ req->type = EVHTTP_REQ_POST;
+ } else if (strcmp(method, "HEAD") == 0) {
+ req->type = EVHTTP_REQ_HEAD;
+ } else {
+ event_warnx("%s: bad method %s on request %p\n",
+ __func__, method, req);
+ return (-1);
+ }
+
+ if (strcmp(version, "HTTP/1.0") == 0) {
+ req->major = 1;
+ req->minor = 0;
+ } else if (strcmp(version, "HTTP/1.1") == 0) {
+ req->major = 1;
+ req->minor = 1;
+ } else {
+ event_warnx("%s: bad version %s on request %p\n",
+ __func__, version, req);
+ return (-1);
+ }
+
+ if ((req->uri = strdup(uri)) == NULL) {
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+const char *
+evhttp_find_header(struct evkeyvalq *headers, const char *key)
+{
+ struct evkeyval *header;
+
+ TAILQ_FOREACH(header, headers, next) {
+ if (strcasecmp(header->key, key) == 0)
+ return (header->value);
+ }
+
+ return (NULL);
+}
+
+void
+evhttp_clear_headers(struct evkeyvalq *headers)
+{
+ struct evkeyval *header;
+
+ for (header = TAILQ_FIRST(headers);
+ header != NULL;
+ header = TAILQ_FIRST(headers)) {
+ TAILQ_REMOVE(headers, header, next);
+ free(header->key);
+ free(header->value);
+ free(header);
+ }
+}
+
+/*
+ * Returns 0, if the header was successfully removed.
+ * Returns -1, if the header could not be found.
+ */
+
+int
+evhttp_remove_header(struct evkeyvalq *headers, const char *key)
+{
+ struct evkeyval *header;
+
+ TAILQ_FOREACH(header, headers, next) {
+ if (strcasecmp(header->key, key) == 0)
+ break;
+ }
+
+ if (header == NULL)
+ return (-1);
+
+ /* Free and remove the header that we found */
+ TAILQ_REMOVE(headers, header, next);
+ free(header->key);
+ free(header->value);
+ free(header);
+
+ return (0);
+}
+
+int
+evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value)
+{
+ struct evkeyval *header;
+
+ header = calloc(1, sizeof(struct evkeyval));
+ if (header == NULL) {
+ event_warn("%s: calloc", __func__);
+ return (-1);
+ }
+ if ((header->key = strdup(key)) == NULL) {
+ free(header);
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+ if ((header->value = strdup(value)) == NULL) {
+ free(header->key);
+ free(header);
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+
+ TAILQ_INSERT_TAIL(headers, header, next);
+
+ return (0);
+}
+
+/*
+ * Parses header lines from a request or a response into the specified
+ * request object given an event buffer.
+ *
+ * Returns
+ * -1 on error
+ * 0 when we need to read more headers
+ * 1 when all headers have been read.
+ */
+
+int
+evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
+{
+ u_char *endp;
+ int done = 0;
+
+ struct evkeyvalq* headers = req->input_headers;
+ while ((endp = evbuffer_find(buffer, "\r\n", 2)) != NULL) {
+ char *skey, *svalue;
+
+ if (strncmp(EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
+ evbuffer_drain(buffer, 2);
+ /* Last header - Done */
+ done = 1;
+ break;
+ }
+
+ *endp = '\0';
+ endp += 2;
+
+ event_debug(("%s: Got: %s\n", __func__, EVBUFFER_DATA(buffer)));
+
+ /* Processing of header lines */
+ if (req->got_firstline == 0) {
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ if (evhttp_parse_request_line(req, EVBUFFER_DATA(buffer)) == -1)
+ return (-1);
+ break;
+ case EVHTTP_RESPONSE:
+ if (evhttp_parse_response_line(req, EVBUFFER_DATA(buffer)) == -1)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ req->got_firstline = 1;
+ } else {
+ /* Regular header */
+ svalue = EVBUFFER_DATA(buffer);
+ skey = strsep(&svalue, ":");
+ if (svalue == NULL)
+ return (-1);
+
+ svalue += strspn(svalue, " ");
+
+ if (evhttp_add_header(headers, skey, svalue) == -1)
+ return (-1);
+ }
+
+ /* Move the uncompleted headers forward */
+ evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer));
+ }
+
+ return (done);
+}
+
+void
+evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+ struct timeval tv;
+ const char *content_length;
+ const char *connection;
+ struct evkeyvalq *headers = req->input_headers;
+
+ /* If this is a request without a body, then we are done */
+ if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) {
+ evhttp_connection_done(evcon);
+ return;
+ }
+
+ content_length = evhttp_find_header(headers, "Content-Length");
+ connection = evhttp_find_header(headers, "Connection");
+
+ if (content_length == NULL && connection == NULL)
+ req->ntoread = -1;
+ else if (content_length == NULL &&
+ strcasecmp(connection, "Close") != 0) {
+ /* Bad combination, we don't know when it will end */
+ event_warnx("%s: we got no content length, but the server"
+ " wants to keep the connection open: %s.\n",
+ __func__, connection);
+ evhttp_connection_fail(evcon);
+ return;
+ } else if (content_length == NULL)
+ req->ntoread = -1;
+ else
+ req->ntoread = atoi(content_length);
+
+ event_debug(("%s: bytes to read: %d (in buffer %d)\n",
+ __func__, req->ntoread, EVBUFFER_LENGTH(evcon->buffer)));
+
+ if (req->ntoread > 0)
+ req->ntoread -= EVBUFFER_LENGTH(evcon->input_buffer);
+
+ if (req->ntoread == 0) {
+ evhttp_connection_done(evcon);
+ return;
+ }
+
+ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon);
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+ return;
+}
+
+void
+evhttp_read_header(int fd, short what, void *arg)
+{
+ struct timeval tv;
+ struct evhttp_connection *evcon = arg;
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ int n, res;
+
+ if (what == EV_TIMEOUT) {
+ event_warnx("%s: timeout on %d\n", __func__, fd);
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ n = evbuffer_read(evcon->input_buffer, fd, -1);
+ if (n == 0) {
+ event_warnx("%s: no more data on %d\n", __func__, fd);
+ evhttp_connection_fail(evcon);
+ return;
+ }
+ if (n == -1) {
+ event_warnx("%s: bad read on %d\n", __func__, fd);
+ evhttp_connection_fail(evcon);
+ return;
+ }
+
+ res = evhttp_parse_lines(req, evcon->input_buffer);
+ if (res == -1) {
+ /* Error while reading, terminate */
+ event_warnx("%s: bad header lines on %d\n", __func__, fd);
+ evhttp_connection_fail(evcon);
+ return;
+ } else if (res == 0) {
+ /* Need more header lines */
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+ return;
+ }
+
+ /* Done reading headers, do the real work */
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ event_debug(("%s: checking for post data on %d\n",
+ __func__, fd));
+ evhttp_get_body(evcon, req);
+ break;
+
+ case EVHTTP_RESPONSE:
+ event_debug(("%s: starting to read body for \"%s\" on %d\n",
+ __func__, req->remote_host, fd));
+ evhttp_get_body(evcon, req);
+ break;
+
+ default:
+ event_warnx("%s: bad header on %d\n", __func__, fd);
+ evhttp_connection_fail(evcon);
+ break;
+ }
+}
+
+/*
+ * Creates a TCP connection to the specified port and executes a callback
+ * when finished. Failure or sucess is indicate by the passed connection
+ * object.
+ *
+ * Although this interface accepts a hostname, it is intended to take
+ * only numeric hostnames so that non-blocking DNS resolution can
+ * happen elsewhere.
+ */
+
+struct evhttp_connection *
+evhttp_connection_new(const char *address, unsigned short port)
+{
+ struct evhttp_connection *evcon = NULL;
+
+ event_debug(("Attempting connection to %s:%d\n", address, port));
+
+ if ((evcon = calloc(1, sizeof(struct evhttp_connection))) == NULL) {
+ event_warn("%s: calloc failed", __func__);
+ goto error;
+ }
+
+ evcon->fd = -1;
+ evcon->port = port;
+
+ if ((evcon->address = strdup(address)) == NULL) {
+ event_warn("%s: strdup failed", __func__);
+ goto error;
+ }
+
+ if ((evcon->input_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new failed", __func__);
+ goto error;
+ }
+
+ if ((evcon->output_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new failed", __func__);
+ goto error;
+ }
+
+ evcon->state = EVCON_DISCONNECTED;
+ TAILQ_INIT(&evcon->requests);
+
+ return (evcon);
+
+ error:
+ if (evcon != NULL)
+ evhttp_connection_free(evcon);
+ return (NULL);
+}
+
+int
+evhttp_connection_connect(struct evhttp_connection *evcon)
+{
+ struct timeval tv;
+
+ if (evcon->state == EVCON_CONNECTING)
+ return (0);
+
+ evhttp_connection_reset(evcon);
+
+ assert(!(evcon->flags & EVHTTP_CON_INCOMING));
+ evcon->flags |= EVHTTP_CON_OUTGOING;
+
+ /* Do async connection to HTTP server */
+ if ((evcon->fd = make_socket(
+ connect, evcon->address, evcon->port)) == -1) {
+ event_warn("%s: failed to connect to \"%s:%d\"",
+ __func__, evcon->address, evcon->port);
+ return (-1);
+ }
+
+ /* Set up a callback for successful connection setup */
+ event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
+ timerclear(&tv);
+ tv.tv_sec = HTTP_CONNECT_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+
+ evcon->state = EVCON_CONNECTING;
+
+ return (0);
+}
+
+/*
+ * Starts an HTTP request on the provided evhttp_connection object.
+ * If the connection object is not connected to the web server already,
+ * this will start the connection.
+ */
+
+int
+evhttp_make_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req,
+ enum evhttp_cmd_type type, const char *uri)
+{
+ /* We are making a request */
+ req->kind = EVHTTP_REQUEST;
+ req->type = type;
+ if (req->uri != NULL)
+ free(req->uri);
+ if ((req->uri = strdup(uri)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+ /* Set the protocol version if it is not supplied */
+ if (!req->major && !req->minor) {
+ req->major = 1;
+ req->minor = 1;
+ }
+
+ assert(req->evcon == NULL);
+ req->evcon = evcon;
+ assert(!(req->flags && EVHTTP_REQ_OWN_CONNECTION));
+
+ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+ /* If the connection object is not connected; make it so */
+ if (evcon->state != EVCON_CONNECTED)
+ return (evhttp_connection_connect(evcon));
+
+ /*
+ * If it's connected already and we are the first in the queue,
+ * then we can dispatch this request immediately. Otherwise, it
+ * will be dispatched once the pending requests are completed.
+ */
+ if (TAILQ_FIRST(&evcon->requests) == req)
+ evhttp_request_dispatch(evcon);
+
+ return (0);
+}
+
+/*
+ * Reads data from file descriptor into request structure
+ * Request structure needs to be set up correctly.
+ */
+
+void
+evhttp_start_read(struct evhttp_connection *evcon)
+{
+ struct timeval tv;
+
+ /* Set up an event to read the headers */
+ if (event_initialized(&evcon->ev))
+ event_del(&evcon->ev);
+ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon);
+
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+}
+
+void
+evhttp_send_done(struct evhttp_connection *evcon, void *arg)
+{
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ TAILQ_REMOVE(&evcon->requests, req, next);
+
+ if (req->flags & EVHTTP_REQ_OWN_CONNECTION)
+ evhttp_connection_free(evcon);
+
+ evhttp_request_free(req);
+}
+
+/*
+ * Returns an error page.
+ */
+
+void
+evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
+{
+ char *fmt = "<HTML><HEAD>\n"
+ "<TITLE>%d %s</TITLE>\n"
+ "</HEAD><BODY>\n"
+ "<H1>Method Not Implemented</H1>\n"
+ "Invalid method in request<P>\n"
+ "</BODY></HTML>\n";
+
+ struct evbuffer *buf = evbuffer_new();
+
+ evhttp_response_code(req, error, reason);
+
+ evbuffer_add_printf(buf, fmt, error, reason);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+}
+
+/* Requires that headers and response code are already set up */
+
+static __inline void
+evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ struct evhttp_connection *evcon = req->evcon;
+
+ assert(TAILQ_FIRST(&evcon->requests) == req);
+
+ /* xxx: not sure if we really should expost the data buffer this way */
+ evbuffer_add_buffer(req->output_buffer, databuf);
+
+ /* Adds headers to the response */
+ evhttp_make_header(evcon, req);
+
+ evhttp_write_buffer(evcon, evhttp_send_done, NULL);
+}
+
+void
+evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
+ struct evbuffer *databuf)
+{
+ evhttp_response_code(req, code, reason);
+
+ evhttp_send(req, databuf);
+}
+
+void
+evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
+{
+ req->kind = EVHTTP_RESPONSE;
+ req->response_code = code;
+ if (req->response_code_line != NULL)
+ free(req->response_code_line);
+ req->response_code_line = strdup(reason);
+}
+
+void
+evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ if (req->kind != EVHTTP_RESPONSE)
+ evhttp_response_code(req, 200, "OK");
+
+ evhttp_clear_headers(req->output_headers);
+ evhttp_add_header(req->output_headers, "Content-Type", "text/html");
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
+ evhttp_send(req, databuf);
+}
+
+/*
+ * Helper function to parse out arguments in a query.
+ * The arguments are separated by key and value.
+ */
+
+void
+evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
+{
+ char *line;
+ char *argument;
+ char *p;
+
+ TAILQ_INIT(headers);
+
+ /* No arguments - we are done */
+ if (strchr(uri, '?') == NULL)
+ return;
+
+ if ((line = strdup(uri)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+
+ argument = line;
+
+ /* We already know that there has to be a ? */
+ strsep(&argument, "?");
+
+ p = argument;
+ while (p != NULL && *p != '\0') {
+ char *key, *value;
+ argument = strsep(&p, "&");
+
+ value = argument;
+ key = strsep(&value, "=");
+ if (value == NULL)
+ goto error;
+
+ event_warnx("Got: %s -> %s\n", key, value);
+ evhttp_add_header(headers, key, value);
+ }
+
+ error:
+ free(line);
+}
+
+void
+evhttp_handle_request(struct evhttp_request *req, void *arg)
+{
+ struct evhttp *http = arg;
+ struct evhttp_cb *cb;
+
+ /* Test for different URLs */
+ TAILQ_FOREACH(cb, &http->callbacks, next) {
+ int res;
+ char *p = strchr(req->uri, '?');
+ if (p == NULL)
+ res = strcmp(cb->what, req->uri) == 0;
+ else
+ res = strncmp(cb->what, req->uri,
+ (size_t)(p - req->uri)) == 0;
+ if (res) {
+ (*cb->cb)(req, cb->cbarg);
+ return;
+ }
+ }
+
+ /* Generic call back */
+ if (http->gencb) {
+ (*http->gencb)(req, http->gencbarg);
+ return;
+ } else {
+ /* We need to send a 404 here */
+ char *fmt = "<html><head>"
+ "<title>404 Not Found</title>"
+ "</head><body>"
+ "<h1>Not Found</h1>"
+ "<p>The requested URL %s was not found on this server.</p>"
+ "</body></html>\n";
+
+ char *escaped_html = evhttp_htmlescape(req->uri);
+ struct evbuffer *buf = evbuffer_new();
+
+ evhttp_response_code(req, HTTP_NOTFOUND, "Not Found");
+
+ evbuffer_add_printf(buf, fmt, escaped_html);
+
+ free(escaped_html);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+ }
+}
+
+static void
+accept_socket(int fd, short what, void *arg)
+{
+ struct evhttp *http = arg;
+ struct sockaddr_storage ss;
+ socklen_t addrlen = sizeof(ss);
+ int nfd;
+
+ if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
+ event_warn("%s: bad accept", __func__);
+ return;
+ }
+
+ evhttp_get_request(nfd, (struct sockaddr *)&ss, addrlen,
+ evhttp_handle_request, http);
+}
+
+static int
+bind_socket(struct evhttp *http, const char *address, u_short port)
+{
+ struct event *ev = &http->bind_ev;
+ int fd;
+
+ if ((fd = make_socket(bind, address, port)) == -1)
+ return (-1);
+
+ if (listen(fd, 10) == -1) {
+ event_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ /* Schedule the socket for accepting */
+ event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http);
+ event_add(ev, NULL);
+
+ event_debug(("Bound to port %d - Awaiting connections ... ", port));
+
+ return (0);
+}
+
+/*
+ * Start a web server on the specified address and port.
+ */
+
+struct evhttp *
+evhttp_start(const char *address, u_short port)
+{
+ struct evhttp *http;
+
+ if ((http = calloc(1, sizeof(struct evhttp))) == NULL) {
+ event_warn("%s: calloc", __func__);
+ return (NULL);
+ }
+
+ TAILQ_INIT(&http->callbacks);
+
+ if (bind_socket(http, address, port) == -1) {
+ free(http);
+ return (NULL);
+ }
+
+ return (http);
+}
+
+void
+evhttp_free(struct evhttp* http)
+{
+ struct evhttp_cb *http_cb;
+ int fd = http->bind_ev.ev_fd;
+
+ /* Remove the accepting part */
+ event_del(&http->bind_ev);
+ close(fd);
+
+ while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
+ TAILQ_REMOVE(&http->callbacks, http_cb, next);
+ free(http_cb->what);
+ free(http_cb);
+ }
+
+ free(http);
+}
+
+void
+evhttp_set_cb(struct evhttp *http, const char *uri,
+ void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ struct evhttp_cb *http_cb;
+
+ if ((http_cb = calloc(1, sizeof(struct evhttp_cb))) == NULL)
+ event_err(1, "%s: calloc", __func__);
+
+ http_cb->what = strdup(uri);
+ http_cb->cb = cb;
+ http_cb->cbarg = cbarg;
+
+ TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);
+}
+
+void
+evhttp_set_gencb(struct evhttp *http,
+ void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ http->gencb = cb;
+ http->gencbarg = cbarg;
+}
+
+/*
+ * Request related functions
+ */
+
+struct evhttp_request *
+evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+ struct evhttp_request *req = NULL;
+
+ /* Allocate request structure */
+ if ((req = calloc(1, sizeof(struct evhttp_request))) == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+
+ req->kind = EVHTTP_RESPONSE;
+ req->input_headers = calloc(1, sizeof(struct evkeyvalq));
+ if (req->input_headers == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+ TAILQ_INIT(req->input_headers);
+
+ req->output_headers = calloc(1, sizeof(struct evkeyvalq));
+ if (req->output_headers == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+ TAILQ_INIT(req->output_headers);
+
+ if ((req->input_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new", __func__);
+ goto error;
+ }
+
+ if ((req->output_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new", __func__);
+ goto error;
+ }
+
+ req->cb = cb;
+ req->cb_arg = arg;
+
+ return (req);
+
+ error:
+ if (req != NULL)
+ evhttp_request_free(req);
+ return (NULL);
+}
+
+void
+evhttp_request_free(struct evhttp_request *req)
+{
+ if (req->remote_host != NULL)
+ free(req->remote_host);
+ if (req->uri != NULL)
+ free(req->uri);
+ if (req->response_code_line != NULL)
+ free(req->response_code_line);
+
+ evhttp_clear_headers(req->input_headers);
+ free(req->input_headers);
+
+ evhttp_clear_headers(req->output_headers);
+ free(req->output_headers);
+
+ if (req->input_buffer != NULL)
+ evbuffer_free(req->input_buffer);
+
+ if (req->output_buffer != NULL)
+ evbuffer_free(req->output_buffer);
+
+ free(req);
+}
+
+/*
+ * Allows for inspection of the request URI
+ */
+
+const char *
+evhttp_request_uri(struct evhttp_request *req) {
+ if (req->uri == NULL)
+ event_debug(("%s: request %p has no uri\n", req));
+ return (req->uri);
+}
+
+/*
+ * Takes a file descriptor to read a request from.
+ * The callback is executed once the whole request has been read.
+ */
+
+void
+evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
+ void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+ struct evhttp_connection *evcon;
+ struct evhttp_request *req;
+ char *hostname, *portname;
+
+ name_from_addr(sa, salen, &hostname, &portname);
+ event_debug(("%s: new request from %s:%s on %d\n",
+ __func__, hostname, portname, fd));
+
+ /* we need a connection object to put the http request on */
+ if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
+ return;
+ evcon->flags |= EVHTTP_CON_INCOMING;
+ evcon->state = EVCON_CONNECTED;
+
+ if ((req = evhttp_request_new(cb, arg)) == NULL) {
+ evhttp_connection_free(evcon);
+ return;
+ }
+
+ evcon->fd = fd;
+
+ req->evcon = evcon; /* the request ends up owning the connection */
+ req->flags |= EVHTTP_REQ_OWN_CONNECTION;
+
+ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+ req->kind = EVHTTP_REQUEST;
+
+ if ((req->remote_host = strdup(hostname)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+ req->remote_port = atoi(portname);
+
+ evhttp_start_read(evcon);
+}
+
+
+/*
+ * Network helper functions that we do not want to export to the rest of
+ * the world.
+ */
+
+static struct addrinfo *
+addr_from_name(char *address)
+{
+ struct addrinfo ai, *aitop;
+
+ memset(&ai, 0, sizeof (ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_RAW;
+ ai.ai_flags = 0;
+ if (getaddrinfo(address, NULL, &ai, &aitop) != 0) {
+ event_warn("getaddrinfo");
+ return (NULL);
+ }
+
+ return (aitop);
+}
+
+static void
+name_from_addr(struct sockaddr *sa, socklen_t salen,
+ char **phost, char **pport)
+{
+ static char ntop[NI_MAXHOST];
+ static char strport[NI_MAXSERV];
+
+ if (getnameinfo(sa, salen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV) != 0)
+ event_err(1, "getnameinfo failed");
+
+ *phost = ntop;
+ *pport = strport;
+}
+
+/* Either connect or bind */
+
+static int
+make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t),
+ struct addrinfo *ai)
+{
+ struct linger linger;
+ int fd, on = 1;
+ int serrno;
+
+ /* Create listen socket */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ event_warn("socket");
+ return (-1);
+ }
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ event_warn("fcntl(O_NONBLOCK)");
+ goto out;
+ }
+
+ if (fcntl(fd, F_SETFD, 1) == -1) {
+ event_warn("fcntl(F_SETFD)");
+ goto out;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on));
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
+
+ if ((f)(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+ if (errno != EINPROGRESS) {
+ goto out;
+ }
+ }
+
+ return (fd);
+
+ out:
+ serrno = errno;
+ close(fd);
+ errno = serrno;
+ return (-1);
+}
+
+static int
+make_socket(int (*f)(int, const struct sockaddr *, socklen_t),
+ const char *address, short port)
+{
+ struct addrinfo ai, *aitop;
+ char strport[NI_MAXSERV];
+ int fd;
+
+ memset(&ai, 0, sizeof (ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_STREAM;
+ ai.ai_flags = f != connect ? AI_PASSIVE : 0;
+ snprintf(strport, sizeof (strport), "%d", port);
+ if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
+ event_warn("getaddrinfo");
+ return (-1);
+ }
+
+ fd = make_socket_ai(f, aitop);
+
+ freeaddrinfo(aitop);
+
+ return (fd);
+}
diff --git a/install-sh b/install-sh
new file mode 100755
index 00000000..89fc9b09
--- /dev/null
+++ b/install-sh
@@ -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
index 00000000..08369c6d
--- /dev/null
+++ b/kqueue.c
@@ -0,0 +1,413 @@
+/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */
+
+/*
+ * 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/event.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
+#define INTPTR(x) (intptr_t)x
+#else
+#define INTPTR(x) x
+#endif
+
+#include "event.h"
+#include "log.h"
+
+#define EVLIST_X_KQINKERNEL 0x1000
+
+#define NEVENT 64
+
+struct kqop {
+ struct kevent *changes;
+ int nchanges;
+ struct kevent *events;
+ int nevents;
+ int kq;
+};
+
+void *kq_init (void);
+int kq_add (void *, struct event *);
+int kq_del (void *, struct event *);
+int kq_recalc (struct event_base *, void *, int);
+int kq_dispatch (struct event_base *, void *, struct timeval *);
+int kq_insert (struct kqop *, struct kevent *);
+void kq_dealloc (void *);
+
+const struct eventop kqops = {
+ "kqueue",
+ kq_init,
+ kq_add,
+ kq_del,
+ kq_recalc,
+ kq_dispatch,
+ kq_dealloc
+};
+
+void *
+kq_init(void)
+{
+ int kq;
+ struct kqop *kqueueop;
+
+ /* Disable kqueue when this environment variable is set */
+ if (getenv("EVENT_NOKQUEUE"))
+ return (NULL);
+
+ if (!(kqueueop = calloc(1, sizeof(struct kqop))))
+ return (NULL);
+
+ /* Initalize the kernel queue */
+
+ if ((kq = kqueue()) == -1) {
+ event_warn("kqueue");
+ free (kqueueop);
+ return (NULL);
+ }
+
+ kqueueop->kq = kq;
+
+ /* Initalize fields */
+ kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
+ if (kqueueop->changes == NULL) {
+ free (kqueueop);
+ return (NULL);
+ }
+ kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
+ if (kqueueop->events == NULL) {
+ free (kqueueop->changes);
+ free (kqueueop);
+ return (NULL);
+ }
+ kqueueop->nevents = NEVENT;
+
+ /* Check for Mac OS X kqueue bug. */
+ kqueueop->changes[0].ident = -1;
+ kqueueop->changes[0].filter = EVFILT_READ;
+ kqueueop->changes[0].flags = EV_ADD;
+ /*
+ * If kqueue works, then kevent will succeed, and it will
+ * stick an error in events[0]. If kqueue is broken, then
+ * kevent will fail.
+ */
+ if (kevent(kq,
+ kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
+ kqueueop->events[0].ident != -1 ||
+ kqueueop->events[0].flags != EV_ERROR) {
+ event_warn("%s: detected broken kqueue; not using.", __func__);
+ free(kqueueop->changes);
+ free(kqueueop->events);
+ free(kqueueop);
+ close(kq);
+ return (NULL);
+ }
+
+ return (kqueueop);
+}
+
+int
+kq_recalc(struct event_base *base, 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) {
+ event_warn("%s: malloc", __func__);
+ return (-1);
+ }
+ kqop->changes = newchange;
+
+ newresult = realloc(kqop->events,
+ 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) {
+ event_warn("%s: malloc", __func__);
+ return (-1);
+ }
+ kqop->events = newresult;
+
+ kqop->nevents = nevents;
+ }
+
+ memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
+
+ event_debug(("%s: fd %d %s%s",
+ __func__, kev->ident,
+ kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
+ kev->flags == EV_DELETE ? " (del)" : ""));
+
+ return (0);
+}
+
+static void
+kq_sighandler(int sig)
+{
+ /* Do nothing here */
+}
+
+int
+kq_dispatch(struct event_base *base, 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) {
+ event_warn("kevent");
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ event_debug(("%s: kevent reports %d", __func__, 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.
+ * EINVAL for some reasons not understood; EINVAL
+ * should not be returned ever; but FreeBSD does :-\
+ * 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 == EINVAL ||
+ events[i].data == ENOENT)
+ continue;
+ errno = events[i].data;
+ return (-1);
+ }
+
+ ev = (struct event *)events[i].udata;
+
+ if (events[i].filter == EVFILT_READ) {
+ which |= EV_READ;
+ } else if (events[i].filter == EVFILT_WRITE) {
+ which |= EV_WRITE;
+ } else if (events[i].filter == EVFILT_SIGNAL) {
+ which |= EV_SIGNAL;
+ }
+
+ if (!which)
+ continue;
+
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+
+ event_active(ev, which,
+ ev->ev_events & EV_SIGNAL ? events[i].data : 1);
+ }
+
+ return (0);
+}
+
+
+int
+kq_add(void *arg, struct event *ev)
+{
+ struct kqop *kqop = arg;
+ struct kevent kev;
+
+ if (ev->ev_events & EV_SIGNAL) {
+ int nsignal = EVENT_SIGNAL(ev);
+
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = nsignal;
+ kev.filter = EVFILT_SIGNAL;
+ kev.flags = EV_ADD;
+ if (!(ev->ev_events & EV_PERSIST))
+ kev.flags |= EV_ONESHOT;
+ kev.udata = INTPTR(ev);
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ if (signal(nsignal, kq_sighandler) == SIG_ERR)
+ return (-1);
+
+ 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;
+#ifdef NOTE_EOF
+ /* Make it behave like select() and poll() */
+ kev.fflags = NOTE_EOF;
+#endif
+ kev.flags = EV_ADD;
+ if (!(ev->ev_events & EV_PERSIST))
+ kev.flags |= EV_ONESHOT;
+ kev.udata = INTPTR(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;
+ if (!(ev->ev_events & EV_PERSIST))
+ kev.flags |= EV_ONESHOT;
+ kev.udata = INTPTR(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_SIGNAL) {
+ int nsignal = EVENT_SIGNAL(ev);
+
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = nsignal;
+ kev.filter = EVFILT_SIGNAL;
+ kev.flags = EV_DELETE;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ if (signal(nsignal, SIG_DFL) == SIG_ERR)
+ return (-1);
+
+ 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);
+}
+
+void
+kq_dealloc(void *arg)
+{
+ struct kqop *kqop = arg;
+
+ if (kqop->changes)
+ free(kqop->changes);
+ if (kqop->events)
+ free(kqop->events);
+ if (kqop->kq)
+ close(kqop->kq);
+ memset(kqop, 0, sizeof(struct kqop));
+ free(kqop);
+}
diff --git a/log.c b/log.c
new file mode 100644
index 00000000..c9275e36
--- /dev/null
+++ b/log.c
@@ -0,0 +1,219 @@
+/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * log.c
+ *
+ * Based on err.c, which was adapted from OpenBSD libc *err* *warn* code.
+ *
+ * Copyright (c) 2005 Nick Mathewson <nickm@freehaven.net>
+ *
+ * Copyright (c) 2000 Dug Song <dugsong@monkey.org>
+ *
+ * Copyright (c) 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include "misc.h"
+#endif
+#include <sys/types.h>
+#include <sys/tree.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include "event.h"
+
+#include "log.h"
+
+static void _warn_helper(int severity, int log_errno, const char *fmt,
+ va_list ap);
+static void event_log(int severity, const char *msg);
+
+static int
+event_vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ int r;
+ if (size == 0)
+ return -1;
+#ifdef WIN32
+ r = _vsnprintf(str, size, format, args);
+#else
+ r = vsnprintf(str, size, format, args);
+#endif
+ str[size-1] = '\0';
+ if (r < 0 || ((size_t)r) >= size) {
+ /* different platforms behave differently on overflow;
+ * handle both kinds. */
+ return -1;
+ }
+ return r;
+}
+
+static int
+event_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int r;
+ va_start(ap, format);
+ r = event_vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return r;
+}
+
+void
+event_err(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_ERR, errno, fmt, ap);
+ va_end(ap);
+ exit(eval);
+}
+
+void
+event_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_WARN, errno, fmt, ap);
+ va_end(ap);
+}
+
+void
+event_errx(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_ERR, -1, fmt, ap);
+ va_end(ap);
+ exit(eval);
+}
+
+void
+event_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_WARN, -1, fmt, ap);
+ va_end(ap);
+}
+
+void
+event_msgx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_MSG, -1, fmt, ap);
+ va_end(ap);
+}
+
+void
+_event_debugx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap);
+ va_end(ap);
+}
+
+static void
+_warn_helper(int severity, int log_errno, const char *fmt, va_list ap)
+{
+ char buf[1024];
+ size_t len;
+
+ if (fmt != NULL)
+ event_vsnprintf(buf, sizeof(buf), fmt, ap);
+ else
+ buf[0] = '\0';
+
+ if (log_errno >= 0) {
+ len = strlen(buf);
+ if (len < sizeof(buf) - 3) {
+ event_snprintf(buf + len, sizeof(buf) - len, ": %s",
+ strerror(log_errno));
+ }
+ }
+
+ event_log(severity, buf);
+}
+
+static event_log_cb log_fn = NULL;
+
+void
+event_set_log_callback(event_log_cb cb)
+{
+ log_fn = cb;
+}
+
+static void
+event_log(int severity, const char *msg)
+{
+ if (log_fn)
+ log_fn(severity, msg);
+ else {
+ const char *severity_str;
+ switch (severity) {
+ case _EVENT_LOG_DEBUG:
+ severity_str = "debug";
+ break;
+ case _EVENT_LOG_MSG:
+ severity_str = "msg";
+ break;
+ case _EVENT_LOG_WARN:
+ severity_str = "warn";
+ break;
+ case _EVENT_LOG_ERR:
+ severity_str = "err";
+ break;
+ default:
+ severity_str = "???";
+ break;
+ }
+ (void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
+ }
+}
diff --git a/log.h b/log.h
new file mode 100644
index 00000000..1f843cf9
--- /dev/null
+++ b/log.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2000-2004 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. 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 _LOG_H_
+#define _LOG_H_
+
+void event_err(int eval, const char *fmt, ...);
+void event_warn(const char *fmt, ...);
+void event_errx(int eval, const char *fmt, ...);
+void event_warnx(const char *fmt, ...);
+void event_msgx(const char *fmt, ...);
+void _event_debugx(const char *fmt, ...);
+
+#ifdef USE_DEBUG
+#define event_debug(x) _event_debugx x
+#else
+#define event_debug(x) do {;} while (0)
+#endif
+
+#endif
diff --git a/missing b/missing
new file mode 100755
index 00000000..e7ef83a1
--- /dev/null
+++ b/missing
@@ -0,0 +1,360 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2003-09-02.23
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003
+# Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case "$1" in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ # Exit code 63 means version mismatch. This often happens
+ # when the user try to use an ancient version of a tool on
+ # a file that requires a minimum version. In this case we
+ # we should proceed has if the program had been absent, or
+ # if --run hadn't been passed.
+ if test $? = 63; then
+ run=:
+ msg="probably too old"
+ fi
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case "$1" in
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ tar try tar, gnutar, gtar, then tar without non-portable flags
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Send bug reports to <bug-automake@gnu.org>."
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+ aclocal*)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case "$f" in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ autom4te)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them.
+ You can get \`$1' as part of \`Autoconf' from any GNU
+ archive site."
+
+ file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
+ test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo "#! /bin/sh"
+ echo "# Created by GNU Automake missing as a replacement of"
+ echo "# $ $@"
+ echo "exit 0"
+ chmod +x $file
+ exit 1
+ fi
+ ;;
+
+ bison|yacc)
+ echo 1>&2 "\
+WARNING: \`$1' $msg. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f y.tab.h ]; then
+ echo >y.tab.h
+ fi
+ if [ ! -f y.tab.c ]; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex|flex)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f lex.yy.c ]; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+ fi
+ if [ -f "$file" ]; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit 1
+ fi
+ ;;
+
+ makeinfo)
+ if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
+ # We have makeinfo, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+ fi
+ touch $file
+ ;;
+
+ tar)
+ shift
+ if test -n "$run"; then
+ echo 1>&2 "ERROR: \`tar' requires --run"
+ exit 1
+ fi
+
+ # We have already tried tar in the generic part.
+ # Look for gnutar/gtar before invocation to avoid ugly error
+ # messages.
+ if (gnutar --version > /dev/null 2>&1); then
+ gnutar "$@" && exit 0
+ fi
+ if (gtar --version > /dev/null 2>&1); then
+ gtar "$@" && exit 0
+ fi
+ firstarg="$1"
+ if shift; then
+ case "$firstarg" in
+ *o*)
+ firstarg=`echo "$firstarg" | sed s/o//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ case "$firstarg" in
+ *h*)
+ firstarg=`echo "$firstarg" | sed s/h//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ fi
+
+ echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+ You may want to install GNU tar or Free paxutils, or check the
+ command line arguments."
+ exit 1
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequisites for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/mkinstalldirs b/mkinstalldirs
new file mode 100755
index 00000000..6b3b5fc5
--- /dev/null
+++ b/mkinstalldirs
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp"
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/poll.c b/poll.c
new file mode 100644
index 00000000..f05819e0
--- /dev/null
+++ b/poll.c
@@ -0,0 +1,377 @@
+/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * Copyright 2000-2003 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "log.h"
+
+extern volatile sig_atomic_t evsignal_caught;
+
+struct pollop {
+ int event_count; /* Highest number alloc */
+ int nfds; /* Size of event_* */
+ int fd_count; /* Size of idxplus1_by_fd */
+ struct pollfd *event_set;
+ struct event **event_r_back;
+ struct event **event_w_back;
+ int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
+ * that 0 (which is easy to memset) can mean
+ * "no entry." */
+ sigset_t evsigmask;
+};
+
+void *poll_init (void);
+int poll_add (void *, struct event *);
+int poll_del (void *, struct event *);
+int poll_recalc (struct event_base *, void *, int);
+int poll_dispatch (struct event_base *, void *, struct timeval *);
+void poll_dealloc (void *);
+
+const struct eventop pollops = {
+ "poll",
+ poll_init,
+ poll_add,
+ poll_del,
+ poll_recalc,
+ poll_dispatch,
+ poll_dealloc
+};
+
+void *
+poll_init(void)
+{
+ struct pollop *pollop;
+
+ /* Disable poll when this environment variable is set */
+ if (getenv("EVENT_NOPOLL"))
+ return (NULL);
+
+ if (!(pollop = calloc(1, sizeof(struct pollop))))
+ return (NULL);
+
+ evsignal_init(&pollop->evsigmask);
+
+ return (pollop);
+}
+
+/*
+ * Called with the highest fd that we know about. If it is 0, completely
+ * recalculate everything.
+ */
+
+int
+poll_recalc(struct event_base *base, void *arg, int max)
+{
+ struct pollop *pop = arg;
+
+ return (evsignal_recalc(&pop->evsigmask));
+}
+
+#ifdef CHECK_INVARIANTS
+static void
+poll_check_ok(struct pollop *pop)
+{
+ int i, idx;
+ struct event *ev;
+
+ for (i = 0; i < pop->fd_count; ++i) {
+ idx = pop->idxplus1_by_fd[i]-1;
+ if (idx < 0)
+ continue;
+ assert(pop->event_set[idx].fd == i);
+ if (pop->event_set[idx].events & POLLIN) {
+ ev = pop->event_r_back[idx];
+ assert(ev);
+ assert(ev->ev_events & EV_READ);
+ assert(ev->ev_fd == i);
+ }
+ if (pop->event_set[idx].events & POLLOUT) {
+ ev = pop->event_w_back[idx];
+ assert(ev);
+ assert(ev->ev_events & EV_WRITE);
+ assert(ev->ev_fd == i);
+ }
+ }
+ for (i = 0; i < pop->nfds; ++i) {
+ struct pollfd *pfd = &pop->event_set[i];
+ assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
+ }
+}
+#else
+#define poll_check_ok(pop)
+#endif
+
+int
+poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ int res, i, sec, nfds;
+ struct pollop *pop = arg;
+
+ if (evsignal_deliver(&pop->evsigmask) == -1)
+ return (-1);
+
+ poll_check_ok(pop);
+ sec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
+ nfds = pop->nfds;
+ res = poll(pop->event_set, nfds, sec);
+
+ if (evsignal_recalc(&pop->evsigmask) == -1)
+ return (-1);
+
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("poll");
+ return (-1);
+ }
+
+ evsignal_process();
+ return (0);
+ } else if (evsignal_caught)
+ evsignal_process();
+
+ event_debug(("%s: poll reports %d", __func__, res));
+
+ if (res == 0)
+ return (0);
+
+ for (i = 0; i < nfds; i++) {
+ int what = pop->event_set[i].revents;
+ struct event *r_ev = NULL, *w_ev = NULL;
+ if (!what)
+ continue;
+
+ res = 0;
+
+ /* If the file gets closed notify */
+ if (what & (POLLHUP|POLLERR))
+ what |= POLLIN|POLLOUT;
+ if (what & POLLIN) {
+ res |= EV_READ;
+ r_ev = pop->event_r_back[i];
+ }
+ if (what & POLLOUT) {
+ res |= EV_WRITE;
+ w_ev = pop->event_w_back[i];
+ }
+ if (res == 0)
+ continue;
+
+ if (r_ev && (res & r_ev->ev_events)) {
+ if (!(r_ev->ev_events & EV_PERSIST))
+ event_del(r_ev);
+ event_active(r_ev, res & r_ev->ev_events, 1);
+ }
+ if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
+ if (!(w_ev->ev_events & EV_PERSIST))
+ event_del(w_ev);
+ event_active(w_ev, res & w_ev->ev_events, 1);
+ }
+ }
+
+ return (0);
+}
+
+int
+poll_add(void *arg, struct event *ev)
+{
+ struct pollop *pop = arg;
+ struct pollfd *pfd = NULL;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(&pop->evsigmask, ev));
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ poll_check_ok(pop);
+ if (pop->nfds + 1 >= pop->event_count) {
+ if (pop->event_count < 32)
+ pop->event_count = 32;
+ else
+ pop->event_count *= 2;
+
+ /* We need more file descriptors */
+ pop->event_set = realloc(pop->event_set,
+ pop->event_count * sizeof(struct pollfd));
+ if (pop->event_set == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ pop->event_r_back = realloc(pop->event_r_back,
+ pop->event_count * sizeof(struct event *));
+ pop->event_w_back = realloc(pop->event_w_back,
+ pop->event_count * sizeof(struct event *));
+ if (pop->event_r_back == NULL ||
+ pop->event_w_back == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ }
+ if (ev->ev_fd >= pop->fd_count) {
+ int new_count;
+ if (pop->fd_count < 32)
+ new_count = 32;
+ else
+ new_count = pop->fd_count * 2;
+ while (new_count <= ev->ev_fd)
+ new_count *= 2;
+ pop->idxplus1_by_fd =
+ realloc(pop->idxplus1_by_fd, new_count*sizeof(int));
+ if (pop->idxplus1_by_fd == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ memset(pop->idxplus1_by_fd + pop->fd_count,
+ 0, sizeof(int)*(new_count - pop->fd_count));
+ pop->fd_count = new_count;
+ }
+
+ i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
+ if (i >= 0) {
+ pfd = &pop->event_set[i];
+ } else {
+ i = pop->nfds++;
+ pfd = &pop->event_set[i];
+ pfd->events = 0;
+ pfd->fd = ev->ev_fd;
+ pop->event_w_back[i] = pop->event_r_back[i] = NULL;
+ pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
+ }
+
+ pfd->revents = 0;
+ if (ev->ev_events & EV_WRITE) {
+ pfd->events |= POLLOUT;
+ pop->event_w_back[i] = ev;
+ }
+ if (ev->ev_events & EV_READ) {
+ pfd->events |= POLLIN;
+ pop->event_r_back[i] = ev;
+ }
+ poll_check_ok(pop);
+
+ return (0);
+}
+
+/*
+ * Nothing to be done here.
+ */
+
+int
+poll_del(void *arg, struct event *ev)
+{
+ struct pollop *pop = arg;
+ struct pollfd *pfd = NULL;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(&pop->evsigmask, ev));
+
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ poll_check_ok(pop);
+ i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
+ if (i < 0)
+ return (-1);
+
+ /* Do we still want to read or write? */
+ pfd = &pop->event_set[i];
+ if (ev->ev_events & EV_READ) {
+ pfd->events &= ~POLLIN;
+ pop->event_r_back[i] = NULL;
+ }
+ if (ev->ev_events & EV_WRITE) {
+ pfd->events &= ~POLLOUT;
+ pop->event_w_back[i] = NULL;
+ }
+ poll_check_ok(pop);
+ if (pfd->events)
+ /* Another event cares about that fd. */
+ return (0);
+
+ /* Okay, so we aren't interested in that fd anymore. */
+ pop->idxplus1_by_fd[ev->ev_fd] = 0;
+
+ --pop->nfds;
+ if (i != pop->nfds) {
+ /*
+ * Shift the last pollfd down into the now-unoccupied
+ * position.
+ */
+ memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
+ sizeof(struct pollfd));
+ pop->event_r_back[i] = pop->event_r_back[pop->nfds];
+ pop->event_w_back[i] = pop->event_w_back[pop->nfds];
+ pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
+ }
+
+ poll_check_ok(pop);
+ return (0);
+}
+
+void
+poll_dealloc(void *arg)
+{
+ struct pollop *pop = arg;
+
+ if (pop->event_set)
+ free(pop->event_set);
+ if (pop->event_r_back)
+ free(pop->event_r_back);
+ if (pop->event_w_back)
+ free(pop->event_w_back);
+ if (pop->idxplus1_by_fd)
+ free(pop->idxplus1_by_fd);
+
+ memset(pop, 0, sizeof(struct pollop));
+ free(pop);
+}
diff --git a/rtsig.c b/rtsig.c
new file mode 100644
index 00000000..29aade69
--- /dev/null
+++ b/rtsig.c
@@ -0,0 +1,985 @@
+/*
+ * Copyright (c) 2006 Mathew Mills <mathewmills@mac.com>
+ * 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 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.
+ */
+/*
+ * Meta-level comments: You know that a kernel interface is wrong if
+ * supporting it requires three times more code than any of the other
+ * kernel interfaces supported in libevent. Niels - 2006-02-22
+ */
+/**
+
+ "RTSIG" is a shorthand for using O_ASYNC to make descriptors send
+ signals when readable/writable and to use POSIX real-time signals
+ witch are queued unlike normal signals. At first blush this may
+ seem like a alternative to epoll, but a number of problems arise
+ when attempting to build an eventloop entirely out of rtsig.
+ Still, we can use rtsig in combination with poll() to
+ provide an eventloop that allows for many thousands of sockets
+ without huge overheads implicit with using select() or poll()
+ alone. epoll and kqueue are far superior to rtsig and should be
+ used where available, but rtsig has been in standard Linux kernels
+ for a long time and have a huge installation base. epoll requires
+ special patches for 2.4 kernels and 2.6 kernels are not yet nearly
+ so ubiquitous.
+
+ rtsig problems:
+ - O_ASYNC mechanisms work only on sockets - not pipes or tty's
+
+ - O_ASYNC signals are edge-triggered, POLLIN on packet arriving
+ or socket close; POLLOUT when a socket transitions from
+ non-writable to writable. Being edge-triggered means the
+ event-handler callbacks must transition the level ( reading
+ completely the socket buffer contents ) or it will be unable to
+ reliably receive notification again.
+
+ - rtsig implementations must be intimately involved in how a
+ process dispatches signals.
+
+ - delivering signals per-event can be expensive, CPU-wise, but
+ sigtimedwait() blocks on signals only and means non-sockets
+ cannot be serviced.
+
+ Theory of operation:
+ This libevent module uses rtsig to allow us to manage a set of
+ poll-event descriptors. We can drop uninteresting fd's from the
+ pollset if the fd will send a signal when it becomes interesting
+ again.
+
+ poll() offers us level-triggering and, when we have verified the
+ level of a socket, we can trust the edge-trigger nature of the
+ ASYNC signal.
+
+ As an eventloop we must poll for external events but leverage
+ kernel functionality to sleep between events ( until the loop's
+ next scheduled timed event ).
+
+ If we are polling on any non-sockets then we simply have no choice
+ about blocking on the poll() call. If we blocked on the
+ sigtimedwait() call as rtsig papers recommend we will not wake on
+ non-socket state transitions. As part of libevent, this module
+ must support non-socket polling.
+
+ Many applications, however, do not need to poll on non-sockets and
+ so this module should be able to optimize this case by using
+ sigtimedwait(). For this reason this module can actually trigger
+ events in each of three different ways:
+ - poll() returning ready events from descriptors in the pollset
+
+ - real-time signals dequeued via sigtimedwait()
+
+ - real-time signals that call an installed signal handler which in
+ turn writes the contents of siginfo to one end of a socketpair
+ DGRAM socket. The other end of the socket is always in the
+ pollset so poll will be guaranteed to return even if the signal is
+ received before entering poll().
+
+ non-socket descriptors force us to block on the poll() for the
+ duration of a dispatch. In this case we unblock (w/ sigprocmask)
+ the managed signals just before polling. Each managed signal is
+ handled by signal_handler() which send()'s the contents of siginfo
+ over the socketpair. Otherwise, we call poll() with a timeout of
+ 0ms so it checks the levels of the fd's in the pollset and returns
+ immediately. Any fd that is a socket and has no active state is
+ removed from the pollset for the next pass -- we will rely on
+ getting a signal for events on these fd's.
+
+ The receiving end of the siginfo socketpair is in the pollset
+ (permanently) so if we are polling on non-sockets, the delivery of
+ signals immediately following sigprocmask( SIG_UNBLOCK...) will
+ result in a readable op->signal_recv_fd which ensures the poll()
+ will return immediately. If the poll() call is blocking and a
+ signal arrives ( possibly a real-time signal from a socket not in
+ the pollset ) its handler will write the data to the socketpair
+ and interrupt the poll().
+
+ After every poll call we attempt a non-blocking recv from the
+ signal_recv_fd and continue to recv and dispatch the events until
+ recv indicates the socket buffer is empty.
+
+ One might raise concerns about receiving event activations from
+ both poll() and from the rtsig data in the signal_recv_fd.
+ Fortunately, libevent is already structured for event coalescing,
+ so this issue is mitigated ( though we do some work twice for the
+ same event making us less efficient ). I suspect that the cost of
+ turning off the O_ASYNC flag on fd's in the pollset is more
+ expensive than handling some events twice. Looking at the
+ kernel's source code for setting O_ASYNC, it looks like it takes a
+ global kernel lock...
+
+ After a poll and recv-loop for the signal_recv_fd, we finally do a
+ sigtimedwait(). sigtimedwait will only block if we haven't
+ blocked in poll() and we have not enqueued events from either the
+ poll or the recv-loop. Because sigtimedwait blocks all signals
+ that are not in the set of signals to be dequeued, we need to
+ dequeue almost all signals and make sure we dispatch them
+ correctly. We dequeue any signal that is not blocked as well as
+ all libevent-managed signals. If we get a signal that is not
+ managed by libevent we lookup the sigaction for the specific
+ signal and call that function ourselves.
+
+ Finally, I should mention that getting a SIGIO signal indicates
+ that the rtsig buffer has overflowed and we have lost events.
+ This forces us to add _every_ descriptor to the pollset to recover.
+
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Enable F_SETSIG and F_SETOWN */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+extern struct event_list signalqueue;
+
+#include <linux/unistd.h>
+#ifndef __NR_gettid
+#define gettid() getpid()
+#else
+
+#if ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
+_syscall0(pid_t,gettid)
+#endif
+
+#endif
+
+#define EVLIST_NONSOCK 0x1000 /* event is for a non-socket file-descriptor */
+#define EVLIST_DONTDEL 0x2000 /* event should always be in the pollset */
+#define MAXBUFFERSIZE (1024 * 1024 * 2) /* max socketbuffer for signal-spair */
+#define INIT_MAX 16 /* init/min # of fd positions in our pollset */
+
+static int signal_send_fd[_NSIG]; /* the globalend of the signal socketpair */
+static int trouble[_NSIG]; /* 1 when signal-handler cant send to signal_send_fd */
+
+struct rtdata;
+TAILQ_HEAD(rtdata_list, rtdata);
+
+struct rtsigop {
+ sigset_t sigs; /* signal mask for all _managed_ signals */
+ struct pollfd *poll; /* poll structures */
+ struct rtdata **ptodat; /* map poll_position to rtdata */
+ int cur; /* cur # fd's in a poll set */
+ int max; /* max # fd's in a poll set, start at 16 and grow as needed */
+ int total; /* count of fd's we are watching now */
+ int signo; /* the signo we use for ASYNC fd notifications */
+ int nonsock; /* number of non-socket fd's we are watching */
+ int highestfd; /* highest fd accomodated by fdtodat */
+ struct rtdata_list **fdtodat; /* map fd to rtdata ( and thus to event ) */
+ int signal_recv_fd; /* recv side of the signal_send_fd */
+ int signal_send_fd; /* recv side of the signal_send_fd */
+ struct event sigfdev; /* our own event structure for the signal fd */
+};
+
+struct rtdata {
+ /* rtdata holds rtsig-private state on each event */
+ TAILQ_ENTRY (rtdata) next;
+ struct event *ev;
+ int poll_position;
+};
+
+void *rtsig_init(void);
+int rtsig_add(void *, struct event *);
+int rtsig_del(void *, struct event *);
+int rtsig_recalc(struct event_base *, void *, int);
+int rtsig_dispatch(struct event_base *, void *, struct timeval *);
+
+struct eventop rtsigops = {
+ "rtsig",
+ rtsig_init,
+ rtsig_add,
+ rtsig_del,
+ rtsig_recalc,
+ rtsig_dispatch
+};
+
+static void
+signal_handler(int sig, siginfo_t *info, void *ctx)
+{
+ /*
+ * the signal handler for all libevent-managed signals only
+ * used if we need to do a blocking poll() call due to
+ * non-socket fd's in the pollset.
+ */
+
+ siginfo_t *i = info;
+ siginfo_t i_local;
+
+ if (trouble[sig - 1]) {
+ i_local.si_signo = SIGIO;
+ i_local.si_errno = 0;
+ i_local.si_code = 0;
+ i = &i_local;
+ trouble[sig - 1] = 0;
+ }
+
+ if (send(signal_send_fd[sig - 1], i, sizeof(*i),
+ MSG_DONTWAIT|MSG_NOSIGNAL) == -1)
+ trouble[sig - 1] = 1;
+}
+
+static void
+donothing(int fd, short event, void *arg)
+{
+ /*
+ * callback for our signal_recv_fd event structure
+ * we don't want to act on these events, we just want to wake the poll()
+ */
+};
+
+static void
+signotset(sigset_t *set)
+{
+ int i, l;
+ l = sizeof(*set) / 4;
+ for (i = 0; i < l; i++) {
+ ((unsigned *)set)[i] = ~((unsigned *)set)[i];
+ }
+}
+
+/* The next three functions manage our private data about each event struct */
+
+static int
+grow_fdset(struct rtsigop *op, int newhigh)
+{
+ /*
+ * grow the fd -> rtdata array because we have encountered a
+ * new fd too high to fit in the existing array
+ */
+
+ struct rtdata_list **p;
+ struct rtdata_list *datset;
+ int i,x;
+ int newcnt = (newhigh + 1) << 1;
+
+ if (newhigh <= op->highestfd)
+ return (0);
+
+ p = op->fdtodat;
+ p = realloc(op->fdtodat, sizeof(struct rtdata_list *) * newcnt);
+ if (p == NULL)
+ return (-1);
+ op->fdtodat = p;
+
+ datset = calloc(newcnt - (op->highestfd + 1),
+ sizeof(struct rtdata_list));
+ if (datset == NULL)
+ return (-1);
+
+ for (i = op->highestfd + 1, x = 0; i < newcnt; i++, x++) {
+ op->fdtodat[i] = &(datset[x]);
+ TAILQ_INIT(op->fdtodat[i]);
+ }
+
+ op->highestfd = newcnt - 1;
+ return (0);
+}
+
+static struct rtdata *
+ev2dat(struct rtsigop *op, struct event *ev, int create)
+{
+ /*
+ * given an event struct, find the dat structure that
+ * corresponds to it if create is non-zero and the rtdata
+ * structure does not exist, create it return NULL if not
+ * found
+ */
+
+ int found = 0;
+ int fd = ev->ev_fd;
+ struct rtdata *ret = NULL;
+
+ if (op->highestfd < fd && create)
+ if (grow_fdset(op, fd) == -1)
+ return (NULL);
+
+ TAILQ_FOREACH(ret, op->fdtodat[fd], next) {
+ if (ret->ev == ev) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (!create)
+ return (NULL);
+
+ ret = calloc(1, sizeof(struct rtdata));
+ if (ret == NULL)
+ return (NULL);
+ ret->ev = ev;
+ ret->poll_position = -1;
+ TAILQ_INSERT_TAIL(op->fdtodat[fd], ret, next);
+ }
+
+ return (ret);
+}
+
+static void
+dat_del(struct rtsigop *op, struct rtdata *dat)
+{
+ /*
+ * delete our private notes about a given event struct
+ * called from rtsig_del() only
+ */
+ int fd;
+ if (dat == NULL)
+ return;
+ fd = dat->ev->ev_fd;
+
+ TAILQ_REMOVE(op->fdtodat[fd], dat, next);
+ memset(dat, 0, sizeof(*dat));
+ free(dat);
+}
+
+
+static void
+set_sigaction(int sig)
+{
+ /*
+ * set the standard handler for any libevent-managed signal,
+ * including the rtsig used for O_ASYNC notifications
+ */
+ struct sigaction act;
+
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigfillset(&(act.sa_mask));
+ act.sa_sigaction = &signal_handler;
+ sigaction(sig, &act, NULL);
+}
+
+static int
+find_rt_signal()
+{
+ /* find an unused rtsignal */
+ struct sigaction act;
+ int sig = SIGRTMIN;
+
+ while (sig <= SIGRTMAX) {
+ if (sigaction(sig, NULL, &act) != 0) {
+ if (errno == EINTR)
+ continue;
+ } else {
+ if (act.sa_flags & SA_SIGINFO) {
+ if (act.sa_sigaction == NULL)
+ return (sig);
+ } else {
+ if (act.sa_handler == SIG_DFL)
+ return (sig);
+ }
+ }
+ sig++;
+ }
+ return (0);
+}
+
+/*
+ * the next three functions manage our pollset and the memory management for
+ * fd -> rtdata -> event -> poll_position maps
+ */
+
+static int
+poll_add(struct rtsigop *op, struct event *ev, struct rtdata *dat)
+{
+ struct pollfd *pfd;
+ int newmax = op->max << 1;
+ int pp;
+
+ if (op->poll == NULL)
+ return (0);
+
+ if (dat == NULL)
+ dat = ev2dat(op, ev, 0);
+
+ if (dat == NULL)
+ return (0);
+
+ pp = dat->poll_position;
+
+ if (pp != -1) {
+ pfd = &op->poll[pp];
+ if (ev->ev_events & EV_READ)
+ pfd->events |= POLLIN;
+
+ if (ev->ev_events & EV_WRITE)
+ pfd->events |= POLLOUT;
+
+ return (0);
+ }
+
+ if (op->cur == op->max) {
+ void *p = realloc(op->poll, sizeof(*op->poll) * newmax);
+ if (p == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ op->poll = p;
+
+ p = realloc(op->ptodat, sizeof(*op->ptodat) * newmax);
+ if (p == NULL) {
+ /* shrink the pollset back down */
+ op->poll = realloc(op->poll,
+ sizeof(*op->poll) * op->max);
+ errno = ENOMEM;
+ return (-1);
+ }
+ op->ptodat = p;
+ op->max = newmax;
+ }
+
+ pfd = &op->poll[op->cur];
+ pfd->fd = ev->ev_fd;
+ pfd->revents = 0;
+ pfd->events = 0;
+
+ if (ev->ev_events & EV_READ)
+ pfd->events |= POLLIN;
+
+ if (ev->ev_events & EV_WRITE)
+ pfd->events |= POLLOUT;
+
+ op->ptodat[op->cur] = dat;
+ dat->poll_position = op->cur;
+ op->cur++;
+
+ return (0);
+}
+
+static void
+poll_free(struct rtsigop *op, int n)
+{
+ if (op->poll == NULL)
+ return;
+
+ op->cur--;
+
+ if (n < op->cur) {
+ memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll));
+ op->ptodat[n] = op->ptodat[op->cur];
+ op->ptodat[n]->poll_position = n;
+ }
+
+
+ /* less then half the max in use causes us to shrink */
+ if (op->max > INIT_MAX && op->cur < op->max >> 1) {
+ op->max >>= 1;
+ op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
+ op->ptodat = realloc(op->ptodat, sizeof(*op->ptodat) * op->max);
+ }
+}
+
+static void
+poll_remove(struct rtsigop *op, struct event *ev, struct rtdata *dat)
+{
+ int pp;
+ if (dat == NULL)
+ dat = ev2dat(op, ev, 0);
+
+ if (dat == NULL) return;
+
+ pp = dat->poll_position;
+ if (pp != -1) {
+ poll_free(op, pp);
+ dat->poll_position = -1;
+ }
+}
+
+static void
+activate(struct event *ev, int flags)
+{
+ /* activate an event, possibly removing one-shot events */
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, flags, 1);
+}
+
+#define FD_CLOSEONEXEC(x) do { \
+ if (fcntl(x, F_SETFD, 1) == -1) \
+ event_warn("fcntl(%d, F_SETFD)", x); \
+} while (0)
+
+void *
+rtsig_init(void)
+{
+ struct rtsigop *op;
+ int sockets[2];
+ int optarg;
+ struct rtdata *dat;
+ int flags;
+
+ if (getenv("EVENT_NORTSIG"))
+ goto err;
+
+ op = calloc(1, sizeof(*op));
+ if (op == NULL)
+ goto err;
+
+ op->max = INIT_MAX;
+ op->poll = malloc(sizeof(*op->poll) * op->max);
+ if (op->poll == NULL)
+ goto err_free_op;
+
+ op->signo = find_rt_signal();
+ if (op->signo == 0)
+ goto err_free_poll;
+
+ op->nonsock = 0;
+
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sockets) != 0)
+ goto err_free_poll;
+
+ FD_CLOSEONEXEC(sockets[0]);
+ FD_CLOSEONEXEC(sockets[1]);
+
+ signal_send_fd[op->signo - 1] = sockets[0];
+ trouble[op->signo - 1] = 0;
+ op->signal_send_fd = sockets[0];
+ op->signal_recv_fd = sockets[1];
+ flags = fcntl(op->signal_recv_fd, F_GETFL);
+ fcntl(op->signal_recv_fd, F_SETFL, flags | O_NONBLOCK);
+
+ optarg = MAXBUFFERSIZE;
+ setsockopt(signal_send_fd[op->signo - 1],
+ SOL_SOCKET, SO_SNDBUF,
+ &optarg, sizeof(optarg));
+
+ optarg = MAXBUFFERSIZE;
+ setsockopt(op->signal_recv_fd,
+ SOL_SOCKET, SO_RCVBUF,
+ &optarg, sizeof(optarg));
+
+ op->highestfd = -1;
+ op->fdtodat = NULL;
+ if (grow_fdset(op, 1) == -1)
+ goto err_close_pair;
+
+ op->ptodat = malloc(sizeof(*op->ptodat) * op->max);
+ if (op->ptodat == NULL)
+ goto err_close_pair;
+
+ sigemptyset(&op->sigs);
+ sigaddset(&op->sigs, SIGIO);
+ sigaddset(&op->sigs, op->signo);
+ sigprocmask(SIG_BLOCK, &op->sigs, NULL);
+ set_sigaction(SIGIO);
+ set_sigaction(op->signo);
+
+ event_set(&(op->sigfdev), op->signal_recv_fd, EV_READ|EV_PERSIST,
+ donothing, NULL);
+ op->sigfdev.ev_flags |= EVLIST_DONTDEL;
+ dat = ev2dat(op, &(op->sigfdev), 1);
+ poll_add(op, &(op->sigfdev), dat);
+
+ return (op);
+
+ err_close_pair:
+ close(op->signal_recv_fd);
+ close(signal_send_fd[op->signo - 1]);
+
+ err_free_poll:
+ free(op->poll);
+
+ err_free_op:
+ free(op);
+ err:
+ return (NULL);
+}
+
+int
+rtsig_add(void *arg, struct event *ev)
+{
+ struct rtsigop *op = (struct rtsigop *) arg;
+ int flags, i;
+ struct stat statbuf;
+ struct rtdata *dat;
+
+ if (ev->ev_events & EV_SIGNAL) {
+ int signo = EVENT_SIGNAL(ev);
+
+ sigaddset(&op->sigs, EVENT_SIGNAL(ev));
+ if (sigprocmask(SIG_BLOCK, &op->sigs, NULL) == -1)
+ return (-1);
+
+ set_sigaction(signo);
+
+ signal_send_fd[signo - 1] = op->signal_send_fd;
+ trouble[signo - 1] = 0;
+
+ return (0);
+ }
+
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ if (-1 == fstat(ev->ev_fd, &statbuf))
+ return (-1);
+
+ if (!S_ISSOCK(statbuf.st_mode))
+ ev->ev_flags |= EVLIST_NONSOCK;
+
+ flags = fcntl(ev->ev_fd, F_GETFL);
+ if (flags == -1)
+ return (-1);
+
+ if (!(flags & O_ASYNC)) {
+ if (fcntl(ev->ev_fd, F_SETSIG, op->signo) == -1 ||
+ fcntl(ev->ev_fd, F_SETOWN, (int) gettid()) == -1)
+ return (-1);
+
+ /*
+ * the overhead of always handling writeable edges
+ * isn't going to be that bad...
+ */
+ if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC|O_RDWR))
+ return (-1);
+ }
+
+#ifdef O_ONESIGFD
+ /*
+ * F_SETAUXFL and O_ONESIGFD are defined in a non-standard
+ * linux kernel patch to coalesce events for fds
+ */
+ fcntl(ev->ev_fd, F_SETAUXFL, O_ONESIGFD);
+#endif
+
+ dat = ev2dat(op, ev, 1);
+ if (dat == NULL)
+ return (-1);
+
+ op->total++;
+ if (ev->ev_flags & EVLIST_NONSOCK)
+ op->nonsock++;
+
+ if (poll_add(op, ev, dat) == -1) {
+ /* must check the level of new fd's */
+ i = errno;
+ fcntl(ev->ev_fd, F_SETFL, flags);
+ errno = i;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+rtsig_del(void *arg, struct event *ev)
+{
+ struct rtdata *dat;
+ struct rtsigop *op = (struct rtsigop *) arg;
+
+ if (ev->ev_events & EV_SIGNAL) {
+ sigset_t sigs;
+
+ sigdelset(&op->sigs, EVENT_SIGNAL(ev));
+
+ sigemptyset(&sigs);
+ sigaddset(&sigs, EVENT_SIGNAL(ev));
+ return (sigprocmask(SIG_UNBLOCK, &sigs, NULL));
+ }
+
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ dat = ev2dat(op, ev, 0);
+ poll_remove(op, ev, dat);
+ dat_del(op, dat);
+ op->total--;
+ if (ev->ev_flags & EVLIST_NONSOCK)
+ op->nonsock--;
+
+ return (0);
+}
+
+int
+rtsig_recalc(struct event_base *base, void *arg, int max)
+{
+ return (0);
+}
+
+/*
+ * the following do_X functions implement the different stages of a single
+ * eventloop pass: poll(), recv(sigsock), sigtimedwait()
+ *
+ * do_siginfo_dispatch() is a common factor to both do_sigwait() and
+ * do_signals_from_socket().
+ */
+
+static inline int
+do_poll(struct rtsigop *op, struct timespec *ts)
+{
+ int res = 0;
+ int i = 0;
+
+ if (op->cur > 1) {
+ /* non-empty poll set (modulo the signalfd) */
+ if (op->nonsock) {
+ int timeout = ts->tv_nsec / 1000000 + ts->tv_sec * 1000;
+
+ sigprocmask(SIG_UNBLOCK, &(op->sigs), NULL);
+
+ res = poll(op->poll, op->cur, timeout);
+
+ sigprocmask(SIG_BLOCK, &(op->sigs), NULL);
+
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ } else {
+ res = poll(op->poll, op->cur, 0);
+ }
+
+ if (res < 0) {
+ return (errno == EINTR ? 0 : -1);
+ } else if (res) {
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ }
+
+ i = 0;
+ while (i < op->cur) {
+ struct rtdata *dat = op->ptodat[i];
+ struct event *ev = dat->ev;
+
+ if (op->poll[i].revents) {
+ int flags = 0;
+
+ if (op->poll[i].revents & (POLLIN | POLLERR))
+ flags |= EV_READ;
+
+ if (op->poll[i].revents & POLLOUT)
+ flags |= EV_WRITE;
+
+ if (!(ev->ev_events & EV_PERSIST)) {
+ poll_remove(op, ev, op->ptodat[i]);
+ event_del(ev);
+ } else {
+ i++;
+ }
+
+ event_active(ev, flags, 1);
+ } else {
+ if (ev->ev_flags & (EVLIST_NONSOCK|EVLIST_DONTDEL)) {
+ i++;
+ } else {
+ poll_remove(op, ev, op->ptodat[i]);
+ }
+ }
+ }
+ }
+ return (res);
+}
+
+static inline int
+do_siginfo_dispatch(struct event_base *base, struct rtsigop *op,
+ siginfo_t *info)
+{
+ int signum;
+ struct rtdata *dat, *next_dat;
+ struct event *ev, *next_ev;
+
+ if (info == NULL)
+ return (-1);
+
+ signum = info->si_signo;
+ if (signum == op->signo) {
+ int flags, sigok = 0;
+ flags = 0;
+
+ if (info->si_band & (POLLIN|POLLERR))
+ flags |= EV_READ;
+ if (info->si_band & POLLOUT)
+ flags |= EV_WRITE;
+
+ if (!flags)
+ return (0);
+
+ if (info->si_fd > op->highestfd)
+ return (-1);
+
+ dat = TAILQ_FIRST(op->fdtodat[info->si_fd]);
+ while (dat != TAILQ_END(op->fdtodat[info->si_fd])) {
+ next_dat = TAILQ_NEXT(dat, next);
+ if (flags & dat->ev->ev_events) {
+ ev = dat->ev;
+ poll_add(op, ev, dat);
+ activate(ev, flags & ev->ev_events);
+ sigok = 1;
+ }
+ dat = next_dat;
+ }
+ } else if (signum == SIGIO) {
+ TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
+ if (ev->ev_events & (EV_READ|EV_WRITE))
+ poll_add(op, ev, NULL);
+ }
+ return (1); /* 1 means the caller should poll() again */
+
+ } else if (sigismember(&op->sigs, signum)) {
+ /* managed signals are queued */
+ ev = TAILQ_FIRST(&signalqueue);
+ while (ev != TAILQ_END(&signalqueue)) {
+ next_ev = TAILQ_NEXT(ev, ev_signal_next);
+ if (EVENT_SIGNAL(ev) == signum)
+ activate(ev, EV_SIGNAL);
+ ev = next_ev;
+ }
+ } else {
+ /* dispatch unmanaged signals immediately */
+ struct sigaction sa;
+ if (sigaction(signum, NULL, &sa) == 0) {
+ if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction) {
+ (*sa.sa_sigaction)(signum, info, NULL);
+ } else if (sa.sa_handler) {
+ if ((int)sa.sa_handler != 1)
+ (*sa.sa_handler)(signum);
+ } else {
+ if (signum != SIGCHLD) {
+ /* non-blocked SIG_DFL */
+ kill(gettid(), signum);
+ }
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * return 1 if we should poll again
+ * return 0 if we are all set
+ * return -1 on error
+ */
+static inline int
+do_sigwait(struct event_base *base, struct rtsigop *op, struct timespec *ts,
+ sigset_t *sigs)
+{
+ for (;;) {
+ siginfo_t info;
+ int signum;
+
+ signum = sigtimedwait(sigs, &info, ts);
+
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+
+ if (signum == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return (0);
+ return (-1);
+ } else if (1 == do_siginfo_dispatch(base, op, &info)) {
+ return (1);
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+static inline int
+do_signals_from_socket(struct event_base *base, struct rtsigop *op,
+ struct timespec *ts)
+{
+ int fd = op->signal_recv_fd;
+ siginfo_t info;
+ int res;
+
+ for (;;) {
+ res = recv(fd, &info, sizeof(info), MSG_NOSIGNAL);
+ if (res == -1) {
+ if (errno == EAGAIN)
+ return (0);
+ if (errno == EINTR)
+ continue;
+ return (-1);
+ } else {
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ if (1 == do_siginfo_dispatch(base, op, &info))
+ return (1);
+ }
+ }
+ /* NOTREACHED */
+}
+
+int
+rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ struct rtsigop *op = (struct rtsigop *) arg;
+ struct timespec ts;
+ int res;
+ sigset_t sigs;
+
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec * 1000;
+
+ poll_for_level:
+ res = do_poll(op, &ts); /* ts can be modified in do_XXX() */
+
+ res = do_signals_from_socket(base, op, &ts);
+ if (res == 1)
+ goto poll_for_level;
+ else if (res == -1)
+ return (-1);
+
+ /*
+ * the mask = managed_signals | unblocked-signals
+ * MM - if this is not blocking do we need to cast the net this wide?
+ */
+ sigemptyset(&sigs);
+ sigprocmask(SIG_BLOCK, &sigs, &sigs);
+ signotset(&sigs);
+ sigorset(&sigs, &sigs, &op->sigs);
+
+ res = do_sigwait(base, op, &ts, &sigs);
+
+ if (res == 1)
+ goto poll_for_level;
+ else if (res == -1)
+ return (-1);
+
+ return (0);
+}
+
diff --git a/sample/Makefile.am b/sample/Makefile.am
new file mode 100644
index 00000000..b749b806
--- /dev/null
+++ b/sample/Makefile.am
@@ -0,0 +1,15 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+LDADD = ../libevent.la
+CPPFPLAGS = -I..
+CFLAGS = -I../compat
+
+noinst_PROGRAMS = event-test time-test signal-test
+
+event_test_sources = event-test.c
+time_test_sources = time-test.c
+signal_test_sources = signal-test.c
+
+verify:
+
+DISTCLEANFILES = *~
diff --git a/sample/event-test.c b/sample/event-test.c
new file mode 100644
index 00000000..965984d4
--- /dev/null
+++ b/sample/event-test.c
@@ -0,0 +1,135 @@
+/*
+ * 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>
+#ifndef WIN32
+#include <sys/queue.h>
+#include <unistd.h>
+#include <sys/time.h>
+#else
+#include <windows.h>
+#endif
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.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;
+#ifdef WIN32
+ DWORD dwBytesRead;
+#endif
+
+ /* Reschedule this event */
+ event_add(ev, NULL);
+
+ fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
+ fd, event, arg);
+#ifdef WIN32
+ len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL);
+
+ // Check for end of file.
+ if(len && dwBytesRead == 0) {
+ fprintf(stderr, "End Of File");
+ event_del(ev);
+ return;
+ }
+
+ buf[dwBytesRead] = '\0';
+#else
+ 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';
+#endif
+ fprintf(stdout, "Read: %s\n", buf);
+}
+
+int
+main (int argc, char **argv)
+{
+ struct event evfifo;
+#ifdef WIN32
+ HANDLE socket;
+ // Open a file.
+ socket = CreateFile("test.txt", // open File
+ GENERIC_READ, // open for reading
+ 0, // do not share
+ NULL, // no security
+ OPEN_EXISTING, // existing file only
+ FILE_ATTRIBUTE_NORMAL, // normal file
+ NULL); // no attr. template
+
+ if(socket == INVALID_HANDLE_VALUE)
+ return 1;
+
+#else
+ struct stat st;
+ char *fifo = "event.fifo";
+ int socket;
+
+ 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);
+#endif
+ /* Initalize the event library */
+ event_init();
+
+ /* Initalize one event */
+#ifdef WIN32
+ event_set(&evfifo, (int)socket, EV_READ, fifo_read, &evfifo);
+#else
+ event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
+#endif
+
+ /* Add it to the active events, without a timeout */
+ event_add(&evfifo, NULL);
+
+ event_dispatch();
+#ifdef WIN32
+ CloseHandle(socket);
+#endif
+ return (0);
+}
+
diff --git a/sample/signal-test.c b/sample/signal-test.c
new file mode 100644
index 00000000..98650ca1
--- /dev/null
+++ b/sample/signal-test.c
@@ -0,0 +1,62 @@
+/*
+ * Compile with:
+ * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
+ */
+
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/stat.h>
+#ifndef WIN32
+#include <sys/queue.h>
+#include <unistd.h>
+#include <sys/time.h>
+#else
+#include <windows.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <event.h>
+
+int called = 0;
+
+void
+signal_cb(int fd, short event, void *arg)
+{
+ struct event *signal = arg;
+
+ printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal));
+
+ if (called >= 2)
+ event_del(signal);
+
+ called++;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct event signal_int;
+
+ /* Initalize the event library */
+ event_init();
+
+ /* Initalize one event */
+ event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
+ &signal_int);
+
+ event_add(&signal_int, NULL);
+
+ event_dispatch();
+
+ return (0);
+}
+
diff --git a/sample/time-test.c b/sample/time-test.c
new file mode 100644
index 00000000..f0f93995
--- /dev/null
+++ b/sample/time-test.c
@@ -0,0 +1,70 @@
+/*
+ * Compile with:
+ * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
+ */
+
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/stat.h>
+#ifndef WIN32
+#include <sys/queue.h>
+#include <unistd.h>
+#else
+#include <time.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.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", __func__, 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 */
+ evtimer_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
index 00000000..1e039e1f
--- /dev/null
+++ b/select.c
@@ -0,0 +1,376 @@
+/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "log.h"
+
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+
+extern volatile sig_atomic_t evsignal_caught;
+
+struct selectop {
+ int event_fds; /* Highest fd in fd set */
+ int event_fdsz;
+ fd_set *event_readset_in;
+ fd_set *event_writeset_in;
+ fd_set *event_readset_out;
+ fd_set *event_writeset_out;
+ struct event **event_r_by_fd;
+ struct event **event_w_by_fd;
+ sigset_t evsigmask;
+};
+
+void *select_init (void);
+int select_add (void *, struct event *);
+int select_del (void *, struct event *);
+int select_recalc (struct event_base *, void *, int);
+int select_dispatch (struct event_base *, void *, struct timeval *);
+void select_dealloc (void *);
+
+const struct eventop selectops = {
+ "select",
+ select_init,
+ select_add,
+ select_del,
+ select_recalc,
+ select_dispatch,
+ select_dealloc
+};
+
+static int select_resize(struct selectop *sop, int fdsz);
+
+void *
+select_init(void)
+{
+ struct selectop *sop;
+
+ /* Disable select when this environment variable is set */
+ if (getenv("EVENT_NOSELECT"))
+ return (NULL);
+
+ if (!(sop = calloc(1, sizeof(struct selectop))))
+ return (NULL);
+
+ select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask));
+
+ evsignal_init(&sop->evsigmask);
+
+ return (sop);
+}
+
+#ifdef CHECK_INVARIANTS
+static void
+check_selectop(struct selectop *sop)
+{
+ int i;
+ for (i=0;i<=sop->event_fds;++i) {
+ if (FD_ISSET(i, sop->event_readset_in)) {
+ assert(sop->event_r_by_fd[i]);
+ assert(sop->event_r_by_fd[i]->ev_events & EV_READ);
+ assert(sop->event_r_by_fd[i]->ev_fd == i);
+ } else {
+ assert(! sop->event_r_by_fd[i]);
+ }
+ if (FD_ISSET(i, sop->event_writeset_in)) {
+ assert(sop->event_w_by_fd[i]);
+ assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE);
+ assert(sop->event_w_by_fd[i]->ev_fd == i);
+ } else {
+ assert(! sop->event_w_by_fd[i]);
+ }
+ }
+
+}
+#else
+#define check_selectop(sop) do {;} while (0)
+#endif
+
+/*
+ * Called with the highest fd that we know about. If it is 0, completely
+ * recalculate everything.
+ */
+
+int
+select_recalc(struct event_base *base, void *arg, int max)
+{
+ struct selectop *sop = arg;
+
+ check_selectop(sop);
+
+ return (evsignal_recalc(&sop->evsigmask));
+}
+
+int
+select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ int res, i;
+ struct selectop *sop = arg;
+
+ check_selectop(sop);
+
+ memcpy(sop->event_readset_out, sop->event_readset_in,
+ sop->event_fdsz);
+ memcpy(sop->event_writeset_out, sop->event_writeset_in,
+ sop->event_fdsz);
+
+ if (evsignal_deliver(&sop->evsigmask) == -1)
+ return (-1);
+
+ res = select(sop->event_fds + 1, sop->event_readset_out,
+ sop->event_writeset_out, NULL, tv);
+
+ check_selectop(sop);
+ if (evsignal_recalc(&sop->evsigmask) == -1)
+ return (-1);
+
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("select");
+ return (-1);
+ }
+
+ evsignal_process();
+ return (0);
+ } else if (evsignal_caught)
+ evsignal_process();
+
+ event_debug(("%s: select reports %d", __func__, res));
+
+ check_selectop(sop);
+ for (i = 0; i <= sop->event_fds; ++i) {
+ struct event *r_ev = NULL, *w_ev = NULL;
+ res = 0;
+ if (FD_ISSET(i, sop->event_readset_out)) {
+ r_ev = sop->event_r_by_fd[i];
+ res |= EV_READ;
+ }
+ if (FD_ISSET(i, sop->event_writeset_out)) {
+ w_ev = sop->event_w_by_fd[i];
+ res |= EV_WRITE;
+ }
+ if (r_ev && (res & r_ev->ev_events)) {
+ if (!(r_ev->ev_events & EV_PERSIST))
+ event_del(r_ev);
+ event_active(r_ev, res & r_ev->ev_events, 1);
+ }
+ if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
+ if (!(w_ev->ev_events & EV_PERSIST))
+ event_del(w_ev);
+ event_active(w_ev, res & w_ev->ev_events, 1);
+ }
+ }
+ check_selectop(sop);
+
+ return (0);
+}
+
+
+static int
+select_resize(struct selectop *sop, int fdsz)
+{
+ int n_events, n_events_old;
+
+ fd_set *readset_in = NULL;
+ fd_set *writeset_in = NULL;
+ fd_set *readset_out = NULL;
+ fd_set *writeset_out = NULL;
+ struct event **r_by_fd = NULL;
+ struct event **w_by_fd = NULL;
+
+ n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
+ n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
+
+ if (sop->event_readset_in)
+ check_selectop(sop);
+
+ if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
+ goto error;
+ sop->event_readset_in = readset_in;
+ if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
+ goto error;
+ sop->event_readset_out = readset_out;
+ if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
+ goto error;
+ sop->event_writeset_in = writeset_in;
+ if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
+ goto error;
+ sop->event_writeset_out = writeset_out;
+ if ((r_by_fd = realloc(sop->event_r_by_fd,
+ n_events*sizeof(struct event*))) == NULL)
+ goto error;
+ sop->event_r_by_fd = r_by_fd;
+ if ((w_by_fd = realloc(sop->event_w_by_fd,
+ n_events * sizeof(struct event*))) == NULL)
+ goto error;
+ sop->event_w_by_fd = w_by_fd;
+
+ memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
+ fdsz - sop->event_fdsz);
+ memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
+ fdsz - sop->event_fdsz);
+ memset(sop->event_r_by_fd + n_events_old, 0,
+ (n_events-n_events_old) * sizeof(struct event*));
+ memset(sop->event_w_by_fd + n_events_old, 0,
+ (n_events-n_events_old) * sizeof(struct event*));
+
+ sop->event_fdsz = fdsz;
+ check_selectop(sop);
+
+ return (0);
+
+ error:
+ event_warn("malloc");
+ return (-1);
+}
+
+
+int
+select_add(void *arg, struct event *ev)
+{
+ struct selectop *sop = arg;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(&sop->evsigmask, ev));
+
+ check_selectop(sop);
+ /*
+ * 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) {
+ int fdsz = sop->event_fdsz;
+
+ if (fdsz < sizeof(fd_mask))
+ fdsz = sizeof(fd_mask);
+
+ while (fdsz <
+ (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
+ fdsz *= 2;
+
+ if (fdsz != sop->event_fdsz) {
+ if (select_resize(sop, fdsz)) {
+ check_selectop(sop);
+ return (-1);
+ }
+ }
+
+ sop->event_fds = ev->ev_fd;
+ }
+
+ if (ev->ev_events & EV_READ) {
+ FD_SET(ev->ev_fd, sop->event_readset_in);
+ sop->event_r_by_fd[ev->ev_fd] = ev;
+ }
+ if (ev->ev_events & EV_WRITE) {
+ FD_SET(ev->ev_fd, sop->event_writeset_in);
+ sop->event_w_by_fd[ev->ev_fd] = ev;
+ }
+ check_selectop(sop);
+
+ return (0);
+}
+
+/*
+ * Nothing to be done here.
+ */
+
+int
+select_del(void *arg, struct event *ev)
+{
+ struct selectop *sop = arg;
+
+ check_selectop(sop);
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(&sop->evsigmask, ev));
+
+ if (sop->event_fds < ev->ev_fd) {
+ check_selectop(sop);
+ return (0);
+ }
+
+ if (ev->ev_events & EV_READ) {
+ FD_CLR(ev->ev_fd, sop->event_readset_in);
+ sop->event_r_by_fd[ev->ev_fd] = NULL;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ FD_CLR(ev->ev_fd, sop->event_writeset_in);
+ sop->event_w_by_fd[ev->ev_fd] = NULL;
+ }
+
+ check_selectop(sop);
+ return (0);
+}
+
+void
+select_dealloc(void *arg)
+{
+ struct selectop *sop = arg;
+
+ if (sop->event_readset_in)
+ free(sop->event_readset_in);
+ if (sop->event_writeset_in)
+ free(sop->event_writeset_in);
+ if (sop->event_readset_out)
+ free(sop->event_readset_out);
+ if (sop->event_writeset_out)
+ free(sop->event_writeset_out);
+ if (sop->event_r_by_fd)
+ free(sop->event_r_by_fd);
+ if (sop->event_w_by_fd)
+ free(sop->event_w_by_fd);
+
+ memset(sop, 0, sizeof(struct selectop));
+ free(sop);
+}
diff --git a/signal.c b/signal.c
new file mode 100644
index 00000000..8a6963b1
--- /dev/null
+++ b/signal.c
@@ -0,0 +1,212 @@
+/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * 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. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "event.h"
+#include "evsignal.h"
+#include "log.h"
+
+extern struct event_list signalqueue;
+
+static sig_atomic_t evsigcaught[NSIG];
+static int needrecalc;
+volatile sig_atomic_t evsignal_caught = 0;
+
+static struct event ev_signal;
+static int ev_signal_pair[2];
+static int ev_signal_added;
+
+/* Callback for when the signal handler write a byte to our signaling socket */
+static void
+evsignal_cb(int fd, short what, void *arg)
+{
+ static char signals[100];
+ struct event *ev = arg;
+ ssize_t n;
+
+ n = read(fd, signals, sizeof(signals));
+ if (n == -1)
+ event_err(1, "%s: read", __func__);
+ event_add(ev, NULL);
+}
+
+#ifdef HAVE_SETFD
+#define FD_CLOSEONEXEC(x) do { \
+ if (fcntl(x, F_SETFD, 1) == -1) \
+ event_warn("fcntl(%d, F_SETFD)", x); \
+} while (0)
+#else
+#define FD_CLOSEONEXEC(x)
+#endif
+
+void
+evsignal_init(sigset_t *evsigmask)
+{
+ sigemptyset(evsigmask);
+
+ /*
+ * Our signal handler is going to write to one end of the socket
+ * pair to wake up our event loop. The event loop then scans for
+ * signals that got delivered.
+ */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ev_signal_pair) == -1)
+ event_err(1, "%s: socketpair", __func__);
+
+ FD_CLOSEONEXEC(ev_signal_pair[0]);
+ FD_CLOSEONEXEC(ev_signal_pair[1]);
+
+ fcntl(ev_signal_pair[0], F_SETFL, O_NONBLOCK);
+
+ event_set(&ev_signal, ev_signal_pair[1], EV_READ,
+ evsignal_cb, &ev_signal);
+ ev_signal.ev_flags |= EVLIST_INTERNAL;
+}
+
+int
+evsignal_add(sigset_t *evsigmask, struct event *ev)
+{
+ int evsignal;
+
+ if (ev->ev_events & (EV_READ|EV_WRITE))
+ event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
+ evsignal = EVENT_SIGNAL(ev);
+ sigaddset(evsigmask, evsignal);
+
+ return (0);
+}
+
+/*
+ * Nothing to be done here.
+ */
+
+int
+evsignal_del(sigset_t *evsigmask, struct event *ev)
+{
+ int evsignal;
+
+ evsignal = EVENT_SIGNAL(ev);
+ sigdelset(evsigmask, evsignal);
+ needrecalc = 1;
+
+ return (sigaction(EVENT_SIGNAL(ev),(struct sigaction *)SIG_DFL, NULL));
+}
+
+static void
+evsignal_handler(int sig)
+{
+ int save_errno = errno;
+
+ evsigcaught[sig]++;
+ evsignal_caught = 1;
+
+ /* Wake up our notification mechanism */
+ write(ev_signal_pair[0], "a", 1);
+ errno = save_errno;
+}
+
+int
+evsignal_recalc(sigset_t *evsigmask)
+{
+ struct sigaction sa;
+ struct event *ev;
+
+ if (!ev_signal_added) {
+ ev_signal_added = 1;
+ event_add(&ev_signal, NULL);
+ }
+
+ if (TAILQ_FIRST(&signalqueue) == NULL && !needrecalc)
+ return (0);
+ needrecalc = 0;
+
+ if (sigprocmask(SIG_BLOCK, evsigmask, NULL) == -1)
+ return (-1);
+
+ /* Reinstall our signal handler. */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = evsignal_handler;
+ sa.sa_mask = *evsigmask;
+ sa.sa_flags |= SA_RESTART;
+
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+ if (sigaction(EVENT_SIGNAL(ev), &sa, NULL) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+int
+evsignal_deliver(sigset_t *evsigmask)
+{
+ if (TAILQ_FIRST(&signalqueue) == NULL)
+ return (0);
+
+ return (sigprocmask(SIG_UNBLOCK, evsigmask, NULL));
+ /* XXX - pending signals handled here */
+}
+
+void
+evsignal_process(void)
+{
+ struct event *ev;
+ sig_atomic_t ncalls;
+
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+ ncalls = evsigcaught[EVENT_SIGNAL(ev)];
+ if (ncalls) {
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, EV_SIGNAL, ncalls);
+ }
+ }
+
+ memset(evsigcaught, 0, sizeof(evsigcaught));
+ evsignal_caught = 0;
+}
+
diff --git a/stamp-h.in b/stamp-h.in
new file mode 100644
index 00000000..9788f702
--- /dev/null
+++ b/stamp-h.in
@@ -0,0 +1 @@
+timestamp
diff --git a/strlcpy.c b/strlcpy.c
new file mode 100644
index 00000000..163f4258
--- /dev/null
+++ b/strlcpy.c
@@ -0,0 +1,74 @@
+/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 00000000..66f0e311
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,31 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+LDADD = ../libevent.la
+CPPFPLAGS = -I..
+CFLAGS = -I../compat @CFLAGS@
+
+EXTRA_DIST = regress.rpc
+
+noinst_PROGRAMS = test-init test-eof test-weof test-time regress bench
+
+BUILT_SOURCES = regress.gen.c regress.gen.h
+test_init_SOURCES = test-init.c
+test_eof_SOURCES = test-eof.c
+test_weof_SOURCES = test-weof.c
+test_time_SOURCES = test-time.c
+regress_SOURCES = regress.c regress.h regress_http.c regress_dns.c \
+ regress.gen.c regress.gen.h
+bench_SOURCES = bench.c
+
+regress.gen.c regress.gen.h: regress.rpc
+ ../event_rpcgen.py regress.rpc || echo "No Python installed"
+
+DISTCLEANFILES = *~
+CLEANFILES = regress.gen.h regress.gen.c
+
+test: test-init test-eof test-weof test-time regress
+
+verify: test
+ @./test.sh
+
+bench test-init test-eof test-weof test-time: ../libevent.la
diff --git a/test/bench.c b/test/bench.c
new file mode 100644
index 00000000..61b05025
--- /dev/null
+++ b/test/bench.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2003 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.
+ * 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.
+ *
+ *
+ * Mon 03/10/2003 - Modified by Davide Libenzi <davidel@xmailserver.org>
+ *
+ * Added chain event propagation to improve the sensitivity of
+ * the measure respect to the event loop efficency.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event.h>
+
+
+static int count, writes, fired;
+static int *pipes;
+static int num_pipes, num_active, num_writes;
+static struct event *events;
+
+
+
+void
+read_cb(int fd, short which, void *arg)
+{
+ int idx = (int) arg, widx = idx + 1;
+ u_char ch;
+
+ count += read(fd, &ch, sizeof(ch));
+ if (writes) {
+ if (widx >= num_pipes)
+ widx -= num_pipes;
+ write(pipes[2 * widx + 1], "e", 1);
+ writes--;
+ fired++;
+ }
+}
+
+struct timeval *
+run_once(void)
+{
+ int *cp, i, space;
+ static struct timeval ts, te;
+
+ for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
+ event_del(&events[i]);
+ event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *) i);
+ event_add(&events[i], NULL);
+ }
+
+ event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
+
+ fired = 0;
+ space = num_pipes / num_active;
+ space = space * 2;
+ for (i = 0; i < num_active; i++, fired++)
+ write(pipes[i * space + 1], "e", 1);
+
+ count = 0;
+ writes = num_writes;
+ { int xcount = 0;
+ gettimeofday(&ts, NULL);
+ do {
+ event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
+ xcount++;
+ } while (count != fired);
+ gettimeofday(&te, NULL);
+
+ if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
+ }
+
+ timersub(&te, &ts, &te);
+
+ return (&te);
+}
+
+int
+main (int argc, char **argv)
+{
+ struct rlimit rl;
+ int i, c;
+ struct timeval *tv;
+ int *cp;
+ extern char *optarg;
+
+ num_pipes = 100;
+ num_active = 1;
+ num_writes = num_pipes;
+ while ((c = getopt(argc, argv, "n:a:w:")) != -1) {
+ switch (c) {
+ case 'n':
+ num_pipes = atoi(optarg);
+ break;
+ case 'a':
+ num_active = atoi(optarg);
+ break;
+ case 'w':
+ num_writes = atoi(optarg);
+ break;
+ default:
+ fprintf(stderr, "Illegal argument \"%c\"\n", c);
+ exit(1);
+ }
+ }
+
+ rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
+ perror("setrlimit");
+ exit(1);
+ }
+
+ events = calloc(num_pipes, sizeof(struct event));
+ pipes = calloc(num_pipes * 2, sizeof(int));
+ if (events == NULL || pipes == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+
+ event_init();
+
+ for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
+#ifdef USE_PIPES
+ if (pipe(cp) == -1) {
+#else
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) {
+#endif
+ perror("pipe");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < 25; i++) {
+ tv = run_once();
+ if (tv == NULL)
+ exit(1);
+ fprintf(stdout, "%ld\n",
+ tv->tv_sec * 1000000L + tv->tv_usec);
+ }
+
+ exit(0);
+}
diff --git a/test/regress.c b/test/regress.c
new file mode 100644
index 00000000..d0f135c0
--- /dev/null
+++ b/test/regress.c
@@ -0,0 +1,923 @@
+/*
+ * Copyright (c) 2003, 2004 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. 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.
+ */
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "event.h"
+#include "log.h"
+
+#include "regress.h"
+#include "regress.gen.h"
+
+int pair[2];
+int test_ok;
+static int called;
+static char wbuf[4096];
+static char rbuf[4096];
+static int woff;
+static int roff;
+static int usepersist;
+static struct timeval tset;
+static struct timeval tcalled;
+static struct event_base *event_base;
+
+#define TEST1 "this is a test"
+#define SECONDS 1
+
+void
+simple_read_cb(int fd, short event, void *arg)
+{
+ char buf[256];
+ int len;
+
+ len = read(fd, buf, sizeof(buf));
+
+ if (len) {
+ if (!called) {
+ if (event_add(arg, NULL) == -1)
+ exit(1);
+ }
+ } else if (called == 1)
+ test_ok = 1;
+
+ called++;
+}
+
+void
+simple_write_cb(int fd, short event, void *arg)
+{
+ int len;
+
+ len = write(fd, TEST1, strlen(TEST1) + 1);
+ if (len == -1)
+ test_ok = 0;
+ else
+ test_ok = 1;
+}
+
+void
+multiple_write_cb(int fd, short event, void *arg)
+{
+ struct event *ev = arg;
+ int len;
+
+ len = 128;
+ if (woff + len >= sizeof(wbuf))
+ len = sizeof(wbuf) - woff;
+
+ len = write(fd, wbuf + woff, len);
+ if (len == -1) {
+ fprintf(stderr, "%s: write\n", __func__);
+ if (usepersist)
+ event_del(ev);
+ return;
+ }
+
+ woff += len;
+
+ if (woff >= sizeof(wbuf)) {
+ shutdown(fd, SHUT_WR);
+ if (usepersist)
+ event_del(ev);
+ return;
+ }
+
+ if (!usepersist) {
+ if (event_add(ev, NULL) == -1)
+ exit(1);
+ }
+}
+
+void
+multiple_read_cb(int fd, short event, void *arg)
+{
+ struct event *ev = arg;
+ int len;
+
+ len = read(fd, rbuf + roff, sizeof(rbuf) - roff);
+ if (len == -1)
+ fprintf(stderr, "%s: read\n", __func__);
+ if (len <= 0) {
+ if (usepersist)
+ event_del(ev);
+ return;
+ }
+
+ roff += len;
+ if (!usepersist) {
+ if (event_add(ev, NULL) == -1)
+ exit(1);
+ }
+}
+
+void
+timeout_cb(int fd, short event, void *arg)
+{
+ struct timeval tv;
+ int diff;
+
+ gettimeofday(&tcalled, NULL);
+ if (timercmp(&tcalled, &tset, >))
+ timersub(&tcalled, &tset, &tv);
+ else
+ timersub(&tset, &tcalled, &tv);
+
+ diff = tv.tv_sec*1000 + tv.tv_usec/1000 - SECONDS * 1000;
+ if (diff < 0)
+ diff = -diff;
+
+ if (diff < 100)
+ test_ok = 1;
+}
+
+void
+signal_cb(int fd, short event, void *arg)
+{
+ struct event *ev = arg;
+
+ signal_del(ev);
+ test_ok = 1;
+}
+
+struct both {
+ struct event ev;
+ int nread;
+};
+
+void
+combined_read_cb(int fd, short event, void *arg)
+{
+ struct both *both = arg;
+ char buf[128];
+ int len;
+
+ len = read(fd, buf, sizeof(buf));
+ if (len == -1)
+ fprintf(stderr, "%s: read\n", __func__);
+ if (len <= 0)
+ return;
+
+ both->nread += len;
+ if (event_add(&both->ev, NULL) == -1)
+ exit(1);
+}
+
+void
+combined_write_cb(int fd, short event, void *arg)
+{
+ struct both *both = arg;
+ char buf[128];
+ int len;
+
+ len = sizeof(buf);
+ if (len > both->nread)
+ len = both->nread;
+
+ len = write(fd, buf, len);
+ if (len == -1)
+ fprintf(stderr, "%s: write\n", __func__);
+ if (len <= 0) {
+ shutdown(fd, SHUT_WR);
+ return;
+ }
+
+ both->nread -= len;
+ if (event_add(&both->ev, NULL) == -1)
+ exit(1);
+}
+
+/* Test infrastructure */
+
+int
+setup_test(char *name)
+{
+
+ fprintf(stdout, "%s", name);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
+ fprintf(stderr, "%s: socketpair\n", __func__);
+ exit(1);
+ }
+
+#ifdef HAVE_FCNTL
+ if (fcntl(pair[0], F_SETFL, O_NONBLOCK) == -1)
+ fprintf(stderr, "fcntl(O_NONBLOCK)");
+
+ if (fcntl(pair[1], F_SETFL, O_NONBLOCK) == -1)
+ fprintf(stderr, "fcntl(O_NONBLOCK)");
+#endif
+
+ test_ok = 0;
+ called = 0;
+ return (0);
+}
+
+int
+cleanup_test(void)
+{
+#ifndef WIN32
+ close(pair[0]);
+ close(pair[1]);
+#else
+ CloseHandle((HANDLE)pair[0]);
+ CloseHandle((HANDLE)pair[1]);
+#endif
+ if (test_ok)
+ fprintf(stdout, "OK\n");
+ else {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ return (0);
+}
+
+void
+test_simpleread(void)
+{
+ struct event ev;
+
+ /* Very simple read test */
+ setup_test("Simple read: ");
+
+ write(pair[0], TEST1, strlen(TEST1)+1);
+ shutdown(pair[0], SHUT_WR);
+
+ event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev);
+ if (event_add(&ev, NULL) == -1)
+ exit(1);
+ event_dispatch();
+
+ cleanup_test();
+}
+
+void
+test_simplewrite(void)
+{
+ struct event ev;
+
+ /* Very simple write test */
+ setup_test("Simple write: ");
+
+ event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev);
+ if (event_add(&ev, NULL) == -1)
+ exit(1);
+ event_dispatch();
+
+ cleanup_test();
+}
+
+void
+test_multiple(void)
+{
+ struct event ev, ev2;
+ int i;
+
+ /* Multiple read and write test */
+ setup_test("Multiple read/write: ");
+ memset(rbuf, 0, sizeof(rbuf));
+ for (i = 0; i < sizeof(wbuf); i++)
+ wbuf[i] = i;
+
+ roff = woff = 0;
+ usepersist = 0;
+
+ event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev);
+ if (event_add(&ev, NULL) == -1)
+ exit(1);
+ event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2);
+ if (event_add(&ev2, NULL) == -1)
+ exit(1);
+ event_dispatch();
+
+ if (roff == woff)
+ test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
+
+ cleanup_test();
+}
+
+void
+test_persistent(void)
+{
+ struct event ev, ev2;
+ int i;
+
+ /* Multiple read and write test with persist */
+ setup_test("Persist read/write: ");
+ memset(rbuf, 0, sizeof(rbuf));
+ for (i = 0; i < sizeof(wbuf); i++)
+ wbuf[i] = i;
+
+ roff = woff = 0;
+ usepersist = 1;
+
+ event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev);
+ if (event_add(&ev, NULL) == -1)
+ exit(1);
+ event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2);
+ if (event_add(&ev2, NULL) == -1)
+ exit(1);
+ event_dispatch();
+
+ if (roff == woff)
+ test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
+
+ cleanup_test();
+}
+
+void
+test_combined(void)
+{
+ struct both r1, r2, w1, w2;
+
+ setup_test("Combined read/write: ");
+ memset(&r1, 0, sizeof(r1));
+ memset(&r2, 0, sizeof(r2));
+ memset(&w1, 0, sizeof(w1));
+ memset(&w2, 0, sizeof(w2));
+
+ w1.nread = 4096;
+ w2.nread = 8192;
+
+ event_set(&r1.ev, pair[0], EV_READ, combined_read_cb, &r1);
+ event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1);
+ event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2);
+ event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2);
+ if (event_add(&r1.ev, NULL) == -1)
+ exit(1);
+ if (event_add(&w1.ev, NULL))
+ exit(1);
+ if (event_add(&r2.ev, NULL))
+ exit(1);
+ if (event_add(&w2.ev, NULL))
+ exit(1);
+
+ event_dispatch();
+
+ if (r1.nread == 8192 && r2.nread == 4096)
+ test_ok = 1;
+
+ cleanup_test();
+}
+
+void
+test_simpletimeout(void)
+{
+ struct timeval tv;
+ struct event ev;
+
+ setup_test("Simple timeout: ");
+
+ tv.tv_usec = 0;
+ tv.tv_sec = SECONDS;
+ evtimer_set(&ev, timeout_cb, NULL);
+ evtimer_add(&ev, &tv);
+
+ gettimeofday(&tset, NULL);
+ event_dispatch();
+
+ cleanup_test();
+}
+
+#ifndef WIN32
+void
+test_simplesignal(void)
+{
+ struct event ev;
+ struct itimerval itv;
+
+ setup_test("Simple signal: ");
+ signal_set(&ev, SIGALRM, signal_cb, &ev);
+ signal_add(&ev, NULL);
+
+ memset(&itv, 0, sizeof(itv));
+ itv.it_value.tv_sec = 1;
+ if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
+ goto skip_simplesignal;
+
+ event_dispatch();
+ skip_simplesignal:
+ if (signal_del(&ev) == -1)
+ test_ok = 0;
+
+ cleanup_test();
+}
+#endif
+
+void
+test_loopexit(void)
+{
+ struct timeval tv, tv_start, tv_end;
+ struct event ev;
+
+ setup_test("Loop exit: ");
+
+ tv.tv_usec = 0;
+ tv.tv_sec = 60*60*24;
+ evtimer_set(&ev, timeout_cb, NULL);
+ evtimer_add(&ev, &tv);
+
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
+ event_loopexit(&tv);
+
+ gettimeofday(&tv_start, NULL);
+ event_dispatch();
+ gettimeofday(&tv_end, NULL);
+ timersub(&tv_end, &tv_start, &tv_end);
+
+ evtimer_del(&ev);
+
+ if (tv.tv_sec < 2)
+ test_ok = 1;
+
+ cleanup_test();
+}
+
+void
+test_evbuffer(void) {
+ setup_test("Evbuffer: ");
+
+ struct evbuffer *evb = evbuffer_new();
+
+ evbuffer_add_printf(evb, "%s/%d", "hello", 1);
+
+ if (EVBUFFER_LENGTH(evb) == 7 &&
+ strcmp(EVBUFFER_DATA(evb), "hello/1") == 0)
+ test_ok = 1;
+
+ cleanup_test();
+}
+
+void
+readcb(struct bufferevent *bev, void *arg)
+{
+ if (EVBUFFER_LENGTH(bev->input) == 8333) {
+ bufferevent_disable(bev, EV_READ);
+ test_ok++;
+ }
+}
+
+void
+writecb(struct bufferevent *bev, void *arg)
+{
+ if (EVBUFFER_LENGTH(bev->output) == 0)
+ test_ok++;
+}
+
+void
+errorcb(struct bufferevent *bev, short what, void *arg)
+{
+ test_ok = -2;
+}
+
+void
+test_bufferevent(void)
+{
+ struct bufferevent *bev1, *bev2;
+ char buffer[8333];
+ int i;
+
+ setup_test("Bufferevent: ");
+
+ bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL);
+ bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL);
+
+ bufferevent_disable(bev1, EV_READ);
+ bufferevent_enable(bev2, EV_READ);
+
+ for (i = 0; i < sizeof(buffer); i++)
+ buffer[0] = i;
+
+ bufferevent_write(bev1, buffer, sizeof(buffer));
+
+ event_dispatch();
+
+ bufferevent_free(bev1);
+ bufferevent_free(bev2);
+
+ if (test_ok != 2)
+ test_ok = 0;
+
+ cleanup_test();
+}
+
+struct test_pri_event {
+ struct event ev;
+ int count;
+};
+
+void
+test_priorities_cb(int fd, short what, void *arg)
+{
+ struct test_pri_event *pri = arg;
+ struct timeval tv;
+
+ if (pri->count == 3) {
+ event_loopexit(NULL);
+ return;
+ }
+
+ pri->count++;
+
+ timerclear(&tv);
+ event_add(&pri->ev, &tv);
+}
+
+void
+test_priorities(int npriorities)
+{
+ char buf[32];
+ struct test_pri_event one, two;
+ struct timeval tv;
+
+ snprintf(buf, sizeof(buf), "Priorities %d: ", npriorities);
+ setup_test(buf);
+
+ event_base_priority_init(event_base, npriorities);
+
+ memset(&one, 0, sizeof(one));
+ memset(&two, 0, sizeof(two));
+
+ timeout_set(&one.ev, test_priorities_cb, &one);
+ if (event_priority_set(&one.ev, 0) == -1) {
+ fprintf(stderr, "%s: failed to set priority", __func__);
+ exit(1);
+ }
+
+ timeout_set(&two.ev, test_priorities_cb, &two);
+ if (event_priority_set(&two.ev, npriorities - 1) == -1) {
+ fprintf(stderr, "%s: failed to set priority", __func__);
+ exit(1);
+ }
+
+ timerclear(&tv);
+
+ if (event_add(&one.ev, &tv) == -1)
+ exit(1);
+ if (event_add(&two.ev, &tv) == -1)
+ exit(1);
+
+ event_dispatch();
+
+ event_del(&one.ev);
+ event_del(&two.ev);
+
+ if (npriorities == 1) {
+ if (one.count == 3 && two.count == 3)
+ test_ok = 1;
+ } else if (npriorities == 2) {
+ /* Two is called once because event_loopexit is priority 1 */
+ if (one.count == 3 && two.count == 1)
+ test_ok = 1;
+ } else {
+ if (one.count == 3 && two.count == 0)
+ test_ok = 1;
+ }
+
+ cleanup_test();
+}
+
+static void
+test_multiple_cb(int fd, short event, void *arg)
+{
+ if (event & EV_READ)
+ test_ok |= 1;
+ else if (event & EV_WRITE)
+ test_ok |= 2;
+}
+
+void
+test_multiple_events_for_same_fd(void)
+{
+ struct event e1, e2;
+
+ setup_test("Multiple events for same fd: ");
+
+ event_set(&e1, pair[0], EV_READ, test_multiple_cb, NULL);
+ event_add(&e1, NULL);
+ event_set(&e2, pair[0], EV_WRITE, test_multiple_cb, NULL);
+ event_add(&e2, NULL);
+ event_loop(EVLOOP_ONCE);
+ event_del(&e2);
+ write(pair[1], TEST1, strlen(TEST1)+1);
+ event_loop(EVLOOP_ONCE);
+ event_del(&e1);
+
+ if (test_ok != 3)
+ test_ok = 0;
+
+ cleanup_test();
+}
+
+int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf);
+
+void
+read_once_cb(int fd, short event, void *arg)
+{
+ char buf[256];
+ int len;
+
+ len = read(fd, buf, sizeof(buf));
+
+ if (called) {
+ test_ok = 0;
+ } else if (len) {
+ /* Assumes global pair[0] can be used for writing */
+ write(pair[0], TEST1, strlen(TEST1)+1);
+ test_ok = 1;
+ }
+
+ called++;
+}
+
+void
+test_want_only_once(void)
+{
+ struct event ev;
+ struct timeval tv;
+
+ /* Very simple read test */
+ setup_test("Want read only once: ");
+
+ write(pair[0], TEST1, strlen(TEST1)+1);
+
+ /* Setup the loop termination */
+ timerclear(&tv);
+ tv.tv_sec = 1;
+ event_loopexit(&tv);
+
+ event_set(&ev, pair[1], EV_READ, read_once_cb, &ev);
+ if (event_add(&ev, NULL) == -1)
+ exit(1);
+ event_dispatch();
+
+ cleanup_test();
+}
+
+#define TEST_MAX_INT 6
+
+void
+evtag_int_test(void)
+{
+ struct evbuffer *tmp = evbuffer_new();
+ u_int32_t integers[TEST_MAX_INT] = {
+ 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000
+ };
+ u_int32_t integer;
+ int i;
+
+ for (i = 0; i < TEST_MAX_INT; i++) {
+ int oldlen, newlen;
+ oldlen = EVBUFFER_LENGTH(tmp);
+ encode_int(tmp, integers[i]);
+ newlen = EVBUFFER_LENGTH(tmp);
+ fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n",
+ integers[i], newlen - oldlen);
+ }
+
+ for (i = 0; i < TEST_MAX_INT; i++) {
+ if (decode_int(&integer, tmp) == -1) {
+ fprintf(stderr, "decode %d failed", i);
+ exit(1);
+ }
+ if (integer != integers[i]) {
+ fprintf(stderr, "got %x, wanted %x",
+ integer, integers[i]);
+ exit(1);
+ }
+ }
+
+ if (EVBUFFER_LENGTH(tmp) != 0) {
+ fprintf(stderr, "trailing data");
+ exit(1);
+ }
+ evbuffer_free(tmp);
+
+ fprintf(stdout, "\t%s: OK\n", __func__);
+}
+
+void
+evtag_fuzz()
+{
+ u_char buffer[4096];
+ struct evbuffer *tmp = evbuffer_new();
+ struct timeval tv;
+ int i, j;
+
+ int not_failed = 0;
+ for (j = 0; j < 100; j++) {
+ for (i = 0; i < sizeof(buffer); i++)
+ buffer[i] = rand();
+ evbuffer_drain(tmp, -1);
+ evbuffer_add(tmp, buffer, sizeof(buffer));
+
+ if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1)
+ not_failed++;
+ }
+
+ /* The majority of decodes should fail */
+ if (not_failed >= 10) {
+ fprintf(stderr, "evtag_unmarshal should have failed");
+ exit(1);
+ }
+
+ /* Now insert some corruption into the tag length field */
+ evbuffer_drain(tmp, -1);
+ timerclear(&tv);
+ tv.tv_sec = 1;
+ evtag_marshal_timeval(tmp, 0, &tv);
+ evbuffer_add(tmp, buffer, sizeof(buffer));
+
+ EVBUFFER_DATA(tmp)[1] = 0xff;
+ if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) {
+ fprintf(stderr, "evtag_unmarshal_timeval should have failed");
+ exit(1);
+ }
+
+ evbuffer_free(tmp);
+
+ fprintf(stdout, "\t%s: OK\n", __func__);
+}
+
+void
+evtag_test(void)
+{
+ fprintf(stdout, "Testing Tagging:\n");
+
+ evtag_init();
+ evtag_int_test();
+ evtag_fuzz();
+
+ fprintf(stdout, "OK\n");
+}
+
+void
+rpc_test(void)
+{
+ struct msg *msg, *msg2;
+ struct kill *kill;
+ struct run *run;
+ struct evbuffer *tmp = evbuffer_new();
+ int i;
+
+ fprintf(stdout, "Testing RPC: ");
+
+ msg = msg_new();
+ EVTAG_ASSIGN(msg, from_name, "niels");
+ EVTAG_ASSIGN(msg, to_name, "phoenix");
+
+ if (EVTAG_GET(msg, kill, &kill) == -1) {
+ fprintf(stderr, "Failed to set kill message.\n");
+ exit(1);
+ }
+
+ EVTAG_ASSIGN(kill, weapon, "feather");
+ EVTAG_ASSIGN(kill, action, "tickle");
+
+ for (i = 0; i < 3; ++i) {
+ run = EVTAG_ADD(msg, run);
+ if (run == NULL) {
+ fprintf(stderr, "Failed to add run message.\n");
+ exit(1);
+ }
+ EVTAG_ASSIGN(run, how, "very fast");
+ }
+
+ if (msg_complete(msg) == -1) {
+ fprintf(stderr, "Failed to make complete message.\n");
+ exit(1);
+ }
+
+ evtag_marshal_msg(tmp, 0, msg);
+
+ msg2 = msg_new();
+ if (evtag_unmarshal_msg(tmp, 0, msg2) == -1) {
+ fprintf(stderr, "Failed to unmarshal message.\n");
+ exit(1);
+ }
+
+ if (!EVTAG_HAS(msg2, from_name) ||
+ !EVTAG_HAS(msg2, to_name) ||
+ !EVTAG_HAS(msg2, kill)) {
+ fprintf(stderr, "Missing data structures.\n");
+ exit(1);
+ }
+
+ if (EVTAG_LEN(msg2, run) != 3) {
+ fprintf(stderr, "Wrong number of run messages.\n");
+ exit(1);
+ }
+
+ msg_free(msg);
+ msg_free(msg2);
+
+ fprintf(stdout, "OK\n");
+}
+
+int
+main (int argc, char **argv)
+{
+#ifdef WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD( 2, 2 );
+
+ err = WSAStartup( wVersionRequested, &wsaData );
+#endif
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /* Initalize the event library */
+ event_base = event_init();
+
+ http_suite();
+
+ dns_suite();
+
+ test_simpleread();
+
+ test_simplewrite();
+
+ test_multiple();
+
+ test_persistent();
+
+ test_combined();
+
+ test_simpletimeout();
+#ifndef WIN32
+ test_simplesignal();
+#endif
+ test_loopexit();
+
+ test_evbuffer();
+
+ test_bufferevent();
+
+ test_priorities(1);
+ test_priorities(2);
+ test_priorities(3);
+
+ test_multiple_events_for_same_fd();
+
+ test_want_only_once();
+
+ evtag_test();
+
+ rpc_test();
+
+ return (0);
+}
+
diff --git a/test/regress.h b/test/regress.h
new file mode 100644
index 00000000..296c93c5
--- /dev/null
+++ b/test/regress.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2000-2004 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. 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 _REGRESS_H_
+#define _REGRESS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void http_suite(void);
+void http_basic_test(void);
+
+void dns_suite(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REGRESS_H_ */
diff --git a/test/regress.rpc b/test/regress.rpc
new file mode 100644
index 00000000..7bc6b75d
--- /dev/null
+++ b/test/regress.rpc
@@ -0,0 +1,17 @@
+/* tests data packing and unpacking */
+
+struct msg {
+ string from_name = 1;
+ string to_name = 2;
+ optional struct[kill] kill = 3;
+ array struct[run] run = 4;
+}
+
+struct kill {
+ string weapon = 1;
+ string action = 2;
+}
+
+struct run {
+ string how = 1;
+}
diff --git a/test/regress_dns.c b/test/regress_dns.c
new file mode 100644
index 00000000..ce941878
--- /dev/null
+++ b/test/regress_dns.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2003-2006 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. 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.
+ */
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "event.h"
+#include "evdns.h"
+#include "log.h"
+
+static int dns_ok = 0;
+
+void
+dns_gethostbyname_cb(int result, char type, int count, int ttl,
+ void *addresses, void *arg)
+{
+ dns_ok = 0;
+
+ if (result != DNS_ERR_NONE)
+ goto out;
+
+ fprintf(stderr, "type: %d, count: %d, ttl: %d: ", type, count, ttl);
+
+ switch (type) {
+ case DNS_IPv4_A: {
+ struct in_addr *in_addrs = addresses;
+ int i;
+ /* a resolution that's not valid does not help */
+ if (ttl < 0)
+ goto out;
+ for (i = 0; i < count; ++i)
+ fprintf(stderr, "%s ", inet_ntoa(in_addrs[0]));
+ break;
+ }
+ case DNS_PTR:
+ /* may get at most one PTR */
+ if (count != 1)
+ goto out;
+
+ fprintf(stderr, "%s ", *(char **)addresses);
+ break;
+ default:
+ goto out;
+ }
+
+ dns_ok = 1;
+
+out:
+ event_loopexit(NULL);
+}
+
+void
+dns_gethostbyname()
+{
+ fprintf(stdout, "Simple DNS resolve: ");
+ dns_ok = 0;
+ evdns_resolve_ipv4("www.monkey.org", 0, dns_gethostbyname_cb, NULL);
+ event_dispatch();
+
+ if (dns_ok) {
+ fprintf(stdout, "OK\n");
+ } else {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+}
+
+void
+dns_gethostbyaddr()
+{
+ struct in_addr in;
+ in.s_addr = htonl(0x7f000001ul); /* 127.0.0.1 */
+ fprintf(stdout, "Simple reverse DNS resolve: ");
+ dns_ok = 0;
+ evdns_resolve_reverse(&in, 0, dns_gethostbyname_cb, NULL);
+ event_dispatch();
+
+ if (dns_ok) {
+ fprintf(stdout, "OK\n");
+ } else {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+}
+
+void
+dns_suite(void)
+{
+ evdns_init();
+ dns_gethostbyname();
+ dns_gethostbyaddr();
+
+ evdns_shutdown(0);
+}
diff --git a/test/regress_http.c b/test/regress_http.c
new file mode 100644
index 00000000..a905b03e
--- /dev/null
+++ b/test/regress_http.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2003-2006 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. 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.
+ */
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "event.h"
+#include "evhttp.h"
+#include "log.h"
+#include "http-internal.h"
+
+extern int pair[];
+extern int test_ok;
+
+static struct evhttp *http;
+
+void http_basic_cb(struct evhttp_request *req, void *arg);
+void http_post_cb(struct evhttp_request *req, void *arg);
+
+struct evhttp *
+http_setup(short *pport)
+{
+ int i;
+ struct evhttp *myhttp;
+ short port = -1;
+
+ /* Try a few different ports */
+ for (i = 0; i < 50; ++i) {
+ myhttp = evhttp_start("127.0.0.1", 8080 + i);
+ if (myhttp != NULL) {
+ port = 8080 + i;
+ break;
+ }
+ }
+
+ if (port == -1)
+ event_errx(1, "Could not start web server");
+
+ /* Register a callback for certain types of requests */
+ evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL);
+ evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
+
+ *pport = port;
+ return (myhttp);
+}
+
+int
+http_connect(const char *address, u_short port)
+{
+ /* Stupid code for connecting */
+ struct addrinfo ai, *aitop;
+ char strport[NI_MAXSERV];
+ int fd;
+
+ memset(&ai, 0, sizeof (ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_STREAM;
+ snprintf(strport, sizeof (strport), "%d", port);
+ if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
+ event_warn("getaddrinfo");
+ return (-1);
+ }
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1)
+ event_err(1, "socket failed");
+
+ if (connect(fd, aitop->ai_addr, aitop->ai_addrlen) == -1)
+ event_err(1, "connect failed");
+
+ freeaddrinfo(aitop);
+
+ return (fd);
+}
+
+void
+http_readcb(struct bufferevent *bev, void *arg)
+{
+ const char *what = "This is funny";
+
+ event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input)));
+
+ if (evbuffer_find(bev->input, what, strlen(what)) != NULL) {
+ struct evhttp_request *req = evhttp_request_new(NULL, NULL);
+ req->kind = EVHTTP_RESPONSE;
+ int done = evhttp_parse_lines(req, bev->input);
+
+ if (done == 1 &&
+ evhttp_find_header(req->input_headers,
+ "Content-Type") != NULL)
+ test_ok++;
+ evhttp_request_free(req);
+ bufferevent_disable(bev, EV_READ);
+ event_loopexit(NULL);
+ }
+}
+
+void
+http_writecb(struct bufferevent *bev, void *arg)
+{
+ if (EVBUFFER_LENGTH(bev->output) == 0) {
+ /* enable reading of the reply */
+ bufferevent_enable(bev, EV_READ);
+ test_ok++;
+ }
+}
+
+void
+http_errorcb(struct bufferevent *bev, short what, void *arg)
+{
+ test_ok = -2;
+ event_loopexit(NULL);
+}
+
+void
+http_basic_cb(struct evhttp_request *req, void *arg)
+{
+ event_debug((stderr, "%s: called\n", __func__));
+
+ struct evbuffer *evb = evbuffer_new();
+ evbuffer_add_printf(evb, "This is funny");
+
+ evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
+
+ evbuffer_free(evb);
+}
+
+void
+http_basic_test(void)
+{
+ struct bufferevent *bev;
+ int fd;
+ char *http_request;
+ short port = -1;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing Basic HTTP Server: ");
+
+ http = http_setup(&port);
+
+ fd = http_connect("127.0.0.1", port);
+
+ /* Stupid thing to send a request */
+ bev = bufferevent_new(fd, http_readcb, http_writecb,
+ http_errorcb, NULL);
+
+ http_request =
+ "GET /test HTTP/1.1\r\n"
+ "Host: somehost \r\n"
+ "\r\n";
+
+ bufferevent_write(bev, http_request, strlen(http_request));
+
+ event_dispatch();
+
+ bufferevent_free(bev);
+ close(fd);
+
+ evhttp_free(http);
+
+ if (test_ok != 2) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void http_request_done(struct evhttp_request *, void *);
+
+void
+http_connection_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing Basic HTTP Connection: ");
+
+ http = http_setup(&port);
+
+ evcon = evhttp_connection_new("127.0.0.1", port);
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ /*
+ * At this point, we want to schedule a request to the HTTP
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_request_done, NULL);
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+
+ /* We give ownership of the request to the connection */
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void
+http_request_done(struct evhttp_request *req, void *arg)
+{
+ const char *what = "This is funny";
+
+ if (req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ test_ok = 1;
+ event_loopexit(NULL);
+}
+
+/*
+ * HTTP POST test.
+ */
+
+void http_postrequest_done(struct evhttp_request *, void *);
+
+#define POST_DATA "Okay. Not really printf"
+
+void
+http_post_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing HTTP POST Request: ");
+
+ http = http_setup(&port);
+
+ evcon = evhttp_connection_new("127.0.0.1", port);
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ /*
+ * At this point, we want to schedule an HTTP POST request
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_postrequest_done, NULL);
+ if (req == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evbuffer_add_printf(req->output_buffer, POST_DATA);
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED: %d\n", test_ok);
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void
+http_post_cb(struct evhttp_request *req, void *arg)
+{
+ event_debug((stderr, "%s: called\n", __func__));
+
+ /* Yes, we are expecting a post request */
+ if (req->type != EVHTTP_REQ_POST) {
+ fprintf(stdout, "FAILED (post type)\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) {
+ fprintf(stdout, "FAILED (length: %ld vs %ld)\n",
+ EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA));
+ exit(1);
+ }
+
+ if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA,
+ strlen(POST_DATA))) {
+ fprintf(stdout, "FAILED (data)\n");
+ fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer));
+ fprintf(stdout, "Want:%s\n", POST_DATA);
+ exit(1);
+ }
+
+ struct evbuffer *evb = evbuffer_new();
+ evbuffer_add_printf(evb, "This is funny");
+
+ evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
+
+ evbuffer_free(evb);
+}
+
+void
+http_postrequest_done(struct evhttp_request *req, void *arg)
+{
+ const char *what = "This is funny";
+
+ if (req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED (response code)\n");
+ exit(1);
+ }
+
+ if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
+ fprintf(stderr, "FAILED (content type)\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
+ fprintf(stderr, "FAILED (length %ld vs %ld)\n",
+ EVBUFFER_LENGTH(req->input_buffer), strlen(what));
+ exit(1);
+ }
+
+ if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
+ fprintf(stderr, "FAILED (data)\n");
+ exit(1);
+ }
+
+ test_ok = 1;
+ event_loopexit(NULL);
+}
+
+
+void
+http_suite(void)
+{
+ http_basic_test();
+ http_connection_test();
+ http_post_test();
+}
diff --git a/test/test-eof.c b/test/test-eof.c
new file mode 100644
index 00000000..020c6611
--- /dev/null
+++ b/test/test-eof.c
@@ -0,0 +1,68 @@
+/*
+ * 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/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", __func__,
+ 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-init.c b/test/test-init.c
new file mode 100644
index 00000000..32806baa
--- /dev/null
+++ b/test/test-init.c
@@ -0,0 +1,27 @@
+/*
+ * 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/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
+main(int argc, char **argv)
+{
+ /* Initalize the event library */
+ event_init();
+
+ return (0);
+}
+
diff --git a/test/test-time.c b/test/test-time.c
new file mode 100644
index 00000000..a26c973e
--- /dev/null
+++ b/test/test-time.c
@@ -0,0 +1,68 @@
+/*
+ * 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/time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event.h>
+
+int called = 0;
+
+#define NEVENT 20000
+
+struct event *ev[NEVENT];
+
+void
+time_cb(int fd, short event, void *arg)
+{
+ struct timeval tv;
+ int i, j;
+
+ called++;
+
+ if (called < 10*NEVENT) {
+ for (i = 0; i < 10; i++) {
+ j = random() % NEVENT;
+ tv.tv_sec = 0;
+ tv.tv_usec = random() % 50000L;
+ if (tv.tv_usec % 2)
+ evtimer_add(ev[j], &tv);
+ else
+ evtimer_del(ev[j]);
+ }
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ struct timeval tv;
+ int i;
+
+ /* Initalize the event library */
+ event_init();
+
+ for (i = 0; i < NEVENT; i++) {
+ ev[i] = malloc(sizeof(struct event));
+
+ /* Initalize one event */
+ evtimer_set(ev[i], time_cb, ev[i]);
+ tv.tv_sec = 0;
+ tv.tv_usec = random() % 50000L;
+ evtimer_add(ev[i], &tv);
+ }
+
+ event_dispatch();
+
+ return (called < NEVENT);
+}
+
diff --git a/test/test-weof.c b/test/test-weof.c
new file mode 100644
index 00000000..5f3be277
--- /dev/null
+++ b/test/test-weof.c
@@ -0,0 +1,68 @@
+/*
+ * 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/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", __func__,
+ 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_ERR)
+ 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);
+}
+
diff --git a/test/test.sh b/test/test.sh
new file mode 100755
index 00000000..878d468d
--- /dev/null
+++ b/test/test.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+setup () {
+ EVENT_NOKQUEUE=yes; export EVENT_NOKQUEUE
+ EVENT_NODEVPOLL=yes; export EVENT_NODEVPOLL
+ EVENT_NOPOLL=yes; export EVENT_NOPOLL
+ EVENT_NOSELECT=yes; export EVENT_NOSELECT
+ EVENT_NOEPOLL=yes; export EVENT_NOEPOLL
+ EVENT_NORTSIG=yes; export EVENT_NORTSIG
+}
+
+test () {
+ if ./test-init 2>/dev/null ;
+ then
+ true
+ else
+ echo Skipping test
+ return
+ fi
+
+echo -n " test-eof: "
+if ./test-eof >/dev/null ;
+then
+ echo OKAY ;
+else
+ echo FAILED ;
+fi
+echo -n " test-weof: "
+if ./test-weof >/dev/null ;
+then
+ echo OKAY ;
+else
+ echo FAILED ;
+fi
+echo -n " test-time: "
+if ./test-time >/dev/null ;
+then
+ echo OKAY ;
+else
+ echo FAILED ;
+fi
+echo -n " regress: "
+if ./regress >/dev/null ;
+then
+ echo OKAY ;
+else
+ echo FAILED ;
+fi
+}
+
+echo "Running tests:"
+
+# Need to do this by hand?
+setup
+unset EVENT_NOKQUEUE
+export EVENT_NOKQUEUE
+echo "KQUEUE"
+test
+
+setup
+unset EVENT_NODEVPOLL
+export EVENT_NODEVPOLL
+echo "DEVPOLL"
+test
+
+setup
+unset EVENT_NOPOLL
+export EVENT_NOPOLL
+echo "POLL"
+test
+
+setup
+unset EVENT_NOSELECT
+export EVENT_NOSELECT
+echo "SELECT"
+test
+
+setup
+unset EVENT_NORTSIG
+export EVENT_NORTSIG
+echo "RTSIG"
+test
+
+setup
+unset EVENT_NOEPOLL
+export EVENT_NOEPOLL
+echo "EPOLL"
+test
+
+
+