summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd/mini_event.c
diff options
context:
space:
mode:
authorStuart Henderson <sthen@cvs.openbsd.org>2013-11-26 12:50:31 +0000
committerStuart Henderson <sthen@cvs.openbsd.org>2013-11-26 12:50:31 +0000
commitb665eb4cb1ea56ccad7fee700f05c85dec76e702 (patch)
tree8453629bcc74596d1a3588c5a534658f6a7b3503 /usr.sbin/nsd/mini_event.c
parent9f9bd245ba092cf635e0212513052b389360c9ba (diff)
import NSD 4.0.0, tests from Dorian Büttner, Patrik Lundin, requested by brad@
Diffstat (limited to 'usr.sbin/nsd/mini_event.c')
-rw-r--r--usr.sbin/nsd/mini_event.c446
1 files changed, 446 insertions, 0 deletions
diff --git a/usr.sbin/nsd/mini_event.c b/usr.sbin/nsd/mini_event.c
new file mode 100644
index 00000000000..4048bcfae89
--- /dev/null
+++ b/usr.sbin/nsd/mini_event.c
@@ -0,0 +1,446 @@
+/*
+ * mini_event.c - implementation of part of libevent api, portably.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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.
+ *
+ */
+
+/**
+ * \file
+ * fake libevent implementation. Less broad in functionality, and only
+ * supports select(2).
+ */
+
+#include "config.h"
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
+#ifdef HAVE_WINSOCK2_H
+#define FD_SET_T (u_int)
+#else
+#define FD_SET_T
+#endif
+
+#include <signal.h>
+#include "mini_event.h"
+#include "util.h"
+
+/** compare events in tree, based on timevalue, ptr for uniqueness */
+int
+mini_ev_cmp(const void* a, const void* b)
+{
+ const struct event* e = (const struct event*)a;
+ const struct event* f = (const struct event*)b;
+ if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
+ return -1;
+ if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
+ return 1;
+ if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
+ return -1;
+ if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
+ return 1;
+ if(e < f)
+ return -1;
+ if(e > f)
+ return 1;
+ return 0;
+}
+
+/** set time */
+static int
+settime(struct event_base* base)
+{
+ if(gettimeofday(base->time_tv, NULL) < 0) {
+ return -1;
+ }
+#ifndef S_SPLINT_S
+ *base->time_secs = (time_t)base->time_tv->tv_sec;
+#endif
+ return 0;
+}
+
+/** create event base */
+void *
+event_init(time_t* time_secs, struct timeval* time_tv)
+{
+ struct event_base* base = (struct event_base*)malloc(
+ sizeof(struct event_base));
+ if(!base)
+ return NULL;
+ memset(base, 0, sizeof(*base));
+ base->region = region_create(xalloc, free);
+ if(!base->region) {
+ free(base);
+ return NULL;
+ }
+ base->time_secs = time_secs;
+ base->time_tv = time_tv;
+ if(settime(base) < 0) {
+ event_base_free(base);
+ return NULL;
+ }
+ base->times = rbtree_create(base->region, mini_ev_cmp);
+ if(!base->times) {
+ event_base_free(base);
+ return NULL;
+ }
+ base->capfd = MAX_FDS;
+#ifdef FD_SETSIZE
+ if((int)FD_SETSIZE < base->capfd)
+ base->capfd = (int)FD_SETSIZE;
+#endif
+ base->fds = (struct event**)calloc((size_t)base->capfd,
+ sizeof(struct event*));
+ if(!base->fds) {
+ event_base_free(base);
+ return NULL;
+ }
+ base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
+ if(!base->signals) {
+ event_base_free(base);
+ return NULL;
+ }
+#ifndef S_SPLINT_S
+ FD_ZERO(&base->reads);
+ FD_ZERO(&base->writes);
+#endif
+ return base;
+}
+
+/** get version */
+const char *
+event_get_version(void)
+{
+ return "mini-event-"PACKAGE_VERSION;
+}
+
+/** get polling method, select */
+const char *
+event_get_method(void)
+{
+ return "select";
+}
+
+/** call timeouts handlers, and return how long to wait for next one or -1 */
+static int
+handle_timeouts(struct event_base* base, struct timeval* now,
+ struct timeval* wait)
+{
+ struct event* p;
+ int tofired = 0;
+#ifndef S_SPLINT_S
+ wait->tv_sec = (time_t)-1;
+#endif
+
+ while((rbnode_t*)(p = (struct event*)rbtree_first(base->times))
+ !=RBTREE_NULL) {
+#ifndef S_SPLINT_S
+ if(p->ev_timeout.tv_sec > now->tv_sec ||
+ (p->ev_timeout.tv_sec==now->tv_sec &&
+ p->ev_timeout.tv_usec > now->tv_usec)) {
+ /* there is a next larger timeout. wait for it */
+ wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
+ if(now->tv_usec > p->ev_timeout.tv_usec) {
+ wait->tv_sec--;
+ wait->tv_usec = 1000000 - (now->tv_usec -
+ p->ev_timeout.tv_usec);
+ } else {
+ wait->tv_usec = p->ev_timeout.tv_usec
+ - now->tv_usec;
+ }
+ return tofired;
+ }
+#endif
+ /* event times out, remove it */
+ tofired = 1;
+ (void)rbtree_delete(base->times, p);
+ p->ev_flags &= ~EV_TIMEOUT;
+ (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
+ }
+ return tofired;
+}
+
+/** call select and callbacks for that */
+static int
+handle_select(struct event_base* base, struct timeval* wait)
+{
+ fd_set r, w;
+ int ret, i;
+
+#ifndef S_SPLINT_S
+ if(wait->tv_sec==(time_t)-1)
+ wait = NULL;
+#endif
+ memmove(&r, &base->reads, sizeof(fd_set));
+ memmove(&w, &base->writes, sizeof(fd_set));
+ memmove(&base->ready, &base->content, sizeof(fd_set));
+
+ if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
+ ret = errno;
+ if(settime(base) < 0)
+ return -1;
+ errno = ret;
+ if(ret == EAGAIN || ret == EINTR)
+ return 0;
+ return -1;
+ }
+ if(settime(base) < 0)
+ return -1;
+
+ for(i=0; i<base->maxfd+1; i++) {
+ short bits = 0;
+ if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
+ continue;
+ }
+ if(FD_ISSET(i, &r)) {
+ bits |= EV_READ;
+ ret--;
+ }
+ if(FD_ISSET(i, &w)) {
+ bits |= EV_WRITE;
+ ret--;
+ }
+ bits &= base->fds[i]->ev_flags;
+ if(bits) {
+ (*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
+ bits, base->fds[i]->ev_arg);
+ if(ret==0)
+ break;
+ }
+ }
+ return 0;
+}
+
+/** run select once */
+int
+event_base_loop(struct event_base* base, int flags)
+{
+ struct timeval wait;
+ if(!(flags & EVLOOP_ONCE))
+ return event_base_dispatch(base);
+ /* see if timeouts need handling */
+ if(handle_timeouts(base, base->time_tv, &wait))
+ return 0; /* there were timeouts, end of loop */
+ if(base->need_to_exit)
+ return 0;
+ /* do select */
+ if(handle_select(base, &wait) < 0) {
+ if(base->need_to_exit)
+ return 0;
+ return -1;
+ }
+ return 0;
+}
+
+/** run select in a loop */
+int
+event_base_dispatch(struct event_base* base)
+{
+ struct timeval wait;
+ if(settime(base) < 0)
+ return -1;
+ while(!base->need_to_exit)
+ {
+ /* see if timeouts need handling */
+ (void)handle_timeouts(base, base->time_tv, &wait);
+ if(base->need_to_exit)
+ return 0;
+ /* do select */
+ if(handle_select(base, &wait) < 0) {
+ if(base->need_to_exit)
+ return 0;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/** exit that loop */
+int
+event_base_loopexit(struct event_base* base,
+ struct timeval* ATTR_UNUSED(tv))
+{
+ base->need_to_exit = 1;
+ return 0;
+}
+
+/* free event base, free events yourself */
+void
+event_base_free(struct event_base* base)
+{
+ if(!base)
+ return;
+ if(base->times)
+ free(base->times);
+ if(base->fds)
+ free(base->fds);
+ if(base->signals)
+ free(base->signals);
+ region_destroy(base->region);
+ free(base);
+}
+
+/** set content of event */
+void
+event_set(struct event* ev, int fd, short bits,
+ void (*cb)(int, short, void *), void* arg)
+{
+ ev->node.key = ev;
+ ev->ev_fd = fd;
+ ev->ev_flags = bits;
+ ev->ev_callback = cb;
+ ev->ev_arg = arg;
+ ev->added = 0;
+}
+
+/* add event to a base */
+int
+event_base_set(struct event_base* base, struct event* ev)
+{
+ ev->ev_base = base;
+ ev->added = 0;
+ return 0;
+}
+
+/* add event to make it active, you may not change it with event_set anymore */
+int
+event_add(struct event* ev, struct timeval* tv)
+{
+ if(ev->added)
+ event_del(ev);
+ if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
+ return -1;
+ if( (ev->ev_flags&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
+ ev->ev_base->fds[ev->ev_fd] = ev;
+ if(ev->ev_flags&EV_READ) {
+ FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
+ }
+ if(ev->ev_flags&EV_WRITE) {
+ FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
+ }
+ FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
+ FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
+ if(ev->ev_fd > ev->ev_base->maxfd)
+ ev->ev_base->maxfd = ev->ev_fd;
+ }
+ if(tv && (ev->ev_flags&EV_TIMEOUT)) {
+#ifndef S_SPLINT_S
+ struct timeval* now = ev->ev_base->time_tv;
+ ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
+ ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
+ while(ev->ev_timeout.tv_usec > 1000000) {
+ ev->ev_timeout.tv_usec -= 1000000;
+ ev->ev_timeout.tv_sec++;
+ }
+#endif
+ (void)rbtree_insert(ev->ev_base->times, &ev->node);
+ }
+ ev->added = 1;
+ return 0;
+}
+
+/* remove event, you may change it again */
+int
+event_del(struct event* ev)
+{
+ if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
+ return -1;
+ if((ev->ev_flags&EV_TIMEOUT))
+ (void)rbtree_delete(ev->ev_base->times, &ev->node);
+ if((ev->ev_flags&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
+ ev->ev_base->fds[ev->ev_fd] = NULL;
+ FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
+ FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
+ FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
+ FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
+ }
+ ev->added = 0;
+ return 0;
+}
+
+/** which base gets to handle signals */
+static struct event_base* signal_base = NULL;
+
+/** signal handler */
+static RETSIGTYPE
+sigh(int sig)
+{
+ struct event* ev;
+ if(!signal_base || sig < 0 || sig >= MAX_SIG)
+ return;
+ ev = signal_base->signals[sig];
+ if(!ev)
+ return;
+ (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
+}
+
+/** install signal handler */
+int
+signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
+{
+ struct sigaction action;
+ if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
+ return -1;
+ signal_base = ev->ev_base;
+ ev->ev_base->signals[ev->ev_fd] = ev;
+ ev->added = 1;
+ action.sa_handler = sigh;
+ sigfillset(&action.sa_mask);
+ action.sa_flags = 0;
+ return sigaction(ev->ev_fd, &action, NULL);
+}
+
+/** remove signal handler */
+int
+signal_del(struct event* ev)
+{
+ if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
+ return -1;
+ ev->ev_base->signals[ev->ev_fd] = NULL;
+ ev->added = 0;
+ return 0;
+}
+
+#else /* USE_MINI_EVENT */
+#ifndef USE_WINSOCK
+int
+mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+ return 0;
+}
+#endif /* not USE_WINSOCK */
+#endif /* USE_MINI_EVENT */