xref: /freebsd/contrib/unbound/util/mini_event.c (revision d6b92ffa)
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 
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 = (time_t)base->time_tv->tv_sec;
83 #endif
84 	return 0;
85 }
86 
87 /** create event base */
88 void *event_init(time_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 	free(base->times);
265 	free(base->fds);
266 	free(base->signals);
267 	free(base);
268 }
269 
270 /** set content of event */
271 void event_set(struct event* ev, int fd, short bits,
272 	void (*cb)(int, short, void *), void* arg)
273 {
274 	ev->node.key = ev;
275 	ev->ev_fd = fd;
276 	ev->ev_events = bits;
277 	ev->ev_callback = cb;
278 	fptr_ok(fptr_whitelist_event(ev->ev_callback));
279 	ev->ev_arg = arg;
280 	ev->added = 0;
281 }
282 
283 /* add event to a base */
284 int event_base_set(struct event_base* base, struct event* ev)
285 {
286 	ev->ev_base = base;
287 	ev->added = 0;
288 	return 0;
289 }
290 
291 /* add event to make it active, you may not change it with event_set anymore */
292 int event_add(struct event* ev, struct timeval* tv)
293 {
294 	if(ev->added)
295 		event_del(ev);
296 	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
297 		return -1;
298 	if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
299 		ev->ev_base->fds[ev->ev_fd] = ev;
300 		if(ev->ev_events&EV_READ) {
301 			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
302 		}
303 		if(ev->ev_events&EV_WRITE) {
304 			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
305 		}
306 		FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
307 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
308 		if(ev->ev_fd > ev->ev_base->maxfd)
309 			ev->ev_base->maxfd = ev->ev_fd;
310 	}
311 	if(tv && (ev->ev_events&EV_TIMEOUT)) {
312 #ifndef S_SPLINT_S
313 		struct timeval *now = ev->ev_base->time_tv;
314 		ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
315 		ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
316 		while(ev->ev_timeout.tv_usec > 1000000) {
317 			ev->ev_timeout.tv_usec -= 1000000;
318 			ev->ev_timeout.tv_sec++;
319 		}
320 #endif
321 		(void)rbtree_insert(ev->ev_base->times, &ev->node);
322 	}
323 	ev->added = 1;
324 	return 0;
325 }
326 
327 /* remove event, you may change it again */
328 int event_del(struct event* ev)
329 {
330 	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
331 		return -1;
332 	if((ev->ev_events&EV_TIMEOUT))
333 		(void)rbtree_delete(ev->ev_base->times, &ev->node);
334 	if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
335 		ev->ev_base->fds[ev->ev_fd] = NULL;
336 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
337 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
338 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
339 		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
340 	}
341 	ev->added = 0;
342 	return 0;
343 }
344 
345 /** which base gets to handle signals */
346 static struct event_base* signal_base = NULL;
347 /** signal handler */
348 static RETSIGTYPE sigh(int sig)
349 {
350 	struct event* ev;
351 	if(!signal_base || sig < 0 || sig >= MAX_SIG)
352 		return;
353 	ev = signal_base->signals[sig];
354 	if(!ev)
355 		return;
356 	fptr_ok(fptr_whitelist_event(ev->ev_callback));
357 	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
358 }
359 
360 /** install signal handler */
361 int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
362 {
363 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
364 		return -1;
365 	signal_base = ev->ev_base;
366 	ev->ev_base->signals[ev->ev_fd] = ev;
367 	ev->added = 1;
368 	if(signal(ev->ev_fd, sigh) == SIG_ERR) {
369 		return -1;
370 	}
371 	return 0;
372 }
373 
374 /** remove signal handler */
375 int signal_del(struct event* ev)
376 {
377 	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
378 		return -1;
379 	ev->ev_base->signals[ev->ev_fd] = NULL;
380 	ev->added = 0;
381 	return 0;
382 }
383 
384 #else /* USE_MINI_EVENT */
385 #ifndef USE_WINSOCK
386 int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
387 {
388 	return 0;
389 }
390 #endif /* not USE_WINSOCK */
391 #endif /* USE_MINI_EVENT */
392