New build path variable
[freeradius.git] / src / lib / event.c
1 /*
2  * event.c      Non-thread-safe event handling, specific to a RADIUS
3  *              server.
4  *
5  * Version:     $Id$
6  *
7  *   This library is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU Lesser General Public
9  *   License as published by the Free Software Foundation; either
10  *   version 2.1 of the License, or (at your option) any later version.
11  *
12  *   This library is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  *   Lesser General Public License for more details.
16  *
17  *   You should have received a copy of the GNU Lesser General Public
18  *   License along with this library; if not, write to the Free Software
19  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  *  Copyright 2007  The FreeRADIUS server project
22  *  Copyright 2007  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/libradius.h>
29 #include <freeradius-devel/heap.h>
30 #include <freeradius-devel/event.h>
31
32 typedef struct fr_event_fd_t {
33         int                     fd;
34         fr_event_fd_handler_t   handler;
35         void                    *ctx;
36 } fr_event_fd_t;
37
38 #define FR_EV_MAX_FDS (256)
39 #undef USEC
40 #define USEC (1000000)
41
42 struct fr_event_list_t {
43         fr_heap_t       *times;
44
45         int             changed;
46
47         int             exit;
48
49         fr_event_status_t status;
50
51         struct timeval  now;
52         int             dispatch;
53
54         int             max_readers;
55         fr_event_fd_t   readers[FR_EV_MAX_FDS];
56 };
57
58 /*
59  *      Internal structure for managing events.
60  */
61 struct fr_event_t {
62         fr_event_callback_t     callback;
63         void                    *ctx;
64         struct timeval          when;
65         fr_event_t              **ev_p;
66         int                     heap;
67 };
68
69
70 static int fr_event_list_time_cmp(const void *one, const void *two)
71 {
72         const fr_event_t *a = one;
73         const fr_event_t *b = two;
74
75         if (a->when.tv_sec < b->when.tv_sec) return -1;
76         if (a->when.tv_sec > b->when.tv_sec) return +1;
77
78         if (a->when.tv_usec < b->when.tv_usec) return -1;
79         if (a->when.tv_usec > b->when.tv_usec) return +1;
80
81         return 0;
82 }
83
84
85 void fr_event_list_free(fr_event_list_t *el)
86 {
87         fr_event_t *ev;
88
89         if (!el) return;
90
91         while ((ev = fr_heap_peek(el->times)) != NULL) {
92                 fr_event_delete(el, &ev);
93         }
94
95         fr_heap_delete(el->times);
96         free(el);
97 }
98
99
100 fr_event_list_t *fr_event_list_create(fr_event_status_t status)
101 {
102         int i;
103         fr_event_list_t *el;
104
105         el = malloc(sizeof(*el));
106         if (!el) return NULL;
107         memset(el, 0, sizeof(*el));
108
109         el->times = fr_heap_create(fr_event_list_time_cmp, 
110                                    offsetof(fr_event_t, heap));
111         if (!el->times) {
112                 fr_event_list_free(el);
113                 return NULL;
114         }
115
116         for (i = 0; i < FR_EV_MAX_FDS; i++) {
117                 el->readers[i].fd = -1;
118         }
119
120         el->status = status;
121         el->changed = 1;        /* force re-set of fds's */
122
123         return el;
124 }
125
126 int fr_event_list_num_elements(fr_event_list_t *el)
127 {
128         if (!el) return 0;
129
130         return fr_heap_num_elements(el->times);
131 }
132
133
134 int fr_event_delete(fr_event_list_t *el, fr_event_t **ev_p)
135 {
136         fr_event_t *ev;
137
138         if (!el || !ev_p || !*ev_p) return 0;
139
140         ev = *ev_p;
141         if (ev->ev_p) *(ev->ev_p) = NULL;
142         *ev_p = NULL;
143
144         fr_heap_extract(el->times, ev);
145         free(ev);
146
147         return 1;
148 }
149
150
151 int fr_event_insert(fr_event_list_t *el,
152                       fr_event_callback_t callback,
153                       void *ctx, struct timeval *when,
154                       fr_event_t **ev_p)
155 {
156         fr_event_t *ev;
157
158         if (!el || !callback | !when || (when->tv_usec > USEC)) return 0;
159
160         if (ev_p && *ev_p) fr_event_delete(el, ev_p);
161
162         ev = malloc(sizeof(*ev));
163         if (!ev) return 0;
164         memset(ev, 0, sizeof(*ev));
165
166         ev->callback = callback;
167         ev->ctx = ctx;
168         ev->when = *when;
169         ev->ev_p = ev_p;
170
171         if (!fr_heap_insert(el->times, ev)) {
172                 free(ev);
173                 return 0;
174         }
175
176         if (ev_p) *ev_p = ev;
177         return 1;
178 }
179
180
181 int fr_event_run(fr_event_list_t *el, struct timeval *when)
182 {
183         fr_event_callback_t callback;
184         void *ctx;
185         fr_event_t *ev;
186
187         if (!el) return 0;
188
189         if (fr_heap_num_elements(el->times) == 0) {
190                 when->tv_sec = 0;
191                 when->tv_usec = 0;
192                 return 0;
193         }
194
195         ev = fr_heap_peek(el->times);
196         if (!ev) {
197                 when->tv_sec = 0;
198                 when->tv_usec = 0;
199                 return 0;
200         }
201
202         /*
203          *      See if it's time to do this one.
204          */
205         if ((ev->when.tv_sec > when->tv_sec) ||
206             ((ev->when.tv_sec == when->tv_sec) &&
207              (ev->when.tv_usec > when->tv_usec))) {
208                 *when = ev->when;
209                 return 0;
210         }
211
212         callback = ev->callback;
213         ctx = ev->ctx;
214
215         /*
216          *      Delete the event before calling it.
217          */
218         fr_event_delete(el, &ev);
219
220         callback(ctx);
221         return 1;
222 }
223
224
225 int fr_event_now(fr_event_list_t *el, struct timeval *when)
226 {
227         if (!when) return 0;
228
229         if (el && el->dispatch) {
230                 *when = el->now;
231         } else {
232                 gettimeofday(when, NULL);
233         }
234
235         return 1;
236 }
237
238
239 int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
240                        fr_event_fd_handler_t handler, void *ctx)
241 {
242         int i;
243         fr_event_fd_t *ef;
244
245         if (!el || (fd < 0) || !handler || !ctx) return 0;
246
247         if (type != 0) return 0;
248
249         if (el->max_readers >= FR_EV_MAX_FDS) return 0;
250
251         ef = NULL;
252         for (i = 0; i <= el->max_readers; i++) {
253                 /*
254                  *      Be fail-safe on multiple inserts.
255                  */
256                 if (el->readers[i].fd == fd) {
257                         if ((el->readers[i].handler != handler) ||
258                             (el->readers[i].ctx != ctx)) {
259                                 return 0;
260                         }
261
262                         /*
263                          *      No change.
264                          */
265                         return 1;
266                 }
267
268                 if (el->readers[i].fd < 0) {
269                         ef = &el->readers[i];
270
271                         if (i == el->max_readers) el->max_readers = i + 1;
272                         break;
273                 }
274         }
275
276         if (!ef) return 0;
277
278         ef->handler = handler;
279         ef->ctx = ctx;
280         ef->fd = fd;
281
282         el->changed = 1;
283
284         return 1;
285 }
286
287 int fr_event_fd_delete(fr_event_list_t *el, int type, int fd)
288 {
289         int i;
290
291         if (!el || (fd < 0)) return 0;
292
293         if (type != 0) return 0;
294
295         for (i = 0; i < el->max_readers; i++) {
296                 if (el->readers[i].fd == fd) {
297                         el->readers[i].fd = -1;
298                         if ((i + 1) == el->max_readers) el->max_readers = i;
299                         el->changed = 1;
300                         return 1;
301                 }
302         }
303
304         return 0;
305 }                        
306
307
308 void fr_event_loop_exit(fr_event_list_t *el, int code)
309 {
310         if (!el) return;
311
312         el->exit = code;
313 }
314
315
316 int fr_event_loop(fr_event_list_t *el)
317 {
318         int i, rcode, maxfd = 0;
319         struct timeval when, *wake;
320         fd_set read_fds, master_fds;
321
322         el->exit = 0;
323         el->dispatch = 1;
324         el->changed = 1;
325
326         while (!el->exit) {
327                 /*
328                  *      Cache the list of FD's to watch.
329                  */
330                 if (el->changed) {
331                         FD_ZERO(&master_fds);
332                         
333                         for (i = 0; i < el->max_readers; i++) {
334                                 if (el->readers[i].fd < 0) continue;
335                                 
336                                 if (el->readers[i].fd > maxfd) {
337                                         maxfd = el->readers[i].fd;
338                                 }
339                                 FD_SET(el->readers[i].fd, &master_fds);
340                         }
341                         
342                         el->changed = 0;
343                 }
344
345                 /*
346                  *      Find the first event.  If there's none, we wait
347                  *      on the socket forever.
348                  */
349                 when.tv_sec = 0;
350                 when.tv_usec = 0;
351
352                 if (fr_heap_num_elements(el->times) > 0) {
353                         fr_event_t *ev;
354
355                         ev = fr_heap_peek(el->times);
356                         if (!ev) _exit(42);
357
358                         gettimeofday(&el->now, NULL);
359
360                         if (timercmp(&el->now, &ev->when, <)) {
361                                 when = ev->when;
362                                 when.tv_sec -= el->now.tv_sec;
363
364                                 if (when.tv_sec > 0) {
365                                         when.tv_sec--;
366                                         when.tv_usec += USEC;
367                                 }
368                                 when.tv_usec -= el->now.tv_usec;
369                                 if (when.tv_usec > USEC) {
370                                         when.tv_usec -= USEC;
371                                         when.tv_sec++;
372                                 }
373                         } else { /* we've passed the event time */
374                                 when.tv_sec = 0;
375                                 when.tv_usec = 0;
376                         }
377
378                         wake = &when;
379                 } else {
380                         wake = NULL;
381                 }
382
383                 /*
384                  *      Tell someone what the status is.
385                  */
386                 if (el->status) el->status(wake);
387
388                 read_fds = master_fds;
389                 rcode = select(maxfd + 1, &read_fds, NULL, NULL, wake);
390                 if ((rcode < 0) && (errno != EINTR)) {
391                         el->dispatch = 0;
392                         return 0;
393                 }
394
395                 if (fr_heap_num_elements(el->times) > 0) {
396                         do {
397                                 gettimeofday(&el->now, NULL);
398                                 when = el->now;
399                         } while (fr_event_run(el, &when) == 1);
400                 }
401                 
402                 if (rcode <= 0) continue;
403
404                 for (i = 0; i < el->max_readers; i++) {
405                         fr_event_fd_t *ef = &el->readers[i];
406
407                         if (ef->fd < 0) continue;
408
409                         if (!FD_ISSET(ef->fd, &read_fds)) continue;
410                         
411                         ef->handler(el, ef->fd, ef->ctx);
412
413                         if (el->changed) break;
414                 }
415         }
416
417         el->dispatch = 0;
418         return el->exit;
419 }
420
421
422 #ifdef TESTING
423
424 /*
425  *  cc -g -I .. -c rbtree.c -o rbtree.o && cc -g -I .. -c isaac.c -o isaac.o && cc -DTESTING -I .. -c event.c  -o event_mine.o && cc event_mine.o rbtree.o isaac.o -o event
426  *
427  *  ./event
428  *
429  *  And hit CTRL-S to stop the output, CTRL-Q to continue.
430  *  It normally alternates printing the time and sleeping,
431  *  but when you hit CTRL-S/CTRL-Q, you should see a number
432  *  of events run right after each other.
433  *
434  *  OR
435  *
436  *   valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./event
437  */
438
439 static void print_time(void *ctx)
440 {
441         struct timeval *when = ctx;
442
443         printf("%d.%06d\n", when->tv_sec, when->tv_usec);
444         fflush(stdout);
445 }
446
447 static fr_randctx rand_pool;
448
449 static uint32_t event_rand(void)
450 {
451         uint32_t num;
452
453         num = rand_pool.randrsl[rand_pool.randcnt++];
454         if (rand_pool.randcnt == 256) {
455                 fr_isaac(&rand_pool);
456                 rand_pool.randcnt = 0;
457         }
458
459         return num;
460 }
461
462
463 #define MAX 100
464 int main(int argc, char **argv)
465 {
466         int i, rcode;
467         struct timeval array[MAX];
468         struct timeval now, when;
469         fr_event_list_t *el;
470
471         el = fr_event_list_create();
472         if (!el) exit(1);
473
474         memset(&rand_pool, 0, sizeof(rand_pool));
475         rand_pool.randrsl[1] = time(NULL);
476
477         fr_randinit(&rand_pool, 1);
478         rand_pool.randcnt = 0;
479
480         gettimeofday(&array[0], NULL);
481         for (i = 1; i < MAX; i++) {
482                 array[i] = array[i - 1];
483
484                 array[i].tv_usec += event_rand() & 0xffff;
485                 if (array[i].tv_usec > 1000000) {
486                         array[i].tv_usec -= 1000000;
487                         array[i].tv_sec++;
488                 }
489                 fr_event_insert(el, print_time, &array[i], &array[i]);
490         }
491
492         while (fr_event_list_num_elements(el)) {
493                 gettimeofday(&now, NULL);
494                 when = now;
495                 if (!fr_event_run(el, &when)) {
496                         int delay = (when.tv_sec - now.tv_sec) * 1000000;
497                         delay += when.tv_usec;
498                         delay -= now.tv_usec;
499
500                         printf("\tsleep %d\n", delay);
501                         fflush(stdout);
502                         usleep(delay);
503                 }
504         }
505
506         fr_event_list_free(el);
507
508         return 0;
509 }
510 #endif