13b6c3722Schristos /*
23b6c3722Schristos * mini_event.c - implementation of part of libevent api, portably.
33b6c3722Schristos *
43b6c3722Schristos * Copyright (c) 2007, NLnet Labs. All rights reserved.
53b6c3722Schristos *
63b6c3722Schristos * This software is open source.
73b6c3722Schristos *
83b6c3722Schristos * Redistribution and use in source and binary forms, with or without
93b6c3722Schristos * modification, are permitted provided that the following conditions
103b6c3722Schristos * are met:
113b6c3722Schristos *
123b6c3722Schristos * Redistributions of source code must retain the above copyright notice,
133b6c3722Schristos * this list of conditions and the following disclaimer.
143b6c3722Schristos *
153b6c3722Schristos * Redistributions in binary form must reproduce the above copyright notice,
163b6c3722Schristos * this list of conditions and the following disclaimer in the documentation
173b6c3722Schristos * and/or other materials provided with the distribution.
183b6c3722Schristos *
193b6c3722Schristos * Neither the name of the NLNET LABS nor the names of its contributors may
203b6c3722Schristos * be used to endorse or promote products derived from this software without
213b6c3722Schristos * specific prior written permission.
223b6c3722Schristos *
233b6c3722Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243b6c3722Schristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
253b6c3722Schristos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
263b6c3722Schristos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
273b6c3722Schristos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283b6c3722Schristos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
293b6c3722Schristos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
303b6c3722Schristos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
313b6c3722Schristos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
323b6c3722Schristos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
333b6c3722Schristos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
343b6c3722Schristos *
353b6c3722Schristos */
363b6c3722Schristos
373b6c3722Schristos /**
383b6c3722Schristos * \file
393b6c3722Schristos * fake libevent implementation. Less broad in functionality, and only
403b6c3722Schristos * supports select(2).
413b6c3722Schristos */
423b6c3722Schristos
433b6c3722Schristos #include "config.h"
443b6c3722Schristos #ifdef HAVE_TIME_H
453b6c3722Schristos #include <time.h>
463b6c3722Schristos #endif
473b6c3722Schristos #include <sys/time.h>
48dcabb5fdSchristos #include "util/mini_event.h"
493b6c3722Schristos
503b6c3722Schristos #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
513b6c3722Schristos #include <signal.h>
523b6c3722Schristos #include "util/fptr_wlist.h"
533b6c3722Schristos
5440ce895eSroy /* Enforce a correct include/config.h as I tire of fixin it. */
5540ce895eSroy #error This code should not be active on NetBSD, please use libevent.
5640ce895eSroy
573b6c3722Schristos /** compare events in tree, based on timevalue, ptr for uniqueness */
mini_ev_cmp(const void * a,const void * b)583b6c3722Schristos int mini_ev_cmp(const void* a, const void* b)
593b6c3722Schristos {
603b6c3722Schristos const struct event *e = (const struct event*)a;
613b6c3722Schristos const struct event *f = (const struct event*)b;
623b6c3722Schristos if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
633b6c3722Schristos return -1;
643b6c3722Schristos if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
653b6c3722Schristos return 1;
663b6c3722Schristos if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
673b6c3722Schristos return -1;
683b6c3722Schristos if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
693b6c3722Schristos return 1;
703b6c3722Schristos if(e < f)
713b6c3722Schristos return -1;
723b6c3722Schristos if(e > f)
733b6c3722Schristos return 1;
743b6c3722Schristos return 0;
753b6c3722Schristos }
763b6c3722Schristos
773b6c3722Schristos /** set time */
783b6c3722Schristos static int
settime(struct event_base * base)793b6c3722Schristos settime(struct event_base* base)
803b6c3722Schristos {
813b6c3722Schristos if(gettimeofday(base->time_tv, NULL) < 0) {
823b6c3722Schristos return -1;
833b6c3722Schristos }
843b6c3722Schristos #ifndef S_SPLINT_S
853b6c3722Schristos *base->time_secs = (time_t)base->time_tv->tv_sec;
863b6c3722Schristos #endif
873b6c3722Schristos return 0;
883b6c3722Schristos }
893b6c3722Schristos
903b6c3722Schristos /** create event base */
event_init(time_t * time_secs,struct timeval * time_tv)913b6c3722Schristos void *event_init(time_t* time_secs, struct timeval* time_tv)
923b6c3722Schristos {
933b6c3722Schristos struct event_base* base = (struct event_base*)malloc(
943b6c3722Schristos sizeof(struct event_base));
953b6c3722Schristos if(!base)
963b6c3722Schristos return NULL;
973b6c3722Schristos memset(base, 0, sizeof(*base));
983b6c3722Schristos base->time_secs = time_secs;
993b6c3722Schristos base->time_tv = time_tv;
1003b6c3722Schristos if(settime(base) < 0) {
1013b6c3722Schristos event_base_free(base);
1023b6c3722Schristos return NULL;
1033b6c3722Schristos }
1043b6c3722Schristos base->times = rbtree_create(mini_ev_cmp);
1053b6c3722Schristos if(!base->times) {
1063b6c3722Schristos event_base_free(base);
1073b6c3722Schristos return NULL;
1083b6c3722Schristos }
1093b6c3722Schristos base->capfd = MAX_FDS;
1103b6c3722Schristos #ifdef FD_SETSIZE
1113b6c3722Schristos if((int)FD_SETSIZE < base->capfd)
1123b6c3722Schristos base->capfd = (int)FD_SETSIZE;
1133b6c3722Schristos #endif
1143b6c3722Schristos base->fds = (struct event**)calloc((size_t)base->capfd,
1153b6c3722Schristos sizeof(struct event*));
1163b6c3722Schristos if(!base->fds) {
1173b6c3722Schristos event_base_free(base);
1183b6c3722Schristos return NULL;
1193b6c3722Schristos }
1203b6c3722Schristos base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
1213b6c3722Schristos if(!base->signals) {
1223b6c3722Schristos event_base_free(base);
1233b6c3722Schristos return NULL;
1243b6c3722Schristos }
1253b6c3722Schristos #ifndef S_SPLINT_S
1263b6c3722Schristos FD_ZERO(&base->reads);
1273b6c3722Schristos FD_ZERO(&base->writes);
1283b6c3722Schristos #endif
1293b6c3722Schristos return base;
1303b6c3722Schristos }
1313b6c3722Schristos
1323b6c3722Schristos /** get version */
event_get_version(void)1333b6c3722Schristos const char *event_get_version(void)
1343b6c3722Schristos {
1353b6c3722Schristos return "mini-event-"PACKAGE_VERSION;
1363b6c3722Schristos }
1373b6c3722Schristos
1383b6c3722Schristos /** get polling method, select */
event_get_method(void)1393b6c3722Schristos const char *event_get_method(void)
1403b6c3722Schristos {
1413b6c3722Schristos return "select";
1423b6c3722Schristos }
1433b6c3722Schristos
1443b6c3722Schristos /** call timeouts handlers, and return how long to wait for next one or -1 */
handle_timeouts(struct event_base * base,struct timeval * now,struct timeval * wait)1453b6c3722Schristos static void handle_timeouts(struct event_base* base, struct timeval* now,
1463b6c3722Schristos struct timeval* wait)
1473b6c3722Schristos {
1483b6c3722Schristos struct event* p;
1493b6c3722Schristos #ifndef S_SPLINT_S
1503b6c3722Schristos wait->tv_sec = (time_t)-1;
1513b6c3722Schristos #endif
1523b6c3722Schristos
1530cd9f4ecSchristos while((rbnode_type*)(p = (struct event*)rbtree_first(base->times))
1543b6c3722Schristos !=RBTREE_NULL) {
1553b6c3722Schristos #ifndef S_SPLINT_S
1563b6c3722Schristos if(p->ev_timeout.tv_sec > now->tv_sec ||
1573b6c3722Schristos (p->ev_timeout.tv_sec==now->tv_sec &&
1583b6c3722Schristos p->ev_timeout.tv_usec > now->tv_usec)) {
1593b6c3722Schristos /* there is a next larger timeout. wait for it */
1603b6c3722Schristos wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
1613b6c3722Schristos if(now->tv_usec > p->ev_timeout.tv_usec) {
1623b6c3722Schristos wait->tv_sec--;
1633b6c3722Schristos wait->tv_usec = 1000000 - (now->tv_usec -
1643b6c3722Schristos p->ev_timeout.tv_usec);
1653b6c3722Schristos } else {
1663b6c3722Schristos wait->tv_usec = p->ev_timeout.tv_usec
1673b6c3722Schristos - now->tv_usec;
1683b6c3722Schristos }
1693b6c3722Schristos return;
1703b6c3722Schristos }
1713b6c3722Schristos #endif
1723b6c3722Schristos /* event times out, remove it */
1733b6c3722Schristos (void)rbtree_delete(base->times, p);
1743b6c3722Schristos p->ev_events &= ~EV_TIMEOUT;
1753b6c3722Schristos fptr_ok(fptr_whitelist_event(p->ev_callback));
1763b6c3722Schristos (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
1773b6c3722Schristos }
1783b6c3722Schristos }
1793b6c3722Schristos
1803b6c3722Schristos /** call select and callbacks for that */
handle_select(struct event_base * base,struct timeval * wait)1813b6c3722Schristos static int handle_select(struct event_base* base, struct timeval* wait)
1823b6c3722Schristos {
1833b6c3722Schristos fd_set r, w;
1843b6c3722Schristos int ret, i;
1853b6c3722Schristos
1863b6c3722Schristos #ifndef S_SPLINT_S
1873b6c3722Schristos if(wait->tv_sec==(time_t)-1)
1883b6c3722Schristos wait = NULL;
1893b6c3722Schristos #endif
1903b6c3722Schristos memmove(&r, &base->reads, sizeof(fd_set));
1913b6c3722Schristos memmove(&w, &base->writes, sizeof(fd_set));
1923b6c3722Schristos memmove(&base->ready, &base->content, sizeof(fd_set));
1933b6c3722Schristos
1943b6c3722Schristos if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
1953b6c3722Schristos ret = errno;
1963b6c3722Schristos if(settime(base) < 0)
1973b6c3722Schristos return -1;
1983b6c3722Schristos errno = ret;
1993b6c3722Schristos if(ret == EAGAIN || ret == EINTR)
2003b6c3722Schristos return 0;
2013b6c3722Schristos return -1;
2023b6c3722Schristos }
2033b6c3722Schristos if(settime(base) < 0)
2043b6c3722Schristos return -1;
2053b6c3722Schristos
2063b6c3722Schristos for(i=0; i<base->maxfd+1; i++) {
2073b6c3722Schristos short bits = 0;
2083b6c3722Schristos if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
2093b6c3722Schristos continue;
2103b6c3722Schristos }
2113b6c3722Schristos if(FD_ISSET(i, &r)) {
2123b6c3722Schristos bits |= EV_READ;
2133b6c3722Schristos ret--;
2143b6c3722Schristos }
2153b6c3722Schristos if(FD_ISSET(i, &w)) {
2163b6c3722Schristos bits |= EV_WRITE;
2173b6c3722Schristos ret--;
2183b6c3722Schristos }
2193b6c3722Schristos bits &= base->fds[i]->ev_events;
2203b6c3722Schristos if(bits) {
2213b6c3722Schristos fptr_ok(fptr_whitelist_event(
2223b6c3722Schristos base->fds[i]->ev_callback));
2233b6c3722Schristos (*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
2243b6c3722Schristos bits, base->fds[i]->ev_arg);
2253b6c3722Schristos if(ret==0)
2263b6c3722Schristos break;
2273b6c3722Schristos }
2283b6c3722Schristos }
2293b6c3722Schristos return 0;
2303b6c3722Schristos }
2313b6c3722Schristos
2323b6c3722Schristos /** run select in a loop */
event_base_dispatch(struct event_base * base)2333b6c3722Schristos int event_base_dispatch(struct event_base* base)
2343b6c3722Schristos {
2353b6c3722Schristos struct timeval wait;
2363b6c3722Schristos if(settime(base) < 0)
2373b6c3722Schristos return -1;
2383b6c3722Schristos while(!base->need_to_exit)
2393b6c3722Schristos {
2403b6c3722Schristos /* see if timeouts need handling */
2413b6c3722Schristos handle_timeouts(base, base->time_tv, &wait);
2423b6c3722Schristos if(base->need_to_exit)
2433b6c3722Schristos return 0;
2443b6c3722Schristos /* do select */
2453b6c3722Schristos if(handle_select(base, &wait) < 0) {
2463b6c3722Schristos if(base->need_to_exit)
2473b6c3722Schristos return 0;
2483b6c3722Schristos return -1;
2493b6c3722Schristos }
2503b6c3722Schristos }
2513b6c3722Schristos return 0;
2523b6c3722Schristos }
2533b6c3722Schristos
2543b6c3722Schristos /** exit that loop */
event_base_loopexit(struct event_base * base,struct timeval * ATTR_UNUSED (tv))2553b6c3722Schristos int event_base_loopexit(struct event_base* base,
2563b6c3722Schristos struct timeval* ATTR_UNUSED(tv))
2573b6c3722Schristos {
2583b6c3722Schristos base->need_to_exit = 1;
2593b6c3722Schristos return 0;
2603b6c3722Schristos }
2613b6c3722Schristos
2623b6c3722Schristos /* free event base, free events yourself */
event_base_free(struct event_base * base)2633b6c3722Schristos void event_base_free(struct event_base* base)
2643b6c3722Schristos {
2653b6c3722Schristos if(!base)
2663b6c3722Schristos return;
2673b6c3722Schristos free(base->times);
2683b6c3722Schristos free(base->fds);
2693b6c3722Schristos free(base->signals);
2703b6c3722Schristos free(base);
2713b6c3722Schristos }
2723b6c3722Schristos
2733b6c3722Schristos /** set content of event */
event_set(struct event * ev,int fd,short bits,void (* cb)(int,short,void *),void * arg)2743b6c3722Schristos void event_set(struct event* ev, int fd, short bits,
2753b6c3722Schristos void (*cb)(int, short, void *), void* arg)
2763b6c3722Schristos {
2773b6c3722Schristos ev->node.key = ev;
2783b6c3722Schristos ev->ev_fd = fd;
2793b6c3722Schristos ev->ev_events = bits;
2803b6c3722Schristos ev->ev_callback = cb;
2813b6c3722Schristos fptr_ok(fptr_whitelist_event(ev->ev_callback));
2823b6c3722Schristos ev->ev_arg = arg;
2833b6c3722Schristos ev->added = 0;
2843b6c3722Schristos }
2853b6c3722Schristos
2863b6c3722Schristos /* add event to a base */
event_base_set(struct event_base * base,struct event * ev)2873b6c3722Schristos int event_base_set(struct event_base* base, struct event* ev)
2883b6c3722Schristos {
2893b6c3722Schristos ev->ev_base = base;
2903b6c3722Schristos ev->added = 0;
2913b6c3722Schristos return 0;
2923b6c3722Schristos }
2933b6c3722Schristos
2943b6c3722Schristos /* add event to make it active, you may not change it with event_set anymore */
event_add(struct event * ev,struct timeval * tv)2953b6c3722Schristos int event_add(struct event* ev, struct timeval* tv)
2963b6c3722Schristos {
2973b6c3722Schristos if(ev->added)
2983b6c3722Schristos event_del(ev);
2993b6c3722Schristos if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
3003b6c3722Schristos return -1;
3013b6c3722Schristos if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
3023b6c3722Schristos ev->ev_base->fds[ev->ev_fd] = ev;
3033b6c3722Schristos if(ev->ev_events&EV_READ) {
3043b6c3722Schristos FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
3053b6c3722Schristos }
3063b6c3722Schristos if(ev->ev_events&EV_WRITE) {
3073b6c3722Schristos FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
3083b6c3722Schristos }
3093b6c3722Schristos FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
3103b6c3722Schristos FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
3113b6c3722Schristos if(ev->ev_fd > ev->ev_base->maxfd)
3123b6c3722Schristos ev->ev_base->maxfd = ev->ev_fd;
3133b6c3722Schristos }
3143b6c3722Schristos if(tv && (ev->ev_events&EV_TIMEOUT)) {
3153b6c3722Schristos #ifndef S_SPLINT_S
3163b6c3722Schristos struct timeval *now = ev->ev_base->time_tv;
3173b6c3722Schristos ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
3183b6c3722Schristos ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
31987edd195Schristos while(ev->ev_timeout.tv_usec >= 1000000) {
3203b6c3722Schristos ev->ev_timeout.tv_usec -= 1000000;
3213b6c3722Schristos ev->ev_timeout.tv_sec++;
3223b6c3722Schristos }
3233b6c3722Schristos #endif
3243b6c3722Schristos (void)rbtree_insert(ev->ev_base->times, &ev->node);
3253b6c3722Schristos }
3263b6c3722Schristos ev->added = 1;
3273b6c3722Schristos return 0;
3283b6c3722Schristos }
3293b6c3722Schristos
3303b6c3722Schristos /* remove event, you may change it again */
event_del(struct event * ev)3313b6c3722Schristos int event_del(struct event* ev)
3323b6c3722Schristos {
3333b6c3722Schristos if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
3343b6c3722Schristos return -1;
3353b6c3722Schristos if((ev->ev_events&EV_TIMEOUT))
3363b6c3722Schristos (void)rbtree_delete(ev->ev_base->times, &ev->node);
3373b6c3722Schristos if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
3383b6c3722Schristos ev->ev_base->fds[ev->ev_fd] = NULL;
3393b6c3722Schristos FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
3403b6c3722Schristos FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
3413b6c3722Schristos FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
3423b6c3722Schristos FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
343*1481e2a9Schristos if(ev->ev_fd == ev->ev_base->maxfd) {
344*1481e2a9Schristos int i = ev->ev_base->maxfd - 1;
345*1481e2a9Schristos for (; i > 3; i--) {
346*1481e2a9Schristos if (NULL != ev->ev_base->fds[i]) {
347*1481e2a9Schristos break;
348*1481e2a9Schristos }
349*1481e2a9Schristos }
350*1481e2a9Schristos ev->ev_base->maxfd = i;
351*1481e2a9Schristos }
3523b6c3722Schristos }
3533b6c3722Schristos ev->added = 0;
3543b6c3722Schristos return 0;
3553b6c3722Schristos }
3563b6c3722Schristos
3573b6c3722Schristos /** which base gets to handle signals */
3583b6c3722Schristos static struct event_base* signal_base = NULL;
3593b6c3722Schristos /** signal handler */
sigh(int sig)3603b6c3722Schristos static RETSIGTYPE sigh(int sig)
3613b6c3722Schristos {
3623b6c3722Schristos struct event* ev;
3633b6c3722Schristos if(!signal_base || sig < 0 || sig >= MAX_SIG)
3643b6c3722Schristos return;
3653b6c3722Schristos ev = signal_base->signals[sig];
3663b6c3722Schristos if(!ev)
3673b6c3722Schristos return;
3683b6c3722Schristos fptr_ok(fptr_whitelist_event(ev->ev_callback));
3693b6c3722Schristos (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
3703b6c3722Schristos }
3713b6c3722Schristos
3723b6c3722Schristos /** install signal handler */
signal_add(struct event * ev,struct timeval * ATTR_UNUSED (tv))3733b6c3722Schristos int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
3743b6c3722Schristos {
3753b6c3722Schristos if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
3763b6c3722Schristos return -1;
3773b6c3722Schristos signal_base = ev->ev_base;
3783b6c3722Schristos ev->ev_base->signals[ev->ev_fd] = ev;
3793b6c3722Schristos ev->added = 1;
3803b6c3722Schristos if(signal(ev->ev_fd, sigh) == SIG_ERR) {
3813b6c3722Schristos return -1;
3823b6c3722Schristos }
3833b6c3722Schristos return 0;
3843b6c3722Schristos }
3853b6c3722Schristos
3863b6c3722Schristos /** remove signal handler */
signal_del(struct event * ev)3873b6c3722Schristos int signal_del(struct event* ev)
3883b6c3722Schristos {
3893b6c3722Schristos if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
3903b6c3722Schristos return -1;
3913b6c3722Schristos ev->ev_base->signals[ev->ev_fd] = NULL;
3923b6c3722Schristos ev->added = 0;
3933b6c3722Schristos return 0;
3943b6c3722Schristos }
3953b6c3722Schristos
3963b6c3722Schristos #else /* USE_MINI_EVENT */
3973b6c3722Schristos #ifndef USE_WINSOCK
mini_ev_cmp(const void * ATTR_UNUSED (a),const void * ATTR_UNUSED (b))3983b6c3722Schristos int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
3993b6c3722Schristos {
4003b6c3722Schristos return 0;
4013b6c3722Schristos }
4023b6c3722Schristos #endif /* not USE_WINSOCK */
4033b6c3722Schristos #endif /* USE_MINI_EVENT */
404