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 #include "util/mini_event.h"
45 #ifdef HAVE_TIME_H
46 #include <time.h>
47 #endif
48 #include <sys/time.h>
49
50 #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
51 #include <signal.h>
52 #include "util/fptr_wlist.h"
53
54 /** compare events in tree, based on timevalue, ptr for uniqueness */
mini_ev_cmp(const void * a,const void * b)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
settime(struct event_base * base)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 */
event_init(time_t * time_secs,struct timeval * time_tv)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 */
event_get_version(void)130 const char *event_get_version(void)
131 {
132 return "mini-event-"PACKAGE_VERSION;
133 }
134
135 /** get polling method, select */
event_get_method(void)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 */
handle_timeouts(struct event_base * base,struct timeval * now,struct timeval * wait)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_type*)(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 */
handle_select(struct event_base * base,struct timeval * wait)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 */
event_base_dispatch(struct event_base * base)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 */
event_base_loopexit(struct event_base * base,struct timeval * ATTR_UNUSED (tv))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 */
event_base_free(struct event_base * base)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 */
event_set(struct event * ev,int fd,short bits,void (* cb)(int,short,void *),void * arg)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 */
event_base_set(struct event_base * base,struct event * ev)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 */
event_add(struct event * ev,struct timeval * tv)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 */
event_del(struct event * ev)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 if(ev->ev_fd == ev->ev_base->maxfd) {
341 int i = ev->ev_base->maxfd - 1;
342 for (; i > 3; i--) {
343 if (NULL != ev->ev_base->fds[i]) {
344 break;
345 }
346 }
347 ev->ev_base->maxfd = i;
348 }
349 }
350 ev->added = 0;
351 return 0;
352 }
353
354 /** which base gets to handle signals */
355 static struct event_base* signal_base = NULL;
356 /** signal handler */
sigh(int sig)357 static RETSIGTYPE sigh(int sig)
358 {
359 struct event* ev;
360 if(!signal_base || sig < 0 || sig >= MAX_SIG)
361 return;
362 ev = signal_base->signals[sig];
363 if(!ev)
364 return;
365 fptr_ok(fptr_whitelist_event(ev->ev_callback));
366 (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
367 }
368
369 /** install signal handler */
signal_add(struct event * ev,struct timeval * ATTR_UNUSED (tv))370 int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
371 {
372 if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
373 return -1;
374 signal_base = ev->ev_base;
375 ev->ev_base->signals[ev->ev_fd] = ev;
376 ev->added = 1;
377 if(signal(ev->ev_fd, sigh) == SIG_ERR) {
378 return -1;
379 }
380 return 0;
381 }
382
383 /** remove signal handler */
signal_del(struct event * ev)384 int signal_del(struct event* ev)
385 {
386 if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
387 return -1;
388 ev->ev_base->signals[ev->ev_fd] = NULL;
389 ev->added = 0;
390 return 0;
391 }
392
393 #else /* USE_MINI_EVENT */
394 #ifndef USE_WINSOCK
mini_ev_cmp(const void * ATTR_UNUSED (a),const void * ATTR_UNUSED (b))395 int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
396 {
397 return 0;
398 }
399 #endif /* not USE_WINSOCK */
400 #endif /* USE_MINI_EVENT */
401