xref: /netbsd-src/external/bsd/unbound/dist/util/mini_event.c (revision 1481e2a94f6072bae608596a6abf63e95484c3ea)
1 /*
2  * mini_event.c - implementation of part of libevent api, portably.
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 
37 /**
38  * \file
39  * fake libevent implementation. Less broad in functionality, and only
40  * supports select(2).
41  */
42 
43 #include "config.h"
44 #ifdef HAVE_TIME_H
45 #include <time.h>
46 #endif
47 #include <sys/time.h>
48 #include "util/mini_event.h"
49 
50 #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
51 #include <signal.h>
52 #include "util/fptr_wlist.h"
53 
54 /* Enforce a correct include/config.h as I tire of fixin it. */
55 #error This code should not be active on NetBSD, please use libevent.
56 
57 /** compare events in tree, based on timevalue, ptr for uniqueness */
mini_ev_cmp(const void * a,const void * b)58 int mini_ev_cmp(const void* a, const void* b)
59 {
60 	const struct event *e = (const struct event*)a;
61 	const struct event *f = (const struct event*)b;
62 	if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
63 		return -1;
64 	if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
65 		return 1;
66 	if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
67 		return -1;
68 	if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
69 		return 1;
70 	if(e < f)
71 		return -1;
72 	if(e > f)
73 		return 1;
74 	return 0;
75 }
76 
77 /** set time */
78 static int
settime(struct event_base * base)79 settime(struct event_base* base)
80 {
81 	if(gettimeofday(base->time_tv, NULL) < 0) {
82 		return -1;
83 	}
84 #ifndef S_SPLINT_S
85 	*base->time_secs = (time_t)base->time_tv->tv_sec;
86 #endif
87 	return 0;
88 }
89 
90 /** create event base */
event_init(time_t * time_secs,struct timeval * time_tv)91 void *event_init(time_t* time_secs, struct timeval* time_tv)
92 {
93 	struct event_base* base = (struct event_base*)malloc(
94 		sizeof(struct event_base));
95 	if(!base)
96 		return NULL;
97 	memset(base, 0, sizeof(*base));
98 	base->time_secs = time_secs;
99 	base->time_tv = time_tv;
100 	if(settime(base) < 0) {
101 		event_base_free(base);
102 		return NULL;
103 	}
104 	base->times = rbtree_create(mini_ev_cmp);
105 	if(!base->times) {
106 		event_base_free(base);
107 		return NULL;
108 	}
109 	base->capfd = MAX_FDS;
110 #ifdef FD_SETSIZE
111 	if((int)FD_SETSIZE < base->capfd)
112 		base->capfd = (int)FD_SETSIZE;
113 #endif
114 	base->fds = (struct event**)calloc((size_t)base->capfd,
115 		sizeof(struct event*));
116 	if(!base->fds) {
117 		event_base_free(base);
118 		return NULL;
119 	}
120 	base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
121 	if(!base->signals) {
122 		event_base_free(base);
123 		return NULL;
124 	}
125 #ifndef S_SPLINT_S
126 	FD_ZERO(&base->reads);
127 	FD_ZERO(&base->writes);
128 #endif
129 	return base;
130 }
131 
132 /** get version */
event_get_version(void)133 const char *event_get_version(void)
134 {
135 	return "mini-event-"PACKAGE_VERSION;
136 }
137 
138 /** get polling method, select */
event_get_method(void)139 const char *event_get_method(void)
140 {
141 	return "select";
142 }
143 
144 /** 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)145 static void handle_timeouts(struct event_base* base, struct timeval* now,
146 	struct timeval* wait)
147 {
148 	struct event* p;
149 #ifndef S_SPLINT_S
150 	wait->tv_sec = (time_t)-1;
151 #endif
152 
153 	while((rbnode_type*)(p = (struct event*)rbtree_first(base->times))
154 		!=RBTREE_NULL) {
155 #ifndef S_SPLINT_S
156 		if(p->ev_timeout.tv_sec > now->tv_sec ||
157 			(p->ev_timeout.tv_sec==now->tv_sec &&
158 		 	p->ev_timeout.tv_usec > now->tv_usec)) {
159 			/* there is a next larger timeout. wait for it */
160 			wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
161 			if(now->tv_usec > p->ev_timeout.tv_usec) {
162 				wait->tv_sec--;
163 				wait->tv_usec = 1000000 - (now->tv_usec -
164 					p->ev_timeout.tv_usec);
165 			} else {
166 				wait->tv_usec = p->ev_timeout.tv_usec
167 					- now->tv_usec;
168 			}
169 			return;
170 		}
171 #endif
172 		/* event times out, remove it */
173 		(void)rbtree_delete(base->times, p);
174 		p->ev_events &= ~EV_TIMEOUT;
175 		fptr_ok(fptr_whitelist_event(p->ev_callback));
176 		(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
177 	}
178 }
179 
180 /** call select and callbacks for that */
handle_select(struct event_base * base,struct timeval * wait)181 static int handle_select(struct event_base* base, struct timeval* wait)
182 {
183 	fd_set r, w;
184 	int ret, i;
185 
186 #ifndef S_SPLINT_S
187 	if(wait->tv_sec==(time_t)-1)
188 		wait = NULL;
189 #endif
190 	memmove(&r, &base->reads, sizeof(fd_set));
191 	memmove(&w, &base->writes, sizeof(fd_set));
192 	memmove(&base->ready, &base->content, sizeof(fd_set));
193 
194 	if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
195 		ret = errno;
196 		if(settime(base) < 0)
197 			return -1;
198 		errno = ret;
199 		if(ret == EAGAIN || ret == EINTR)
200 			return 0;
201 		return -1;
202 	}
203 	if(settime(base) < 0)
204 		return -1;
205 
206 	for(i=0; i<base->maxfd+1; i++) {
207 		short bits = 0;
208 		if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
209 			continue;
210 		}
211 		if(FD_ISSET(i, &r)) {
212 			bits |= EV_READ;
213 			ret--;
214 		}
215 		if(FD_ISSET(i, &w)) {
216 			bits |= EV_WRITE;
217 			ret--;
218 		}
219 		bits &= base->fds[i]->ev_events;
220 		if(bits) {
221 			fptr_ok(fptr_whitelist_event(
222 				base->fds[i]->ev_callback));
223 			(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
224 				bits, base->fds[i]->ev_arg);
225 			if(ret==0)
226 				break;
227 		}
228 	}
229 	return 0;
230 }
231 
232 /** run select in a loop */
event_base_dispatch(struct event_base * base)233 int event_base_dispatch(struct event_base* base)
234 {
235 	struct timeval wait;
236 	if(settime(base) < 0)
237 		return -1;
238 	while(!base->need_to_exit)
239 	{
240 		/* see if timeouts need handling */
241 		handle_timeouts(base, base->time_tv, &wait);
242 		if(base->need_to_exit)
243 			return 0;
244 		/* do select */
245 		if(handle_select(base, &wait) < 0) {
246 			if(base->need_to_exit)
247 				return 0;
248 			return -1;
249 		}
250 	}
251 	return 0;
252 }
253 
254 /** exit that loop */
event_base_loopexit(struct event_base * base,struct timeval * ATTR_UNUSED (tv))255 int event_base_loopexit(struct event_base* base,
256 	struct timeval* ATTR_UNUSED(tv))
257 {
258 	base->need_to_exit = 1;
259 	return 0;
260 }
261 
262 /* free event base, free events yourself */
event_base_free(struct event_base * base)263 void event_base_free(struct event_base* base)
264 {
265 	if(!base)
266 		return;
267 	free(base->times);
268 	free(base->fds);
269 	free(base->signals);
270 	free(base);
271 }
272 
273 /** set content of event */
event_set(struct event * ev,int fd,short bits,void (* cb)(int,short,void *),void * arg)274 void event_set(struct event* ev, int fd, short bits,
275 	void (*cb)(int, short, void *), void* arg)
276 {
277 	ev->node.key = ev;
278 	ev->ev_fd = fd;
279 	ev->ev_events = bits;
280 	ev->ev_callback = cb;
281 	fptr_ok(fptr_whitelist_event(ev->ev_callback));
282 	ev->ev_arg = arg;
283 	ev->added = 0;
284 }
285 
286 /* add event to a base */
event_base_set(struct event_base * base,struct event * ev)287 int event_base_set(struct event_base* base, struct event* ev)
288 {
289 	ev->ev_base = base;
290 	ev->added = 0;
291 	return 0;
292 }
293 
294 /* add event to make it active, you may not change it with event_set anymore */
event_add(struct event * ev,struct timeval * tv)295 int event_add(struct event* ev, struct timeval* tv)
296 {
297 	if(ev->added)
298 		event_del(ev);
299 	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
300 		return -1;
301 	if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
302 		ev->ev_base->fds[ev->ev_fd] = ev;
303 		if(ev->ev_events&EV_READ) {
304 			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
305 		}
306 		if(ev->ev_events&EV_WRITE) {
307 			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
308 		}
309 		FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
310 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
311 		if(ev->ev_fd > ev->ev_base->maxfd)
312 			ev->ev_base->maxfd = ev->ev_fd;
313 	}
314 	if(tv && (ev->ev_events&EV_TIMEOUT)) {
315 #ifndef S_SPLINT_S
316 		struct timeval *now = ev->ev_base->time_tv;
317 		ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
318 		ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
319 		while(ev->ev_timeout.tv_usec >= 1000000) {
320 			ev->ev_timeout.tv_usec -= 1000000;
321 			ev->ev_timeout.tv_sec++;
322 		}
323 #endif
324 		(void)rbtree_insert(ev->ev_base->times, &ev->node);
325 	}
326 	ev->added = 1;
327 	return 0;
328 }
329 
330 /* remove event, you may change it again */
event_del(struct event * ev)331 int event_del(struct event* ev)
332 {
333 	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
334 		return -1;
335 	if((ev->ev_events&EV_TIMEOUT))
336 		(void)rbtree_delete(ev->ev_base->times, &ev->node);
337 	if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
338 		ev->ev_base->fds[ev->ev_fd] = NULL;
339 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
340 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
341 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
342 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
343 		if(ev->ev_fd == ev->ev_base->maxfd) {
344                         int i = ev->ev_base->maxfd - 1;
345                         for (; i > 3; i--) {
346                                 if (NULL != ev->ev_base->fds[i]) {
347                                         break;
348                                 }
349                         }
350                         ev->ev_base->maxfd = i;
351                 }
352 	}
353 	ev->added = 0;
354 	return 0;
355 }
356 
357 /** which base gets to handle signals */
358 static struct event_base* signal_base = NULL;
359 /** signal handler */
sigh(int sig)360 static RETSIGTYPE sigh(int sig)
361 {
362 	struct event* ev;
363 	if(!signal_base || sig < 0 || sig >= MAX_SIG)
364 		return;
365 	ev = signal_base->signals[sig];
366 	if(!ev)
367 		return;
368 	fptr_ok(fptr_whitelist_event(ev->ev_callback));
369 	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
370 }
371 
372 /** install signal handler */
signal_add(struct event * ev,struct timeval * ATTR_UNUSED (tv))373 int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
374 {
375 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
376 		return -1;
377 	signal_base = ev->ev_base;
378 	ev->ev_base->signals[ev->ev_fd] = ev;
379 	ev->added = 1;
380 	if(signal(ev->ev_fd, sigh) == SIG_ERR) {
381 		return -1;
382 	}
383 	return 0;
384 }
385 
386 /** remove signal handler */
signal_del(struct event * ev)387 int signal_del(struct event* ev)
388 {
389 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
390 		return -1;
391 	ev->ev_base->signals[ev->ev_fd] = NULL;
392 	ev->added = 0;
393 	return 0;
394 }
395 
396 #else /* USE_MINI_EVENT */
397 #ifndef USE_WINSOCK
mini_ev_cmp(const void * ATTR_UNUSED (a),const void * ATTR_UNUSED (b))398 int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
399 {
400 	return 0;
401 }
402 #endif /* not USE_WINSOCK */
403 #endif /* USE_MINI_EVENT */
404