All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
event.c
Go to the documentation of this file.
1 /*
2  * event.c Non-thread-safe event handling, specific to a RADIUS
3  * server.
4  *
5  * Version: $Id: c1cc9a02193f7c8d69ce4114fdf5c92f899bde41 $
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 RCSID("$Id: c1cc9a02193f7c8d69ce4114fdf5c92f899bde41 $")
26 
27 #include <freeradius-devel/libradius.h>
28 #include <freeradius-devel/heap.h>
29 #include <freeradius-devel/event.h>
30 
31 #ifdef HAVE_KQUEUE
32 #ifndef HAVE_SYS_EVENT_H
33 #error kqueue requires <sys/event.h>
34 
35 #else
36 #include <sys/event.h>
37 #endif
38 #endif /* HAVE_KQUEUE */
39 
40 typedef struct fr_event_fd_t {
41  int fd;
43  void *ctx;
45 
46 #define FR_EV_MAX_FDS (256)
47 
48 #undef USEC
49 #define USEC (1000000)
50 
53 
54  int exit;
55 
57 
58  struct timeval now;
59  bool dispatch;
60 
62 #ifndef HAVE_KQUEUE
64 
65  bool changed;
66 
67 #else
68  int kq;
69  struct kevent events[FR_EV_MAX_FDS]; /* so it doesn't go on the stack every time */
70 #endif
72 };
73 
74 /*
75  * Internal structure for managing events.
76  */
77 struct fr_event_t {
79  void *ctx;
80  struct timeval when;
82  int heap;
83 };
84 
85 
86 static int fr_event_list_time_cmp(void const *one, void const *two)
87 {
88  fr_event_t const *a = one;
89  fr_event_t const *b = two;
90 
91  if (a->when.tv_sec < b->when.tv_sec) return -1;
92  if (a->when.tv_sec > b->when.tv_sec) return +1;
93 
94  if (a->when.tv_usec < b->when.tv_usec) return -1;
95  if (a->when.tv_usec > b->when.tv_usec) return +1;
96 
97  return 0;
98 }
99 
100 
102 {
103  fr_event_list_t *el = list;
104  fr_event_t *ev;
105 
106  while ((ev = fr_heap_peek(el->times)) != NULL) {
107  fr_event_delete(el, &ev);
108  }
109 
110  fr_heap_delete(el->times);
111 
112 #ifdef HAVE_KQUEUE
113  close(el->kq);
114 #endif
115 
116  return 0;
117 }
118 
119 
121 {
122  int i;
124 
125  el = talloc_zero(ctx, fr_event_list_t);
126  if (!fr_assert(el)) {
127  return NULL;
128  }
129  talloc_set_destructor(el, _event_list_free);
130 
131  el->times = fr_heap_create(fr_event_list_time_cmp, offsetof(fr_event_t, heap));
132  if (!el->times) {
133  talloc_free(el);
134  return NULL;
135  }
136 
137  for (i = 0; i < FR_EV_MAX_FDS; i++) {
138  el->readers[i].fd = -1;
139  }
140 
141 #ifndef HAVE_KQUEUE
142  el->changed = true; /* force re-set of fds's */
143 
144 #else
145  el->kq = kqueue();
146  if (el->kq < 0) {
147  talloc_free(el);
148  return NULL;
149  }
150 #endif
151 
152  el->status = status;
153 
154  return el;
155 }
156 
158 {
159  if (!el) return 0;
160 
161  return el->num_readers;
162 }
163 
165 {
166  if (!el) return 0;
167 
168  return fr_heap_num_elements(el->times);
169 }
170 
171 
173 {
174  int ret;
175 
176  fr_event_t *ev;
177 
178  if (!el || !parent || !*parent) return 0;
179 
180 #ifndef NDEBUG
181  /*
182  * Validate the event_t struct to detect memory issues early.
183  */
184  ev = talloc_get_type_abort(*parent, fr_event_t);
185 
186 #else
187  ev = *parent;
188 #endif
189 
190  if (ev->parent) {
191  fr_assert(*(ev->parent) == ev);
192  *ev->parent = NULL;
193  }
194  *parent = NULL;
195 
196  ret = fr_heap_extract(el->times, ev);
197  fr_assert(ret == 1); /* events MUST be in the heap */
198  talloc_free(ev);
199 
200  return ret;
201 }
202 
203 
204 int fr_event_insert(fr_event_list_t *el, fr_event_callback_t callback, void *ctx, struct timeval *when,
205  fr_event_t **parent)
206 {
207  fr_event_t *ev;
208 
209  if (!el) {
210  fr_strerror_printf("Invalid arguments (NULL event list)");
211  return 0;
212  }
213 
214  if (!callback) {
215  fr_strerror_printf("Invalid arguments (NULL callback)");
216  return 0;
217  }
218 
219  if (!when || (when->tv_usec >= USEC)) {
220  fr_strerror_printf("Invalid arguments (time)");
221  return 0;
222  }
223 
224  if (!parent) {
225  fr_strerror_printf("Invalid arguments (NULL parent)");
226  return 0;
227  }
228 
229  /*
230  * If there is an event, re-use it instead of freeing it
231  * and allocating a new one.
232  */
233  if (*parent) {
234  int ret;
235 
236 #ifndef NDEBUG
237  ev = talloc_get_type_abort(*parent, fr_event_t);
238 #else
239  ev = *parent;
240 #endif
241 
242  ret = fr_heap_extract(el->times, ev);
243  fr_assert(ret == 1); /* events MUST be in the heap */
244 
245  memset(ev, 0, sizeof(*ev));
246  } else {
247  ev = talloc_zero(el, fr_event_t);
248  if (!ev) return 0;
249  }
250 
251  ev->callback = callback;
252  ev->ctx = ctx;
253  ev->when = *when;
254  ev->parent = parent;
255 
256  if (!fr_heap_insert(el->times, ev)) {
257  talloc_free(ev);
258  return 0;
259  }
260 
261  *parent = ev;
262  return 1;
263 }
264 
265 
266 int fr_event_run(fr_event_list_t *el, struct timeval *when)
267 {
268  fr_event_callback_t callback;
269  void *ctx;
270  fr_event_t *ev;
271 
272  if (!el) return 0;
273 
274  if (fr_heap_num_elements(el->times) == 0) {
275  when->tv_sec = 0;
276  when->tv_usec = 0;
277  return 0;
278  }
279 
280  ev = fr_heap_peek(el->times);
281  if (!ev) {
282  when->tv_sec = 0;
283  when->tv_usec = 0;
284  return 0;
285  }
286 
287  /*
288  * See if it's time to do this one.
289  */
290  if ((ev->when.tv_sec > when->tv_sec) ||
291  ((ev->when.tv_sec == when->tv_sec) &&
292  (ev->when.tv_usec > when->tv_usec))) {
293  *when = ev->when;
294  return 0;
295  }
296 
297  callback = ev->callback;
298  ctx = ev->ctx;
299 
300  /*
301  * Delete the event before calling it.
302  */
303  fr_event_delete(el, ev->parent);
304 
305  callback(ctx, when);
306  return 1;
307 }
308 
309 
310 int fr_event_now(fr_event_list_t *el, struct timeval *when)
311 {
312  if (!when) return 0;
313 
314  if (el && el->dispatch) {
315  *when = el->now;
316  } else {
317  gettimeofday(when, NULL);
318  }
319 
320  return 1;
321 }
322 
323 
324 int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
325  fr_event_fd_handler_t handler, void *ctx)
326 {
327  int i;
328  fr_event_fd_t *ef;
329 
330  if (!el) {
331  fr_strerror_printf("Invalid arguments (NULL event list)");
332  return 0;
333  }
334 
335  if (!handler) {
336  fr_strerror_printf("Invalid arguments (NULL handler)");
337  return 0;
338  }
339 
340  if (!ctx) {
341  fr_strerror_printf("Invalid arguments (NULL ctx)");
342  return 0;
343  }
344 
345  if (fd < 0) {
346  fr_strerror_printf("Invalid arguments (bad FD %i)", fd);
347  return 0;
348  }
349 
350  if (type != 0) {
351  fr_strerror_printf("Invalid type %i", type);
352  return 0;
353  }
354 
355  if (el->num_readers >= FR_EV_MAX_FDS) {
356  fr_strerror_printf("Too many readers");
357  return 0;
358  }
359  ef = NULL;
360 
361 #ifdef HAVE_KQUEUE
362  /*
363  * We need to store TWO fields with the event. kqueue
364  * only lets us store one. If we put the two fields into
365  * a malloc'd structure, that would help. Except that
366  * kqueue can silently delete the event when the socket
367  * is closed, and not give us the opportunity to free it.
368  * <sigh>
369  *
370  * The solution is to put the fields into an array, and
371  * do a linear search on addition/deletion of the FDs.
372  * However, to avoid MOST linear issues, we start off the
373  * search at "FD" offset. Since FDs are unique, AND
374  * usually less than 256, we do "FD & 0xff", which is a
375  * good guess, and makes the lookups mostly O(1).
376  */
377  for (i = 0; i < FR_EV_MAX_FDS; i++) {
378  int j;
379  struct kevent evset;
380 
381  j = (i + fd) & (FR_EV_MAX_FDS - 1);
382 
383  if (el->readers[j].fd >= 0) continue;
384 
385  /*
386  * We want to read from the FD.
387  */
388  EV_SET(&evset, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, &el->readers[j]);
389  if (kevent(el->kq, &evset, 1, NULL, 0, NULL) < 0) {
390  fr_strerror_printf("Failed inserting event for FD %i: %s", fd, fr_syserror(errno));
391  return 0;
392  }
393 
394  ef = &el->readers[j];
395  el->num_readers++;
396  break;
397  }
398 
399 #else /* HAVE_KQUEUE */
400 
401  for (i = 0; i <= el->max_readers; i++) {
402  /*
403  * Be fail-safe on multiple inserts.
404  */
405  if (el->readers[i].fd == fd) {
406  if ((el->readers[i].handler != handler) ||
407  (el->readers[i].ctx != ctx)) {
408  fr_strerror_printf("Multiple handlers for same FD");
409  return 0;
410  }
411 
412  /*
413  * No change.
414  */
415  return 1;
416  }
417 
418  if (el->readers[i].fd < 0) {
419  ef = &el->readers[i];
420  el->num_readers++;
421 
422  if (i == el->max_readers) el->max_readers = i + 1;
423  break;
424  }
425  }
426 #endif
427 
428  if (!ef) {
429  fr_strerror_printf("Failed assigning FD");
430  return 0;
431  }
432 
433  ef->fd = fd;
434  ef->handler = handler;
435  ef->ctx = ctx;
436 
437 #ifndef HAVE_KQUEUE
438  el->changed = true;
439 #endif
440 
441  return 1;
442 }
443 
444 int fr_event_fd_delete(fr_event_list_t *el, int type, int fd)
445 {
446  int i;
447 
448  if (!el || (fd < 0)) return 0;
449 
450  if (type != 0) return 0;
451 
452 #ifdef HAVE_KQUEUE
453  for (i = 0; i < FR_EV_MAX_FDS; i++) {
454  int j;
455  struct kevent evset;
456 
457  j = (i + fd) & (FR_EV_MAX_FDS - 1);
458 
459  if (el->readers[j].fd != fd) continue;
460 
461  /*
462  * Tell the kernel to delete it from the list.
463  *
464  * The caller MAY have closed it, in which case
465  * the kernel has removed it from the list. So
466  * we ignore the return code from kevent().
467  */
468  EV_SET(&evset, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
469  (void) kevent(el->kq, &evset, 1, NULL, 0, NULL);
470 
471  el->readers[j].fd = -1;
472  el->num_readers--;
473 
474  return 1;
475  }
476 
477 #else
478 
479  for (i = 0; i < el->max_readers; i++) {
480  if (el->readers[i].fd == fd) {
481  el->readers[i].fd = -1;
482  el->num_readers--;
483 
484  if ((i + 1) == el->max_readers) el->max_readers = i;
485  el->changed = true;
486  return 1;
487  }
488  }
489 #endif /* HAVE_KQUEUE */
490 
491  return 0;
492 }
493 
494 
496 {
497  if (!el) return;
498 
499  el->exit = code;
500 }
501 
503 {
504  return (el->exit != 0);
505 }
506 
508 {
509  int i, rcode;
510  struct timeval when, *wake;
511 #ifdef HAVE_KQUEUE
512  struct timespec ts_when, *ts_wake;
513 #else
514  int maxfd = 0;
515  fd_set read_fds, master_fds;
516 
517  el->changed = true;
518 #endif
519 
520  el->exit = 0;
521  el->dispatch = true;
522 
523  while (!el->exit) {
524 #ifndef HAVE_KQUEUE
525  /*
526  * Cache the list of FD's to watch.
527  */
528  if (el->changed) {
529 #ifdef __clang_analyzer__
530  memset(&master_fds, 0, sizeof(master_fds));
531 #else
532  FD_ZERO(&master_fds);
533 #endif
534  for (i = 0; i < el->max_readers; i++) {
535  if (el->readers[i].fd < 0) continue;
536 
537  if (el->readers[i].fd > maxfd) {
538  maxfd = el->readers[i].fd;
539  }
540  FD_SET(el->readers[i].fd, &master_fds);
541  }
542 
543  el->changed = false;
544  }
545 #endif /* HAVE_KQUEUE */
546 
547  /*
548  * Find the first event. If there's none, we wait
549  * on the socket forever.
550  */
551  when.tv_sec = 0;
552  when.tv_usec = 0;
553 
554  if (fr_heap_num_elements(el->times) > 0) {
555  fr_event_t *ev;
556 
557  ev = fr_heap_peek(el->times);
558  if (!ev) {
559  fr_exit_now(42);
560  }
561 
562  gettimeofday(&el->now, NULL);
563 
564  if (timercmp(&el->now, &ev->when, <)) {
565  when = ev->when;
566  when.tv_sec -= el->now.tv_sec;
567 
568  if (when.tv_sec > 0) {
569  when.tv_sec--;
570  when.tv_usec += USEC;
571  } else {
572  when.tv_sec = 0;
573  }
574  when.tv_usec -= el->now.tv_usec;
575  if (when.tv_usec >= USEC) {
576  when.tv_usec -= USEC;
577  when.tv_sec++;
578  }
579  } else { /* we've passed the event time */
580  when.tv_sec = 0;
581  when.tv_usec = 0;
582  }
583 
584  wake = &when;
585  } else {
586  wake = NULL;
587  }
588 
589  /*
590  * Tell someone what the status is.
591  */
592  if (el->status) el->status(wake);
593 
594 #ifndef HAVE_KQUEUE
595  read_fds = master_fds;
596  rcode = select(maxfd + 1, &read_fds, NULL, NULL, wake);
597  if ((rcode < 0) && (errno != EINTR)) {
598  fr_strerror_printf("Failed in select: %s", fr_syserror(errno));
599  el->dispatch = false;
600  return -1;
601  }
602 
603 #else /* HAVE_KQUEUE */
604 
605  if (wake) {
606  ts_wake = &ts_when;
607  ts_when.tv_sec = when.tv_sec;
608  ts_when.tv_nsec = when.tv_usec * 1000;
609  } else {
610  ts_wake = NULL;
611  }
612 
613  rcode = kevent(el->kq, NULL, 0, el->events, FR_EV_MAX_FDS, ts_wake);
614 #endif /* HAVE_KQUEUE */
615 
616  if (fr_heap_num_elements(el->times) > 0) {
617  do {
618  gettimeofday(&el->now, NULL);
619  when = el->now;
620  } while (fr_event_run(el, &when) == 1);
621  }
622 
623  if (rcode <= 0) continue;
624 
625 #ifndef HAVE_KQUEUE
626  /*
627  * Loop over all of the sockets to see if there's
628  * an event for that socket.
629  */
630  for (i = 0; i < el->max_readers; i++) {
631  fr_event_fd_t *ef = &el->readers[i];
632 
633  if (ef->fd < 0) continue;
634 
635  if (!FD_ISSET(ef->fd, &read_fds)) continue;
636 
637  ef->handler(el, ef->fd, ef->ctx);
638 
639  if (el->changed) break;
640  }
641 
642 #else /* HAVE_KQUEUE */
643 
644  /*
645  * Loop over all of the events, servicing them.
646  */
647  for (i = 0; i < rcode; i++) {
648  fr_event_fd_t *ef = el->events[i].udata;
649 
650  if (el->events[i].flags & EV_EOF) {
651  /*
652  * FIXME: delete the handler
653  * here, and fix process.c to not
654  * call fr_event_fd_delete().
655  * It's cleaner.
656  *
657  * Call the handler, which SHOULD
658  * delete the connection.
659  */
660  ef->handler(el, ef->fd, ef->ctx);
661  continue;
662  }
663 
664  /*
665  * Else it's our event. We only set
666  * EVFILT_READ, so it must be a read
667  * event.
668  */
669  ef->handler(el, ef->fd, ef->ctx);
670  }
671 #endif /* HAVE_KQUEUE */
672  }
673 
674  el->dispatch = false;
675  return el->exit;
676 }
677 
678 
679 #ifdef TESTING
680 
681 /*
682  * 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
683  *
684  * ./event
685  *
686  * And hit CTRL-S to stop the output, CTRL-Q to continue.
687  * It normally alternates printing the time and sleeping,
688  * but when you hit CTRL-S/CTRL-Q, you should see a number
689  * of events run right after each other.
690  *
691  * OR
692  *
693  * valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./event
694  */
695 
696 static void print_time(void *ctx)
697 {
698  struct timeval *when = ctx;
699 
700  printf("%d.%06d\n", when->tv_sec, when->tv_usec);
701  fflush(stdout);
702 }
703 
704 static fr_randctx rand_pool;
705 
706 static uint32_t event_rand(void)
707 {
708  uint32_t num;
709 
710  num = rand_pool.randrsl[rand_pool.randcnt++];
711  if (rand_pool.randcnt == 256) {
712  fr_isaac(&rand_pool);
713  rand_pool.randcnt = 0;
714  }
715 
716  return num;
717 }
718 
719 
720 #define MAX 100
721 int main(int argc, char **argv)
722 {
723  int i, rcode;
724  struct timeval array[MAX];
725  struct timeval now, when;
727 
728  el = fr_event_list_create(NULL, NULL);
729  if (!el) exit(1);
730 
731  memset(&rand_pool, 0, sizeof(rand_pool));
732  rand_pool.randrsl[1] = time(NULL);
733 
734  fr_randinit(&rand_pool, 1);
735  rand_pool.randcnt = 0;
736 
737  gettimeofday(&array[0], NULL);
738  for (i = 1; i < MAX; i++) {
739  array[i] = array[i - 1];
740 
741  array[i].tv_usec += event_rand() & 0xffff;
742  if (array[i].tv_usec > 1000000) {
743  array[i].tv_usec -= 1000000;
744  array[i].tv_sec++;
745  }
746  fr_event_insert(el, print_time, &array[i], &array[i]);
747  }
748 
749  while (fr_event_list_num_elements(el)) {
750  gettimeofday(&now, NULL);
751  when = now;
752  if (!fr_event_run(el, &when)) {
753  int delay = (when.tv_sec - now.tv_sec) * 1000000;
754  delay += when.tv_usec;
755  delay -= now.tv_usec;
756 
757  printf("\tsleep %d\n", delay);
758  fflush(stdout);
759  usleep(delay);
760  }
761  }
762 
763  talloc_free(el);
764 
765  return 0;
766 }
767 #endif
bool fr_event_loop_exiting(fr_event_list_t *el)
Definition: event.c:502
fr_event_list_t * fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status)
Definition: event.c:120
static fr_event_list_t * el
Definition: process.c:54
void(* fr_event_callback_t)(void *, struct timeval *now)
Definition: event.h:36
struct fr_event_fd_t fr_event_fd_t
int fr_event_now(fr_event_list_t *el, struct timeval *when)
Definition: event.c:310
#define FR_EV_MAX_FDS
Definition: event.c:46
int num_readers
Definition: event.c:61
void * ctx
Definition: event.c:43
void fr_randinit(fr_randctx *ctx, int flag)
Definition: isaac.c:65
int fr_event_delete(fr_event_list_t *el, fr_event_t **parent)
Definition: event.c:172
fr_event_callback_t callback
Definition: event.c:78
int fr_heap_extract(fr_heap_t *hp, void *data)
Definition: heap.c:147
void fr_event_loop_exit(fr_event_list_t *el, int code)
Definition: event.c:495
int fr_event_loop(fr_event_list_t *el)
Definition: event.c:507
static fr_event_list_t * events
Definition: radsniff.c:52
void * fr_heap_peek(fr_heap_t *hp)
Definition: heap.c:207
void(* fr_event_status_t)(struct timeval *)
Definition: event.h:37
struct timeval when
Definition: event.c:80
int fr_event_list_num_elements(fr_event_list_t *el)
Definition: event.c:164
int heap
Definition: event.c:82
fr_event_fd_handler_t handler
Definition: event.c:42
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
int fr_heap_insert(fr_heap_t *hp, void *data)
Definition: heap.c:92
int fd
Definition: event.c:41
Definition: heap.c:16
#define fr_assert(_x)
Definition: libradius.h:505
static int fr_event_list_time_cmp(void const *one, void const *two)
Definition: event.c:86
void fr_heap_delete(fr_heap_t *hp)
Definition: heap.c:36
fr_event_t ** parent
Definition: event.c:81
void fr_isaac(fr_randctx *ctx)
Definition: isaac.c:29
#define USEC
Definition: event.c:49
bool changed
Definition: event.c:65
void * ctx
Definition: event.c:79
struct timeval now
Definition: event.c:58
uint32_t randrsl[256]
Definition: libradius.h:429
int fr_event_insert(fr_event_list_t *el, fr_event_callback_t callback, void *ctx, struct timeval *when, fr_event_t **parent)
Definition: event.c:204
int fr_event_fd_delete(fr_event_list_t *el, int type, int fd)
Definition: event.c:444
fr_heap_t * times
Definition: event.c:52
int fr_event_fd_insert(fr_event_list_t *el, int type, int fd, fr_event_fd_handler_t handler, void *ctx)
Definition: event.c:324
fr_event_status_t status
Definition: event.c:56
int fr_event_run(fr_event_list_t *el, struct timeval *when)
Definition: event.c:266
#define fr_exit_now(_x)
Definition: libradius.h:511
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
fr_heap_t * fr_heap_create(fr_heap_cmp_t cmp, size_t offset)
Definition: heap.c:44
int main(int argc, char *argv[])
Definition: radattr.c:959
fr_event_fd_t readers[FR_EV_MAX_FDS]
Definition: event.c:71
int fr_event_list_num_fds(fr_event_list_t *el)
Definition: event.c:157
bool dispatch
Definition: event.c:59
#define RCSID(id)
Definition: build.h:135
uint32_t randcnt
Definition: libradius.h:428
int max_readers
Definition: event.c:63
void(* fr_event_fd_handler_t)(fr_event_list_t *el, int sock, void *ctx)
Definition: event.h:38
static int _event_list_free(fr_event_list_t *list)
Definition: event.c:101
size_t fr_heap_num_elements(fr_heap_t *hp)
Definition: heap.c:217