diff options
author | Stuart Henderson <sthen@cvs.openbsd.org> | 2013-11-26 12:50:31 +0000 |
---|---|---|
committer | Stuart Henderson <sthen@cvs.openbsd.org> | 2013-11-26 12:50:31 +0000 |
commit | b665eb4cb1ea56ccad7fee700f05c85dec76e702 (patch) | |
tree | 8453629bcc74596d1a3588c5a534658f6a7b3503 /usr.sbin/nsd/mini_event.c | |
parent | 9f9bd245ba092cf635e0212513052b389360c9ba (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.c | 446 |
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 */ |