xref: /freebsd/contrib/unbound/util/mini_event.c (revision cc16dea626cf2fc80cde667ac4798065108e596c)
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 LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * 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 
49 #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
50 #include <signal.h>
51 #include "util/mini_event.h"
52 #include "util/fptr_wlist.h"
53 
54 /** compare events in tree, based on timevalue, ptr for uniqueness */
55 int mini_ev_cmp(const void* a, const void* b)
56 {
57 	const struct event *e = (const struct event*)a;
58 	const struct event *f = (const struct event*)b;
59 	if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
60 		return -1;
61 	if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
62 		return 1;
63 	if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
64 		return -1;
65 	if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
66 		return 1;
67 	if(e < f)
68 		return -1;
69 	if(e > f)
70 		return 1;
71 	return 0;
72 }
73 
74 /** set time */
75 static int
76 settime(struct event_base* base)
77 {
78 	if(gettimeofday(base->time_tv, NULL) < 0) {
79 		return -1;
80 	}
81 #ifndef S_SPLINT_S
82 	*base->time_secs = (uint32_t)base->time_tv->tv_sec;
83 #endif
84 	return 0;
85 }
86 
87 /** create event base */
88 void *event_init(uint32_t* time_secs, struct timeval* time_tv)
89 {
90 	struct event_base* base = (struct event_base*)malloc(
91 		sizeof(struct event_base));
92 	if(!base)
93 		return NULL;
94 	memset(base, 0, sizeof(*base));
95 	base->time_secs = time_secs;
96 	base->time_tv = time_tv;
97 	if(settime(base) < 0) {
98 		event_base_free(base);
99 		return NULL;
100 	}
101 	base->times = rbtree_create(mini_ev_cmp);
102 	if(!base->times) {
103 		event_base_free(base);
104 		return NULL;
105 	}
106 	base->capfd = MAX_FDS;
107 #ifdef FD_SETSIZE
108 	if((int)FD_SETSIZE < base->capfd)
109 		base->capfd = (int)FD_SETSIZE;
110 #endif
111 	base->fds = (struct event**)calloc((size_t)base->capfd,
112 		sizeof(struct event*));
113 	if(!base->fds) {
114 		event_base_free(base);
115 		return NULL;
116 	}
117 	base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
118 	if(!base->signals) {
119 		event_base_free(base);
120 		return NULL;
121 	}
122 #ifndef S_SPLINT_S
123 	FD_ZERO(&base->reads);
124 	FD_ZERO(&base->writes);
125 #endif
126 	return base;
127 }
128 
129 /** get version */
130 const char *event_get_version(void)
131 {
132 	return "mini-event-"PACKAGE_VERSION;
133 }
134 
135 /** get polling method, select */
136 const char *event_get_method(void)
137 {
138 	return "select";
139 }
140 
141 /** call timeouts handlers, and return how long to wait for next one or -1 */
142 static void handle_timeouts(struct event_base* base, struct timeval* now,
143 	struct timeval* wait)
144 {
145 	struct event* p;
146 #ifndef S_SPLINT_S
147 	wait->tv_sec = (time_t)-1;
148 #endif
149 
150 	while((rbnode_t*)(p = (struct event*)rbtree_first(base->times))
151 		!=RBTREE_NULL) {
152 #ifndef S_SPLINT_S
153 		if(p->ev_timeout.tv_sec > now->tv_sec ||
154 			(p->ev_timeout.tv_sec==now->tv_sec &&
155 		 	p->ev_timeout.tv_usec > now->tv_usec)) {
156 			/* there is a next larger timeout. wait for it */
157 			wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
158 			if(now->tv_usec > p->ev_timeout.tv_usec) {
159 				wait->tv_sec--;
160 				wait->tv_usec = 1000000 - (now->tv_usec -
161 					p->ev_timeout.tv_usec);
162 			} else {
163 				wait->tv_usec = p->ev_timeout.tv_usec
164 					- now->tv_usec;
165 			}
166 			return;
167 		}
168 #endif
169 		/* event times out, remove it */
170 		(void)rbtree_delete(base->times, p);
171 		p->ev_events &= ~EV_TIMEOUT;
172 		fptr_ok(fptr_whitelist_event(p->ev_callback));
173 		(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
174 	}
175 }
176 
177 /** call select and callbacks for that */
178 static int handle_select(struct event_base* base, struct timeval* wait)
179 {
180 	fd_set r, w;
181 	int ret, i;
182 
183 #ifndef S_SPLINT_S
184 	if(wait->tv_sec==(time_t)-1)
185 		wait = NULL;
186 #endif
187 	memmove(&r, &base->reads, sizeof(fd_set));
188 	memmove(&w, &base->writes, sizeof(fd_set));
189 	memmove(&base->ready, &base->content, sizeof(fd_set));
190 
191 	if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
192 		ret = errno;
193 		if(settime(base) < 0)
194 			return -1;
195 		errno = ret;
196 		if(ret == EAGAIN || ret == EINTR)
197 			return 0;
198 		return -1;
199 	}
200 	if(settime(base) < 0)
201 		return -1;
202 
203 	for(i=0; i<base->maxfd+1; i++) {
204 		short bits = 0;
205 		if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
206 			continue;
207 		}
208 		if(FD_ISSET(i, &r)) {
209 			bits |= EV_READ;
210 			ret--;
211 		}
212 		if(FD_ISSET(i, &w)) {
213 			bits |= EV_WRITE;
214 			ret--;
215 		}
216 		bits &= base->fds[i]->ev_events;
217 		if(bits) {
218 			fptr_ok(fptr_whitelist_event(
219 				base->fds[i]->ev_callback));
220 			(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
221 				bits, base->fds[i]->ev_arg);
222 			if(ret==0)
223 				break;
224 		}
225 	}
226 	return 0;
227 }
228 
229 /** run select in a loop */
230 int event_base_dispatch(struct event_base* base)
231 {
232 	struct timeval wait;
233 	if(settime(base) < 0)
234 		return -1;
235 	while(!base->need_to_exit)
236 	{
237 		/* see if timeouts need handling */
238 		handle_timeouts(base, base->time_tv, &wait);
239 		if(base->need_to_exit)
240 			return 0;
241 		/* do select */
242 		if(handle_select(base, &wait) < 0) {
243 			if(base->need_to_exit)
244 				return 0;
245 			return -1;
246 		}
247 	}
248 	return 0;
249 }
250 
251 /** exit that loop */
252 int event_base_loopexit(struct event_base* base,
253 	struct timeval* ATTR_UNUSED(tv))
254 {
255 	base->need_to_exit = 1;
256 	return 0;
257 }
258 
259 /* free event base, free events yourself */
260 void event_base_free(struct event_base* base)
261 {
262 	if(!base)
263 		return;
264 	if(base->times)
265 		free(base->times);
266 	if(base->fds)
267 		free(base->fds);
268 	if(base->signals)
269 		free(base->signals);
270 	free(base);
271 }
272 
273 /** set content of event */
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 */
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 */
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 */
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 	}
344 	ev->added = 0;
345 	return 0;
346 }
347 
348 /** which base gets to handle signals */
349 static struct event_base* signal_base = NULL;
350 /** signal handler */
351 static RETSIGTYPE sigh(int sig)
352 {
353 	struct event* ev;
354 	if(!signal_base || sig < 0 || sig >= MAX_SIG)
355 		return;
356 	ev = signal_base->signals[sig];
357 	if(!ev)
358 		return;
359 	fptr_ok(fptr_whitelist_event(ev->ev_callback));
360 	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
361 }
362 
363 /** install signal handler */
364 int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
365 {
366 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
367 		return -1;
368 	signal_base = ev->ev_base;
369 	ev->ev_base->signals[ev->ev_fd] = ev;
370 	ev->added = 1;
371 	if(signal(ev->ev_fd, sigh) == SIG_ERR) {
372 		return -1;
373 	}
374 	return 0;
375 }
376 
377 /** remove signal handler */
378 int signal_del(struct event* ev)
379 {
380 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
381 		return -1;
382 	ev->ev_base->signals[ev->ev_fd] = NULL;
383 	ev->added = 0;
384 	return 0;
385 }
386 
387 #else /* USE_MINI_EVENT */
388 #ifndef USE_WINSOCK
389 int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
390 {
391 	return 0;
392 }
393 #endif /* not USE_WINSOCK */
394 #endif /* USE_MINI_EVENT */
395