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