xref: /openbsd/usr.sbin/nsd/mini_event.c (revision cbbc2d6c)
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 <string.h>
48 #include <errno.h>
49 #include <sys/time.h>
50 
51 #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
52 #ifdef HAVE_WINSOCK2_H
53 #define FD_SET_T (u_int)
54 #else
55 #define FD_SET_T
56 #endif
57 
58 #include <signal.h>
59 #include "mini_event.h"
60 #include "util.h"
61 
62 /** compare events in tree, based on timevalue, ptr for uniqueness */
63 int
64 mini_ev_cmp(const void* a, const void* b)
65 {
66 	const struct event* e = (const struct event*)a;
67 	const struct event* f = (const struct event*)b;
68 	if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
69 		return -1;
70 	if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
71 		return 1;
72 	if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
73 		return -1;
74 	if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
75 		return 1;
76 	if(e < f)
77 		return -1;
78 	if(e > f)
79 		return 1;
80 	return 0;
81 }
82 
83 /** set time */
84 static int
85 settime(struct event_base* base)
86 {
87 	if(gettimeofday(base->time_tv, NULL) < 0) {
88 		return -1;
89 	}
90 #ifndef S_SPLINT_S
91 	*base->time_secs = (time_t)base->time_tv->tv_sec;
92 #endif
93 	return 0;
94 }
95 
96 /** create event base */
97 void *
98 event_init(time_t* time_secs, struct timeval* time_tv)
99 {
100 	struct event_base* base = (struct event_base*)malloc(
101 		sizeof(struct event_base));
102 	if(!base)
103 		return NULL;
104 	memset(base, 0, sizeof(*base));
105 	base->region = region_create(xalloc, free);
106 	if(!base->region) {
107 		free(base);
108 		return NULL;
109 	}
110 	base->time_secs = time_secs;
111 	base->time_tv = time_tv;
112 	if(settime(base) < 0) {
113 		event_base_free(base);
114 		return NULL;
115 	}
116 	base->times = rbtree_create(base->region, mini_ev_cmp);
117 	if(!base->times) {
118 		event_base_free(base);
119 		return NULL;
120 	}
121 	base->capfd = MAX_FDS;
122 #ifdef FD_SETSIZE
123 	if((int)FD_SETSIZE < base->capfd)
124 		base->capfd = (int)FD_SETSIZE;
125 #endif
126 	base->fds = (struct event**)calloc((size_t)base->capfd,
127 		sizeof(struct event*));
128 	if(!base->fds) {
129 		event_base_free(base);
130 		return NULL;
131 	}
132 	base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
133 	if(!base->signals) {
134 		event_base_free(base);
135 		return NULL;
136 	}
137 #ifndef S_SPLINT_S
138 	FD_ZERO(&base->reads);
139 	FD_ZERO(&base->writes);
140 #endif
141 	return base;
142 }
143 
144 /** get version */
145 const char *
146 event_get_version(void)
147 {
148 	return "mini-event-"PACKAGE_VERSION;
149 }
150 
151 /** get polling method, select */
152 const char *
153 event_get_method(void)
154 {
155 	return "select";
156 }
157 
158 /** call timeouts handlers, and return how long to wait for next one or -1 */
159 static int
160 handle_timeouts(struct event_base* base, struct timeval* now,
161 	struct timeval* wait)
162 {
163 	struct event* p;
164 	int tofired = 0;
165 #ifndef S_SPLINT_S
166 	wait->tv_sec = (time_t)-1;
167 #endif
168 
169 	while((rbnode_t*)(p = (struct event*)rbtree_first(base->times))
170 		!=RBTREE_NULL) {
171 #ifndef S_SPLINT_S
172 		if(p->ev_timeout.tv_sec > now->tv_sec ||
173 			(p->ev_timeout.tv_sec==now->tv_sec &&
174 		 	p->ev_timeout.tv_usec > now->tv_usec)) {
175 			/* there is a next larger timeout. wait for it */
176 			wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
177 			if(now->tv_usec > p->ev_timeout.tv_usec) {
178 				wait->tv_sec--;
179 				wait->tv_usec = 1000000 - (now->tv_usec -
180 					p->ev_timeout.tv_usec);
181 			} else {
182 				wait->tv_usec = p->ev_timeout.tv_usec
183 					- now->tv_usec;
184 			}
185 			return tofired;
186 		}
187 #endif
188 		/* event times out, remove it */
189 		tofired = 1;
190 		(void)rbtree_delete(base->times, p);
191 		p->ev_flags &= ~EV_TIMEOUT;
192 		(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
193 	}
194 	return tofired;
195 }
196 
197 /** call select and callbacks for that */
198 static int
199 handle_select(struct event_base* base, struct timeval* wait)
200 {
201 	fd_set r, w;
202 	int ret, i;
203 
204 #ifndef S_SPLINT_S
205 	if(wait->tv_sec==(time_t)-1)
206 		wait = NULL;
207 #endif
208 	memmove(&r, &base->reads, sizeof(fd_set));
209 	memmove(&w, &base->writes, sizeof(fd_set));
210 	memmove(&base->ready, &base->content, sizeof(fd_set));
211 
212 	if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
213 		ret = errno;
214 		if(settime(base) < 0)
215 			return -1;
216 		errno = ret;
217 		if(ret == EAGAIN || ret == EINTR)
218 			return 0;
219 		return -1;
220 	}
221 	if(settime(base) < 0)
222 		return -1;
223 
224 	for(i=0; i<base->maxfd+1; i++) {
225 		short bits = 0;
226 		if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
227 			continue;
228 		}
229 		if(FD_ISSET(i, &r)) {
230 			bits |= EV_READ;
231 			ret--;
232 		}
233 		if(FD_ISSET(i, &w)) {
234 			bits |= EV_WRITE;
235 			ret--;
236 		}
237 		bits &= base->fds[i]->ev_flags;
238 		if(bits) {
239 			(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
240 				bits, base->fds[i]->ev_arg);
241 			if(ret==0)
242 				break;
243 		}
244 	}
245 	return 0;
246 }
247 
248 /** run select once */
249 int
250 event_base_loop(struct event_base* base, int flags)
251 {
252 	struct timeval wait;
253 	if(!(flags & EVLOOP_ONCE))
254 		return event_base_dispatch(base);
255 	/* see if timeouts need handling */
256 	if(handle_timeouts(base, base->time_tv, &wait))
257 		return 0; /* there were timeouts, end of loop */
258 	if(base->need_to_exit)
259 		return 0;
260 	/* do select */
261 	if(handle_select(base, &wait) < 0) {
262 		if(base->need_to_exit)
263 			return 0;
264 		return -1;
265 	}
266 	return 0;
267 }
268 
269 /** run select in a loop */
270 int
271 event_base_dispatch(struct event_base* base)
272 {
273 	struct timeval wait;
274 	if(settime(base) < 0)
275 		return -1;
276 	while(!base->need_to_exit)
277 	{
278 		/* see if timeouts need handling */
279 		(void)handle_timeouts(base, base->time_tv, &wait);
280 		if(base->need_to_exit)
281 			return 0;
282 		/* do select */
283 		if(handle_select(base, &wait) < 0) {
284 			if(base->need_to_exit)
285 				return 0;
286 			return -1;
287 		}
288 	}
289 	return 0;
290 }
291 
292 /** exit that loop */
293 int
294 event_base_loopexit(struct event_base* base,
295 	struct timeval* ATTR_UNUSED(tv))
296 {
297 	base->need_to_exit = 1;
298 	return 0;
299 }
300 
301 /* free event base, free events yourself */
302 void
303 event_base_free(struct event_base* base)
304 {
305 	if(!base)
306 		return;
307 	if(base->times)
308 		free(base->times);
309 	if(base->fds)
310 		free(base->fds);
311 	if(base->signals)
312 		free(base->signals);
313 	region_destroy(base->region);
314 	free(base);
315 }
316 
317 /** set content of event */
318 void
319 event_set(struct event* ev, int fd, short bits,
320 	void (*cb)(int, short, void *), void* arg)
321 {
322 	ev->node.key = ev;
323 	ev->ev_fd = fd;
324 	ev->ev_flags = bits;
325 	ev->ev_callback = cb;
326 	ev->ev_arg = arg;
327 	ev->added = 0;
328 }
329 
330 /* add event to a base */
331 int
332 event_base_set(struct event_base* base, struct event* ev)
333 {
334 	ev->ev_base = base;
335 	ev->added = 0;
336 	return 0;
337 }
338 
339 /* add event to make it active, you may not change it with event_set anymore */
340 int
341 event_add(struct event* ev, struct timeval* tv)
342 {
343 	if(ev->added)
344 		event_del(ev);
345 	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
346 		return -1;
347 	if( (ev->ev_flags&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
348 		ev->ev_base->fds[ev->ev_fd] = ev;
349 		if(ev->ev_flags&EV_READ) {
350 			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
351 		}
352 		if(ev->ev_flags&EV_WRITE) {
353 			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
354 		}
355 		FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
356 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
357 		if(ev->ev_fd > ev->ev_base->maxfd)
358 			ev->ev_base->maxfd = ev->ev_fd;
359 	}
360 	if(tv && (ev->ev_flags&EV_TIMEOUT)) {
361 #ifndef S_SPLINT_S
362 		struct timeval* now = ev->ev_base->time_tv;
363 		ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
364 		ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
365 		while(ev->ev_timeout.tv_usec > 1000000) {
366 			ev->ev_timeout.tv_usec -= 1000000;
367 			ev->ev_timeout.tv_sec++;
368 		}
369 #endif
370 		(void)rbtree_insert(ev->ev_base->times, &ev->node);
371 	}
372 	ev->added = 1;
373 	return 0;
374 }
375 
376 /* remove event, you may change it again */
377 int
378 event_del(struct event* ev)
379 {
380 	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
381 		return -1;
382 	if((ev->ev_flags&EV_TIMEOUT))
383 		(void)rbtree_delete(ev->ev_base->times, &ev->node);
384 	if((ev->ev_flags&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
385 		ev->ev_base->fds[ev->ev_fd] = NULL;
386 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
387 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
388 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
389 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
390 	}
391 	ev->added = 0;
392 	return 0;
393 }
394 
395 /** which base gets to handle signals */
396 static struct event_base* signal_base = NULL;
397 
398 /** signal handler */
399 static RETSIGTYPE
400 sigh(int sig)
401 {
402 	struct event* ev;
403 	if(!signal_base || sig < 0 || sig >= MAX_SIG)
404 		return;
405 	ev = signal_base->signals[sig];
406 	if(!ev)
407 		return;
408 	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
409 }
410 
411 /** install signal handler */
412 int
413 signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
414 {
415 	struct sigaction action;
416 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
417 		return -1;
418 	signal_base = ev->ev_base;
419 	ev->ev_base->signals[ev->ev_fd] = ev;
420 	ev->added = 1;
421 	action.sa_handler = sigh;
422 	sigfillset(&action.sa_mask);
423 	action.sa_flags = 0;
424 	return sigaction(ev->ev_fd, &action, NULL);
425 }
426 
427 /** remove signal handler */
428 int
429 signal_del(struct event* ev)
430 {
431 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
432 		return -1;
433 	ev->ev_base->signals[ev->ev_fd] = NULL;
434 	ev->added = 0;
435 	return 0;
436 }
437 
438 #else /* USE_MINI_EVENT */
439 #ifndef USE_WINSOCK
440 int
441 mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
442 {
443 	return 0;
444 }
445 #endif /* not USE_WINSOCK */
446 #endif /* USE_MINI_EVENT */
447