The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
event.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Wrapper around libkqueue to make managing events easier
18 *
19 * Non-thread-safe event handling specific to FreeRADIUS.
20 *
21 * By non-thread-safe we mean multiple threads can't insert/delete
22 * events concurrently into the same event list without synchronization.
23 *
24 * @file src/lib/util/event.c
25 *
26 * @copyright 2007-2016 The FreeRADIUS server project
27 * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
28 * @copyright 2007 Alan DeKok (aland@freeradius.org)
29 */
30RCSID("$Id: 68b9755d01506bb001d3ff24356db4e6b871c303 $")
31
32#define _EVENT_LIST_PRIVATE 1
34
35#include <freeradius-devel/util/dlist.h>
36#include <freeradius-devel/util/event.h>
37#include <freeradius-devel/util/log.h>
38#include <freeradius-devel/util/rb.h>
39#include <freeradius-devel/util/strerror.h>
40#include <freeradius-devel/util/syserror.h>
41#include <freeradius-devel/util/token.h>
42#include <freeradius-devel/util/atexit.h>
43
44#include <sys/stat.h>
45#include <sys/wait.h>
46
47#ifdef NDEBUG
48/*
49 * Turn off documentation warnings as file/line
50 * args aren't used for non-debug builds.
51 */
53DIAG_OFF(documentation)
55#endif
56
57#define FR_EV_BATCH_FDS (256)
58
59DIAG_OFF(unused-macros)
60#define fr_time() static_assert(0, "Use el->time for event loop timing")
61DIAG_ON(unused-macros)
62
63#if !defined(SO_GET_FILTER) && defined(SO_ATTACH_FILTER)
64# define SO_GET_FILTER SO_ATTACH_FILTER
65#endif
66
68#ifdef EVFILT_AIO
69 { L("EVFILT_AIO"), EVFILT_AIO },
70#endif
71#ifdef EVFILT_EXCEPT
72 { L("EVFILT_EXCEPT"), EVFILT_EXCEPT },
73#endif
74#ifdef EVFILT_MACHPORT
75 { L("EVFILT_MACHPORT"), EVFILT_MACHPORT },
76#endif
77 { L("EVFILT_PROC"), EVFILT_PROC },
78 { L("EVFILT_READ"), EVFILT_READ },
79 { L("EVFILT_SIGNAL"), EVFILT_SIGNAL },
80 { L("EVFILT_TIMER"), EVFILT_TIMER },
81 { L("EVFILT_VNODE"), EVFILT_VNODE },
82 { L("EVFILT_WRITE"), EVFILT_WRITE }
83};
85
86#ifdef EVFILT_LIBKQUEUE
87static int log_conf_kq;
88#endif
89
90typedef enum {
91 FR_EVENT_FD_SOCKET = 1, //!< is a socket.
92 FR_EVENT_FD_FILE = 2, //!< is a file.
93 FR_EVENT_FD_DIRECTORY = 4, //!< is a directory.
94
95#ifdef SO_GET_FILTER
97#endif
99
100typedef enum {
102
103 FR_EVENT_FUNC_IDX_FILTER, //!< Sign flip is performed i.e. -1 = 0The filter is used
104 //// as the index in the ev to func index.
105 FR_EVENT_FUNC_IDX_FFLAGS //!< The bit position of the flags in FFLAGS
106 ///< is used to provide the index.
107 ///< i.e. 0x01 -> 0, 0x02 -> 1, 0x08 -> 3 etc..
109
110#ifndef SO_GET_FILTER
111# define FR_EVENT_FD_PCAP 0
112#endif
113
114/** Specifies a mapping between a function pointer in a structure and its respective event
115 *
116 * If the function pointer at the specified offset is set, then a matching event
117 * will be added.
118 *
119 * If the function pointer is NULL, then any existing events will be removed.
120 */
121typedef struct {
122 size_t offset; //!< Offset of function pointer in structure.
123 char const *name; //!< Name of the event.
124 int16_t filter; //!< Filter to apply.
125 uint16_t flags; //!< Flags to use for inserting event.
126 uint32_t fflags; //!< fflags to pass to filter.
127 int type; //!< Type this filter applies to.
128 bool coalesce; //!< Coalesce this map with the next.
130
131typedef struct {
132 fr_event_func_idx_type_t idx_type; //!< What type of index we use for
133 ///< event to function mapping.
134 fr_event_func_map_entry_t *func_to_ev; //!< Function -> Event maps coalesced, out of order.
135 fr_event_func_map_entry_t **ev_to_func; //!< Function -> Event maps in index order.
137
141 .func_to_ev = (fr_event_func_map_entry_t[]){
142 {
143 .offset = offsetof(fr_event_io_func_t, read),
144 .name = "read",
145 .filter = EVFILT_READ,
146 .flags = EV_ADD | EV_ENABLE,
147#ifdef NOTE_NONE
148 .fflags = NOTE_NONE,
149#else
150 .fflags = 0,
151#endif
153 },
154 {
155 .offset = offsetof(fr_event_io_func_t, write),
156 .name = "write",
157 .filter = EVFILT_WRITE,
158 .flags = EV_ADD | EV_ENABLE,
159 .fflags = 0,
161 },
162 { 0 }
163 }
164 },
166 .idx_type = FR_EVENT_FUNC_IDX_FFLAGS,
167 .func_to_ev = (fr_event_func_map_entry_t[]){
168 {
169 .offset = offsetof(fr_event_vnode_func_t, delete),
170 .name = "delete",
171 .filter = EVFILT_VNODE,
172 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
173 .fflags = NOTE_DELETE,
175 .coalesce = true
176 },
177 {
178 .offset = offsetof(fr_event_vnode_func_t, write),
179 .name = "write",
180 .filter = EVFILT_VNODE,
181 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
182 .fflags = NOTE_WRITE,
183 .type = FR_EVENT_FD_FILE,
184 .coalesce = true
185 },
186 {
187 .offset = offsetof(fr_event_vnode_func_t, extend),
188 .name = "extend",
189 .filter = EVFILT_VNODE,
190 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
191 .fflags = NOTE_EXTEND,
193 .coalesce = true
194 },
195 {
196 .offset = offsetof(fr_event_vnode_func_t, attrib),
197 .name = "attrib",
198 .filter = EVFILT_VNODE,
199 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
200 .fflags = NOTE_ATTRIB,
201 .type = FR_EVENT_FD_FILE,
202 .coalesce = true
203 },
204 {
205 .offset = offsetof(fr_event_vnode_func_t, link),
206 .name = "link",
207 .filter = EVFILT_VNODE,
208 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
209 .fflags = NOTE_LINK,
210 .type = FR_EVENT_FD_FILE,
211 .coalesce = true
212 },
213 {
214 .offset = offsetof(fr_event_vnode_func_t, rename),
215 .name = "rename",
216 .filter = EVFILT_VNODE,
217 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
218 .fflags = NOTE_RENAME,
219 .type = FR_EVENT_FD_FILE,
220 .coalesce = true
221 },
222#ifdef NOTE_REVOKE
223 {
224 .offset = offsetof(fr_event_vnode_func_t, revoke),
225 .name = "revoke",
226 .filter = EVFILT_VNODE,
227 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
228 .fflags = NOTE_REVOKE,
229 .type = FR_EVENT_FD_FILE,
230 .coalesce = true
231 },
232#endif
233#ifdef NOTE_FUNLOCK
234 {
235 .offset = offsetof(fr_event_vnode_func_t, funlock),
236 .name = "funlock",
237 .filter = EVFILT_VNODE,
238 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
239 .fflags = NOTE_FUNLOCK,
240 .type = FR_EVENT_FD_FILE,
241 .coalesce = true
242 },
243#endif
244 { 0 }
245 }
246 }
247};
248
250 { L("directory"), FR_EVENT_FD_DIRECTORY },
251 { L("file"), FR_EVENT_FD_FILE },
252 { L("pcap"), FR_EVENT_FD_PCAP },
253 { L("socket"), FR_EVENT_FD_SOCKET }
254};
256
257/** A file descriptor/filter event
258 *
259 */
261 fr_rb_node_t node; //!< Entry in the tree of file descriptor handles.
262 ///< this should really go away and we should pass around
263 ///< handles directly.
264
265 fr_event_list_t *el; //!< Event list this event belongs to.
267 int fd; //!< File descriptor we're listening for events on.
268
269 fr_event_fd_type_t type; //!< Type of events we're interested in.
270
271 int sock_type; //!< The type of socket SOCK_STREAM, SOCK_RAW etc...
272
273 fr_event_funcs_t active; //!< Active filter functions.
274 fr_event_funcs_t stored; //!< Stored (set, but inactive) filter functions.
275
276 fr_event_error_cb_t error; //!< Callback for when an error occurs on the FD.
277
278 fr_event_func_map_t const *map; //!< Function map between #fr_event_funcs_t and kevent filters.
279
280 bool is_registered; //!< Whether this fr_event_fd_t's FD has been registered with
281 ///< kevent. Mostly for debugging.
282
283 void *uctx; //!< Context pointer to pass to each file descriptor callback.
284 TALLOC_CTX *linked_ctx; //!< talloc ctx this event was bound to.
285
286 fr_dlist_t entry; //!< Entry in free list.
287
288#ifndef NDEBUG
289 uintptr_t armour; //!< protection flag from being deleted.
290#endif
291
292#ifndef NDEBUG
293 char const *file; //!< Source file this event was last updated in.
294 int line; //!< Line this event was last updated on.
295#endif
296};
297
299 fr_event_list_t *el; //!< Event list this event belongs to.
300
301 bool is_registered; //!< Whether this user event has been registered
302 ///< with the event loop.
303
304 pid_t pid; //!< child to wait for
306
307 fr_event_pid_cb_t callback; //!< callback to run when the child exits
308 void *uctx; //!< Context pointer to pass to each file descriptor callback.
309
310 /** Fields that are only used if we're being triggered by a user event
311 */
312 struct {
313 fr_event_user_t *ev; //!< Fallback user event we use to raise a PID event when
314 ///< a race occurs with kevent.
315 int status; //!< Status we got from waitid.
317#ifndef NDEBUG
318 char const *file; //!< Source file this event was last updated in.
319 int line; //!< Line this event was last updated on.
320#endif
321};
322
323/** Hold additional information for automatically reaped PIDs
324 */
325typedef struct {
326 fr_event_list_t *el; //!< Event list this event belongs to.
327 fr_event_pid_t const *pid_ev; //!< pid_ev this reaper is bound to.
328
329 fr_dlist_t entry; //!< If the fr_event_pid is in the detached, reap state,
330 ///< it's inserted into a list associated with the event.
331 //!< We then send SIGKILL, and forcefully reap the process
332 ///< on exit.
333
334 fr_event_pid_cb_t callback; //!< callback to run when the child exits
335 void *uctx; //!< Context pointer to pass to each file descriptor callback.
337
338/** Callbacks for kevent() user events
339 *
340 */
342 fr_event_list_t *el; //!< Event list this event belongs to.
343
344 bool is_registered; //!< Whether this user event has been registered
345 ///< with the event loop.
346
347 fr_event_user_cb_t callback; //!< The callback to call.
348 void *uctx; //!< Context for the callback.
349
350#ifndef NDEBUG
351 char const *file; //!< Source file this event was last updated in.
352 int line; //!< Line this event was last updated on.
353#endif
354};
355
356/** Callbacks to perform when the event handler is about to check the events
357 *
358 */
359typedef struct {
360 fr_dlist_t entry; //!< Linked list of callback.
361 fr_event_status_cb_t callback; //!< The callback to call.
362 void *uctx; //!< Context for the callback.
364
365/** Callbacks to perform after all timers and FDs have been checked
366 *
367 */
368typedef struct {
369 fr_dlist_t entry; //!< Linked list of callback.
370 fr_event_post_cb_t callback; //!< The callback to call.
371 void *uctx; //!< Context for the callback.
373
374/** Stores all information relating to an event list
375 *
376 */
378 struct fr_event_list_pub_s pub; //!< Next event list in the chain.
379 fr_rb_tree_t *fds; //!< Tree used to track FDs with filters in kqueue.
380
381 int will_exit; //!< Will exit on next call to fr_event_corral.
382 int exit; //!< If non-zero event loop will prevent the addition
383 ///< of new events, and will return immediately
384 ///< from the corral/service function.
385
386 bool dispatch; //!< Whether the event list is currently dispatching events.
387
388 int num_fd_events; //!< Number of events in this event list.
389
390 int kq; //!< instance associated with this event list.
391
392 fr_dlist_head_t pre_callbacks; //!< callbacks when we may be idle...
393 fr_dlist_head_t post_callbacks; //!< post-processing callbacks
394
395 fr_dlist_head_t pid_to_reap; //!< A list of all orphaned child processes we're
396 ///< waiting to reap.
397
398 struct kevent events[FR_EV_BATCH_FDS]; /* so it doesn't go on the stack every time */
399
400 bool in_handler; //!< Deletes should be deferred until after the
401 ///< handlers complete.
402
403 fr_dlist_head_t fd_to_free; //!< File descriptor events pending deletion.
404
405#ifdef WITH_EVENT_DEBUG
406 fr_timer_t *report; //!< Report event.
407#endif
408};
409
411{
412 switch (map->idx_type) {
413 default:
414 return;
415
416 /*
417 * - Figure out the lowest filter value
418 * - Invert it
419 * - Allocate an array
420 * - Populate the array
421 */
423 {
424 int low = 0;
426
427 for (entry = map->func_to_ev; entry->name; entry++) if (entry->filter < low) low = entry->filter;
428
429 map->ev_to_func = talloc_zero_array(NULL, fr_event_func_map_entry_t *, ~low + 1);
430 if (unlikely(!map->ev_to_func)) abort();
431
432 for (entry = map->func_to_ev; entry->name; entry++) map->ev_to_func[~entry->filter] = entry;
433 }
434 break;
435
436 /*
437 * - Figure out the highest bit position
438 * - Allocate an array
439 * - Populate the array
440 */
442 {
443 uint8_t high = 0, pos;
445
446 for (entry = map->func_to_ev; entry->name; entry++) {
447 pos = fr_high_bit_pos(entry->fflags);
448 if (pos > high) high = pos;
449 }
450
451 map->ev_to_func = talloc_zero_array(NULL, fr_event_func_map_entry_t *, high);
452 if (unlikely(!map->ev_to_func)) abort();
453
454 for (entry = map->func_to_ev; entry->name; entry++) {
455 typeof_field(fr_event_func_map_entry_t, fflags) fflags = entry->fflags;
456
457 /*
458 * Multiple notes can be associated
459 * with the same function.
460 */
461 while ((pos = fr_high_bit_pos(fflags))) {
462 pos -= 1;
463 map->ev_to_func[pos] = entry;
464 /*
465 * Coverity thinks that after this decrement, pos
466 * can be 255 even though the loop condition precludes
467 * it. Adding a Coverity-only check won't change that,
468 * so we're stuck with annotation.
469 */
470 /* coverity[overflow_const] */
471 fflags &= ~(1 << pos);
472 }
473 }
474 }
475 break;
476 }
477}
478
479/** Figure out which function to call given a kevent
480 *
481 * This function should be called in a loop until it returns NULL.
482 *
483 * @param[in] ef File descriptor state handle.
484 * @param[in] filter from the kevent.
485 * @param[in,out] fflags from the kevent. Each call will return the function
486 * from the next most significant NOTE_*, with each
487 * NOTE_* before unset from fflags.
488 * @return
489 * - NULL there are no more callbacks to call.
490 * - The next callback to call.
491 */
492static inline CC_HINT(always_inline) fr_event_fd_cb_t event_fd_func(fr_event_fd_t *ef, int *filter, int *fflags)
493{
494 fr_event_func_map_t const *map = ef->map;
495
496#define GET_FUNC(_ef, _offset) *((fr_event_fd_cb_t const *)((uint8_t const *)&(_ef)->active + _offset))
497
498 switch (map->idx_type) {
499 default:
500 fr_assert_fail("Invalid index type %u", map->idx_type);
501 return NULL;
502
504 {
505 int idx;
506
507 if (!*filter) return NULL;
508
509 idx = ~*filter; /* Consume the filter */
510 *filter = 0;
511
512 return GET_FUNC(ef, map->ev_to_func[idx]->offset);
513 }
514
516 {
517 int our_fflags = *fflags;
518 uint8_t pos = fr_high_bit_pos(our_fflags);
519
520 if (!pos) return NULL; /* No more fflags to consume */
521 pos -= 1; /* Saves an array element */
522
523 *fflags = our_fflags & ~(1 << pos); /* Consume the knote */
524
525 return GET_FUNC(ef, map->ev_to_func[pos]->offset);
526 }
527 }
528}
529
530/** Compare two file descriptor handles
531 *
532 * @param[in] one the first file descriptor handle.
533 * @param[in] two the second file descriptor handle.
534 * @return CMP(one, two)
535 */
536static int8_t fr_event_fd_cmp(void const *one, void const *two)
537{
538 fr_event_fd_t const *a = one, *b = two;
539
540 CMP_RETURN(a, b, fd);
541
542 return CMP(a->filter, b->filter);
543}
544
545/** Return the number of file descriptors is_registered with this event loop
546 *
547 */
549{
550 if (unlikely(!el)) return 0;
551
552 return fr_rb_num_elements(el->fds);
553}
554
555/** Return the number of timer events currently scheduled
556 *
557 * @param[in] el to return timer events for.
558 * @return number of timer events.
559 */
561{
562 if (unlikely(!el)) return 0;
563
565}
566
567/** Return the kq associated with an event list.
568 *
569 * @param[in] el to return timer events for.
570 * @return kq
571 */
573{
574 if (unlikely(!el)) return -1;
575
576 return el->kq;
577}
578
579/** Get the current server time according to the event list
580 *
581 * If the event list is currently dispatching events, we return the time
582 * this iteration of the event list started.
583 *
584 * If the event list is not currently dispatching events, we return the
585 * current system time.
586 *
587 * @param[in] el to get time from.
588 * @return the current time according to the event list.
589 */
591{
592 return el->pub.tl->time();
593}
594
595/** Placeholder callback to avoid branches in service loop
596 *
597 * This is set in place of any NULL function pointers, so that the event loop doesn't
598 * SEGV if a filter callback function is unset between corral and service.
599 */
600static void fr_event_fd_noop(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED void *uctx)
601{
602 return;
603}
604
605/** Build a new evset based on function pointers present
606 *
607 * @note The contents of active functions may be inconsistent if this function errors. But the
608 * only time that will occur is if the caller passed invalid arguments.
609 *
610 * @param[in] el we're building events for.
611 * @param[out] out_kev where to write the evset.
612 * @param[in] outlen length of output buffer.
613 * @param[out] active The set of function pointers with active filters.
614 * @param[in] ef event to insert.
615 * @param[in] new Functions to map to filters.
616 * @param[in] prev Previous set of functions mapped to filters.
617 * @return
618 * - >= 0 the number of changes written to out.
619 * - < 0 an error occurred.
620 */
622#ifndef WITH_EVENT_DEBUG
623 UNUSED
624#endif
626 struct kevent out_kev[], size_t outlen, fr_event_funcs_t *active,
627 fr_event_fd_t *ef,
628 fr_event_funcs_t const *new, fr_event_funcs_t const *prev)
629{
630 struct kevent *out = out_kev, *end = out + outlen;
631 fr_event_func_map_entry_t const *map;
632 struct kevent add[10], *add_p = add;
633 size_t i;
634
635 EVENT_DEBUG("%p - Building new evset for FD %i (new %p, prev %p)", el, ef->fd, new, prev);
636
637 /*
638 * Iterate over the function map, setting/unsetting
639 * filters and filter flags.
640 */
641 for (map = ef->map->func_to_ev; map->name; map++) {
642 bool has_current_func = false;
643 bool has_prev_func = false;
644 uint32_t current_fflags = 0;
645 uint32_t prev_fflags = 0;
646
647 do {
648 fr_event_fd_cb_t prev_func;
649 fr_event_fd_cb_t new_func;
650
651 /*
652 * If the previous value was the 'noop'
653 * callback, it's identical to being unset.
654 */
655 prev_func = *(fr_event_fd_cb_t const *)((uint8_t const *)prev + map->offset);
656 if (prev_func && (prev_func != fr_event_fd_noop)) {
657 EVENT_DEBUG("\t%s prev set (%p)", map->name, prev_func);
658 prev_fflags |= map->fflags;
659 has_prev_func = true;
660 } else {
661 EVENT_DEBUG("\t%s prev unset", map->name);
662 }
663
664 new_func = *(fr_event_fd_cb_t const *)((uint8_t const *)new + map->offset);
665 if (new_func && (new_func != fr_event_fd_noop)) {
666 EVENT_DEBUG("\t%s curr set (%p)", map->name, new_func);
667 current_fflags |= map->fflags;
668 has_current_func = true;
669
670 /*
671 * Check the filter will work for the
672 * type of file descriptor specified.
673 */
674 if (!(map->type & ef->type)) {
675 fr_strerror_printf("kevent %s (%s), can't be applied to fd of type %s",
676 map->name,
679 map->type, "<INVALID>"));
680 return -1;
681 }
682
683 /*
684 * Mark this filter function as active
685 */
686 memcpy((uint8_t *)active + map->offset, (uint8_t const *)new + map->offset,
687 sizeof(fr_event_fd_cb_t));
688 } else {
689 EVENT_DEBUG("\t%s curr unset", map->name);
690
691 /*
692 * Mark this filter function as inactive
693 * by setting it to the 'noop' callback.
694 */
695 *((fr_event_fd_cb_t *)((uint8_t *)active + map->offset)) = fr_event_fd_noop;
696 }
697
698 if (!(map + 1)->coalesce) break;
699 map++;
700 } while (1);
701
702 if (out >= end) {
703 fr_strerror_const("Out of memory to store kevent filters");
704 return -1;
705 }
706
707 /*
708 * Upsert if we add a function or change the flags.
709 */
710 if (has_current_func &&
711 (!has_prev_func || (current_fflags != prev_fflags))) {
712 if ((size_t)(add_p - add) >= (NUM_ELEMENTS(add))) {
713 fr_strerror_const("Out of memory to store kevent EV_ADD filters");
714 return -1;
715 }
716 EVENT_DEBUG("\tEV_SET EV_ADD filter %s (%i), flags %i, fflags %i",
718 map->filter, map->flags, current_fflags);
719 EV_SET(add_p++, ef->fd, map->filter, map->flags, current_fflags, 0, ef);
720
721 /*
722 * Delete if we remove a function.
723 */
724 } else if (!has_current_func && has_prev_func) {
725 EVENT_DEBUG("\tEV_SET EV_DELETE filter %s (%i), flags %i, fflags %i",
727 map->filter, EV_DELETE, 0);
728 EV_SET(out++, ef->fd, map->filter, EV_DELETE, 0, 0, ef);
729 }
730 }
731
732 /*
733 * kevent is fine with adds/deletes in the same operation
734 * on the same file descriptor, but libkqueue doesn't do
735 * any kind of coalescing or ordering so you get an EEXIST
736 * error.
737 */
738 for (i = 0; i < (size_t)(add_p - add); i++) memcpy(out++, &add[i], sizeof(*out));
739
740 return out - out_kev;
741}
742
743/** Discover the type of a file descriptor
744 *
745 * This function writes the result of the discovery to the ef->type,
746 * and ef->sock_type fields.
747 *
748 * @param[out] ef to write type data to.
749 * @param[in] fd to discover the type of.
750 * @return
751 * - 0 on success.
752 * - -1 on failure.
753 */
754static int fr_event_fd_type_set(fr_event_fd_t *ef, int fd)
755{
756 socklen_t opt_len = sizeof(ef->sock_type);
757
758 /*
759 * It's a socket or PCAP socket
760 */
761 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &ef->sock_type, &opt_len) == 0) {
762#ifdef SO_GET_FILTER
763 opt_len = 0;
764 if (unlikely(getsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, NULL, &opt_len) < 0)) {
765 fr_strerror_printf("Failed determining PF status: %s", fr_syserror(errno));
766 return -1;
767 }
768 if (opt_len) {
770 } else
771#endif
772 {
774 }
775
776 /*
777 * It's a file or directory
778 */
779 } else {
780 struct stat buf;
781
782 if (errno != ENOTSOCK) {
783 fr_strerror_printf("Failed retrieving socket type: %s", fr_syserror(errno));
784 return -1;
785 }
786
787 if (fstat(fd, &buf) < 0) {
788 fr_strerror_printf("Failed calling stat() on file: %s", fr_syserror(errno));
789 return -1;
790 }
791
792 if (S_ISDIR(buf.st_mode)) {
794 } else {
796 }
797 }
798 ef->fd = fd;
799
800 return 0;
801}
802
803/** Remove a file descriptor from the event loop and rbtree but don't explicitly free it
804 *
805 *
806 * @param[in] ef to remove.
807 * @return
808 * - 0 on success.
809 * - -1 on error;
810 */
812{
813 struct kevent evset[10];
814 int count = 0;
815 fr_event_list_t *el = ef->el;
816 fr_event_funcs_t funcs;
817
818 /*
819 * Already been removed from the various trees and
820 * the event loop.
821 */
822 if (ef->is_registered) {
823 memset(&funcs, 0, sizeof(funcs));
824
825 fr_assert(ef->armour == 0);
826
827 /*
828 * If this fails, it's a pretty catastrophic error.
829 */
830 count = fr_event_build_evset(el, evset, sizeof(evset)/sizeof(*evset),
831 &ef->active, ef, &funcs, &ef->active);
832 if (count > 0) {
833 int ret;
834
835 /*
836 * If this fails, assert on debug builds.
837 */
838 ret = kevent(el->kq, evset, count, NULL, 0, NULL);
839 if (!fr_cond_assert_msg(ret >= 0,
840 "FD %i was closed without being removed from the KQ: %s",
841 ef->fd, fr_syserror(errno))) {
842 return -1; /* Prevent the free, and leave the fd in the trees */
843 }
844 }
845
846 fr_rb_delete(el->fds, ef);
847 ef->is_registered = false;
848 }
849
850 /*
851 * Insert into the deferred free list, event will be
852 * freed later.
853 */
854 if (el->in_handler) {
855 /*
856 * Don't allow the same event to be
857 * inserted into the free list multiple
858 * times.
859 *
860 * This can happen if the same ef is
861 * delivered by multiple filters, i.e.
862 * if EVFILT_READ and EVFILT_WRITE
863 * were both high, and both handlers
864 * attempted to delete the event
865 * we'd need to prevent the event being
866 * inserted into the free list multiple
867 * times.
868 */
870 return -1; /* Will be freed later */
871 } else if (fr_dlist_entry_in_list(&ef->entry)) {
873 }
874
875 return 0;
876}
877
878/** Move a file descriptor event from one event list to another
879 *
880 * FIXME - Move suspended events too.
881 *
882 * @note Any pending events will not be transferred.
883 *
884 * @param[in] dst Event list to move file descriptor event to.
885 * @param[in] src Event list to move file descriptor from.
886 * @param[in] fd of the event to move.
887 * @param[in] filter of the event to move.
888 * @return
889 * - 0 on success.
890 * - -1 on failure. The event will remain active in the src list.
891 */
893 fr_event_list_t *dst, fr_event_list_t *src, int fd, fr_event_filter_t filter)
894{
895 fr_event_fd_t *ef;
896 int ret;
897
898 if (fr_event_loop_exiting(dst)) {
899 fr_strerror_const("Destination event loop exiting");
900 return -1;
901 }
902
903 /*
904 * Ensure this exists
905 */
906 ef = fr_rb_find(src->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
907 if (unlikely(!ef)) {
908 fr_strerror_printf("No events are registered for fd %i", fd);
909 return -1;
910 }
911
913 ef->linked_ctx, NULL,
914 dst, ef->fd, ef->filter, &ef->active, ef->error, ef->uctx);
915 if (ret < 0) return -1;
916
917 (void)fr_event_fd_delete(src, ef->fd, ef->filter);
918
919 return ret;
920}
921
922
923/** Suspend/resume a subset of filters
924 *
925 * This function trades producing useful errors for speed.
926 *
927 * An example of suspending the read filter for an FD would be:
928 @code {.c}
929 static fr_event_update_t pause_read[] = {
930 FR_EVENT_SUSPEND(fr_event_io_func_t, read),
931 { 0 }
932 }
933
934 fr_event_filter_update(el, fd, FR_EVENT_FILTER_IO, pause_read);
935 @endcode
936 *
937 * @param[in] el to update descriptor in.
938 * @param[in] fd to update filters for.
939 * @param[in] filter The type of filter to update.
940 * @param[in] updates An array of updates to toggle filters on/off without removing
941 * the callback function.
942 */
944 fr_event_list_t *el, int fd, fr_event_filter_t filter, fr_event_update_t const updates[])
945{
946 fr_event_fd_t *ef;
947 size_t i;
948 fr_event_funcs_t curr_active, curr_stored;
949 struct kevent evset[10];
950 int count = 0;
951
952 ef = fr_rb_find(el->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
953 if (unlikely(!ef)) {
954 fr_strerror_printf("No events are registered for fd %i", fd);
955 return -1;
956 }
957
958#ifndef NDEBUG
959 ef->file = file;
960 ef->line = line;
961#endif
962
963 /*
964 * Cheapest way of ensuring this function can error without
965 * leaving everything in an inconsistent state.
966 */
967 memcpy(&curr_active, &ef->active, sizeof(curr_active));
968 memcpy(&curr_stored, &ef->stored, sizeof(curr_stored));
969
970 /*
971 * Apply modifications to our copies of the active/stored array.
972 */
973 for (i = 0; updates[i].op; i++) {
974 switch (updates[i].op) {
975 default:
977 fr_assert(ef->armour == 0); /* can't suspect protected FDs */
978 memcpy((uint8_t *)&ef->stored + updates[i].offset,
979 (uint8_t *)&ef->active + updates[i].offset, sizeof(fr_event_fd_cb_t));
980 memset((uint8_t *)&ef->active + updates[i].offset, 0, sizeof(fr_event_fd_cb_t));
981 break;
982
984 memcpy((uint8_t *)&ef->active + updates[i].offset,
985 (uint8_t *)&ef->stored + updates[i].offset, sizeof(fr_event_fd_cb_t));
986 memset((uint8_t *)&ef->stored + updates[i].offset, 0, sizeof(fr_event_fd_cb_t));
987 break;
988 }
989 }
990
991 count = fr_event_build_evset(el, evset, sizeof(evset)/sizeof(*evset), &ef->active,
992 ef, &ef->active, &curr_active);
993 if (unlikely(count < 0)) {
994 error:
995 memcpy(&ef->active, &curr_active, sizeof(curr_active));
996 memcpy(&ef->stored, &curr_stored, sizeof(curr_stored));
997 return -1;
998 }
999
1000 if (count && unlikely(kevent(el->kq, evset, count, NULL, 0, NULL) < 0)) {
1001 fr_strerror_printf("Failed updating filters for FD %i: %s", ef->fd, fr_syserror(errno));
1002 goto error;
1003 }
1004
1005 return 0;
1006}
1007
1008/** Insert a filter for the specified fd
1009 *
1010 * @param[in] ctx to bind lifetime of the event to.
1011 * @param[out] ef_out Previously allocated ef, or NULL.
1012 * @param[in] el to insert fd callback into.
1013 * @param[in] fd to install filters for.
1014 * @param[in] filter one of the #fr_event_filter_t values.
1015 * @param[in] funcs Structure containing callback functions. If a function pointer
1016 * is set, the equivalent kevent filter will be installed.
1017 * @param[in] error function to call when an error occurs on the fd.
1018 * @param[in] uctx to pass to handler.
1019 */
1021 TALLOC_CTX *ctx, fr_event_fd_t **ef_out,
1022 fr_event_list_t *el, int fd,
1023 fr_event_filter_t filter,
1024 void *funcs, fr_event_error_cb_t error,
1025 void *uctx)
1026{
1027 ssize_t count;
1028 fr_event_fd_t *ef;
1029 fr_event_funcs_t active;
1030 struct kevent evset[10];
1031
1032 if (unlikely(!el)) {
1033 fr_strerror_const("Invalid argument: NULL event list");
1034 return -1;
1035 }
1036
1037 if (unlikely(fd < 0)) {
1038 fr_strerror_printf("Invalid arguments: Bad FD %i", fd);
1039 return -1;
1040 }
1041
1042 if (unlikely(el->exit)) {
1043 fr_strerror_const("Event loop exiting");
1044 return -1;
1045 }
1046
1047 if (!ef_out || !*ef_out) {
1048 ef = fr_rb_find(el->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
1049 } else {
1050 ef = *ef_out;
1051 fr_assert((fd < 0) || (ef->fd == fd));
1052 }
1053
1054 /*
1055 * Need to free the event to change the talloc link.
1056 *
1057 * This is generally bad. If you hit this
1058 * code path you probably screwed up somewhere.
1059 */
1060 if (unlikely(ef && (ef->linked_ctx != ctx))) TALLOC_FREE(ef);
1061
1062 /*
1063 * No pre-existing event. Allocate an entry
1064 * for insertion into the rbtree.
1065 */
1066 if (!ef) {
1067 ef = talloc_zero(el->fds, fr_event_fd_t);
1068 if (unlikely(!ef)) {
1069 fr_strerror_const("Out of memory");
1070 return -1;
1071 }
1072 talloc_set_destructor(ef, _event_fd_delete);
1073
1074 /*
1075 * Bind the lifetime of the event to the specified
1076 * talloc ctx. If the talloc ctx is freed, the
1077 * event will also be freed.
1078 */
1079 if (ctx != el) talloc_link_ctx(ctx, ef);
1080 ef->linked_ctx = ctx;
1081 ef->el = el;
1082
1083 /*
1084 * Determine what type of file descriptor
1085 * this is.
1086 */
1087 if (fr_event_fd_type_set(ef, fd) < 0) {
1088 free:
1089 talloc_free(ef);
1090 return -1;
1091 }
1092
1093 /*
1094 * Check the filter value is valid
1095 */
1096 if ((filter > (NUM_ELEMENTS(filter_maps) - 1))) {
1097 not_supported:
1098 fr_strerror_printf("Filter %u not supported", filter);
1099 goto free;
1100 }
1101 ef->map = &filter_maps[filter];
1102 if (ef->map->idx_type == FR_EVENT_FUNC_IDX_NONE) goto not_supported;
1103
1104 count = fr_event_build_evset(el, evset, sizeof(evset)/sizeof(*evset),
1105 &ef->active, ef, funcs, &ef->active);
1106 if (count < 0) goto free;
1107 if (count && (unlikely(kevent(el->kq, evset, count, NULL, 0, NULL) < 0))) {
1108 fr_strerror_printf("Failed inserting filters for FD %i: %s", fd, fr_syserror(errno));
1109 goto free;
1110 }
1111
1112 ef->filter = filter;
1113 fr_rb_insert(el->fds, ef);
1114 ef->is_registered = true;
1115
1116 /*
1117 * Pre-existing event, update the filters and
1118 * functions associated with the file descriptor.
1119 */
1120 } else {
1121 fr_assert(ef->is_registered == true);
1122
1123 /*
1124 * Take a copy of the current set of active
1125 * functions, so we can error out in a
1126 * consistent state.
1127 */
1128 memcpy(&active, &ef->active, sizeof(ef->active));
1129
1130 fr_assert((ef->armour == 0) || ef->active.io.read);
1131
1132 count = fr_event_build_evset(el, evset, sizeof(evset)/sizeof(*evset),
1133 &ef->active, ef, funcs, &ef->active);
1134 if (count < 0) {
1135 error:
1136 memcpy(&ef->active, &active, sizeof(ef->active));
1137 return -1;
1138 }
1139 if (count && (unlikely(kevent(el->kq, evset, count, NULL, 0, NULL) < 0))) {
1140 fr_strerror_printf("Failed modifying filters for FD %i: %s", fd, fr_syserror(errno));
1141 goto error;
1142 }
1143
1144 /*
1145 * Clear any previously suspended functions
1146 */
1147 memset(&ef->stored, 0, sizeof(ef->stored));
1148 }
1149
1150#ifndef NDEBUG
1151 ef->file = file;
1152 ef->line = line;
1153#endif
1154 ef->error = error;
1155 ef->uctx = uctx;
1156
1157 if (ef_out) *ef_out = ef;
1158
1159 return 0;
1160}
1161
1162/** Associate I/O callbacks with a file descriptor
1163 *
1164 * @param[in] ctx to bind lifetime of the event to.
1165 * @param[out] ef_out Where to store the output event
1166 * @param[in] el to insert fd callback into.
1167 * @param[in] fd to install filters for.
1168 * @param[in] read_fn function to call when fd is readable.
1169 * @param[in] write_fn function to call when fd is writable.
1170 * @param[in] error function to call when an error occurs on the fd.
1171 * @param[in] uctx to pass to handler.
1172 * @return
1173 * - 0 on success.
1174 * - -1 on failure.
1175 */
1177 TALLOC_CTX *ctx, fr_event_fd_t **ef_out, fr_event_list_t *el, int fd,
1178 fr_event_fd_cb_t read_fn,
1179 fr_event_fd_cb_t write_fn,
1180 fr_event_error_cb_t error,
1181 void *uctx)
1182{
1183 fr_event_io_func_t funcs = { .read = read_fn, .write = write_fn };
1184
1185 if (unlikely(!read_fn && !write_fn)) {
1186 fr_strerror_const("Invalid arguments: All callbacks are NULL");
1187 return -1;
1188 }
1189
1191 ctx, ef_out, el, fd, FR_EVENT_FILTER_IO, &funcs, error, uctx);
1192}
1193
1194/** Remove a file descriptor from the event loop
1195 *
1196 * @param[in] el to remove file descriptor from.
1197 * @param[in] fd to remove.
1198 * @param[in] filter The type of filter to remove.
1199 * @return
1200 * - 0 if file descriptor was removed.
1201 * - <0 on error.
1202 */
1204{
1205 fr_event_fd_t *ef;
1206
1207 ef = fr_rb_find(el->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
1208 if (unlikely(!ef)) {
1209 fr_strerror_printf("No events are registered for fd %d, filter %u", fd, filter);
1210 return -1;
1211 }
1212
1213 return fr_event_fd_delete_handle(ef);
1214}
1215
1216/** Remove a file descriptor from the event loop, by handle
1217 *
1218 * Lets a caller holding the handle from #fr_event_fd_insert remove exactly the
1219 * event it inserted, without a lookup and without having to still know the fd
1220 * and filter it was inserted with.
1221 *
1222 * @param[in] ef to remove, as returned by #fr_event_fd_insert or
1223 * #fr_event_fd_handle.
1224 * @return
1225 * - 0 if file descriptor was removed.
1226 * - <0 on error.
1227 */
1229{
1230 /*
1231 * Free will normally fail if it's
1232 * a deferred free. There is a special
1233 * case for kevent failures though.
1234 *
1235 * We distinguish between the two by
1236 * looking to see if the ef is still
1237 * in the even tree.
1238 *
1239 * Talloc returning -1 guarantees the
1240 * memory has not been freed.
1241 */
1242 if ((talloc_free(ef) == -1) && ef->is_registered) return -1;
1243
1244 return 0;
1245}
1246
1247/** Get the opaque event handle from a file descriptor
1248 *
1249 * @param[in] el to search for fd/filter in.
1250 * @param[in] fd to search for.
1251 * @param[in] filter to search for.
1252 * @return
1253 * - NULL if no event could be found.
1254 * - The opaque handle representing an fd event.
1255 */
1257{
1258 fr_event_fd_t *ef;
1259
1260 ef = fr_rb_find(el->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
1261 if (unlikely(!ef)) {
1262 fr_strerror_printf("No events are registered for fd %i", fd);
1263 return NULL;
1264 }
1265
1266 return ef;
1267}
1268
1269/** Returns the appropriate callback function for a given event
1270 *
1271 * @param[in] ef the event filter fd handle.
1272 * @param[in] kq_filter If the callbacks are indexed by filter.
1273 * @param[in] kq_fflags If the callbacks are indexed by NOTES (fflags).
1274 * @return
1275 * - NULL if no event it associated with the given ef/kq_filter or kq_fflags combo.
1276 * - The callback that would be called if an event with this filter/fflag combo was received.
1277 */
1278fr_event_fd_cb_t fr_event_fd_cb(fr_event_fd_t *ef, int kq_filter, int kq_fflags)
1279{
1280 return event_fd_func(ef, &kq_filter, &kq_fflags);
1281}
1282
1283/** Returns the uctx associated with an fr_event_fd_t handle
1284 *
1285 */
1287{
1288 return ef->uctx;
1289}
1290
1291#ifndef NDEBUG
1292/** Armour an FD
1293 *
1294 * @param[in] el to remove file descriptor from.
1295 * @param[in] fd to remove.
1296 * @param[in] filter The type of filter to remove.
1297 * @param[in] armour The armour to add.
1298 * @return
1299 * - 0 if file descriptor was armoured
1300 * - <0 on error.
1301 */
1302int fr_event_fd_armour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
1303{
1304 fr_event_fd_t *ef;
1305
1306 ef = fr_rb_find(el->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
1307 if (unlikely(!ef)) {
1308 fr_strerror_printf("No events are registered for fd %i", fd);
1309 return -1;
1310 }
1311
1312 if (ef->armour != 0) {
1313 fr_strerror_printf("FD %i is already armoured", fd);
1314 return -1;
1315 }
1316
1317 ef->armour = armour;
1318
1319 return 0;
1320}
1321
1322/** Unarmour an FD
1323 *
1324 * @param[in] el to remove file descriptor from.
1325 * @param[in] fd to remove.
1326 * @param[in] filter The type of filter to remove.
1327 * @param[in] armour The armour to remove
1328 * @return
1329 * - 0 if file descriptor was unarmoured
1330 * - <0 on error.
1331 */
1332int fr_event_fd_unarmour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
1333{
1334 fr_event_fd_t *ef;
1335
1336 ef = fr_rb_find(el->fds, &(fr_event_fd_t){ .fd = fd, .filter = filter });
1337 if (unlikely(!ef)) {
1338 fr_strerror_printf("No events are registered for fd %i", fd);
1339 return -1;
1340 }
1341
1342 fr_assert(ef->armour == armour);
1343
1344 ef->armour = 0;
1345 return 0;
1346}
1347#endif
1348
1349/** Remove PID wait event from kevent if the fr_event_pid_t is freed
1350 *
1351 * @param[in] ev to free.
1352 * @return 0
1353 */
1355{
1356 struct kevent evset;
1357
1358 if (ev->parent) *ev->parent = NULL;
1359 if (!ev->is_registered || (ev->pid < 0)) return 0; /* already deleted from kevent */
1360
1361 EVENT_DEBUG("%p - Disabling event for PID %u - %p was freed", ev->el, (unsigned int)ev->pid, ev);
1362
1363 EV_SET(&evset, ev->pid, EVFILT_PROC, EV_DELETE, NOTE_EXIT, 0, ev);
1364
1365 (void) kevent(ev->el->kq, &evset, 1, NULL, 0, NULL);
1366
1367 return 0;
1368}
1369
1370/** Evaluate a EVFILT_PROC event
1371 *
1372 */
1373CC_NO_UBSAN(function) /* UBSAN: false positive - Public/private version of fr_event_list_t trips -fsanitize=function */
1374static inline CC_HINT(always_inline)
1375void event_pid_eval(fr_event_list_t *el, struct kevent *kev)
1376{
1377 pid_t pid;
1378 fr_event_pid_t *ev;
1379 fr_event_pid_cb_t callback;
1380 void *uctx;
1381
1382 EVENT_DEBUG("%p - PID %u exited with status %i",
1383 el, (unsigned int)kev->ident, (unsigned int)kev->data);
1384
1385 ev = talloc_get_type_abort((void *)kev->udata, fr_event_pid_t);
1386
1387 fr_assert(ev->pid == (pid_t) kev->ident);
1388 fr_assert((kev->fflags & NOTE_EXIT) != 0);
1389
1390 pid = ev->pid;
1391 callback = ev->callback;
1392 uctx = ev->uctx;
1393
1394 ev->is_registered = false; /* so we won't hit kevent again when it's freed */
1395
1396 /*
1397 * Delete the event before calling it.
1398 *
1399 * This also sets the parent pointer
1400 * to NULL, so the thing that started
1401 * monitoring the process knows the
1402 * handle is no longer valid.
1403 *
1404 * EVFILT_PROC NOTE_EXIT events are always
1405 * oneshot no matter what flags we pass,
1406 * so we're just reflecting the state of
1407 * the kqueue.
1408 */
1409 talloc_free(ev);
1410
1411 if (callback) callback(el, pid, (int) kev->data, uctx);
1412}
1413
1414/** Called on the next loop through the event loop when inserting an EVFILT_PROC event fails
1415 *
1416 * This is just a trampoleen function which takes the user event and simulates
1417 * an EVFILT_PROC event from it.
1418 *
1419 * @param[in] el That received the event.
1420 * @param[in] uctx An fr_event_pid_t to process.
1421 */
1423{
1424 fr_event_pid_t *ev = talloc_get_type_abort(uctx, fr_event_pid_t);
1425
1426 EVENT_DEBUG("%p - PID %ld exited early, triggered through user event", el, (long)ev->pid);
1427
1428 /*
1429 * Simulate a real struct kevent with the values we
1430 * recorded in fr_event_pid_wait.
1431 */
1432 event_pid_eval(el, &(struct kevent){ .ident = ev->pid, .data = ev->early_exit.status, .fflags = NOTE_EXIT, .udata = ev });
1433}
1434
1435/** Insert a PID event into an event list
1436 *
1437 * @note The talloc parent of the memory returned in ev_p must not be changed.
1438 * If the lifetime of the event needs to be bound to another context
1439 * this function should be called with the existing event pointed to by
1440 * ev_p.
1441 *
1442 * @param[in] ctx to bind lifetime of the event to.
1443 * @param[in] el to insert event into.
1444 * @param[in,out] ev_p If not NULL modify this event instead of creating a new one. This is a parent
1445 * in a temporal sense, not in a memory structure or dependency sense.
1446 * @param[in] pid child PID to wait for
1447 * @param[in] callback function to execute if the event fires.
1448 * @param[in] uctx user data to pass to the event.
1449 * @return
1450 * - 0 on success.
1451 * - -1 on failure.
1452 */
1454 TALLOC_CTX *ctx, fr_event_list_t *el, fr_event_pid_t const **ev_p,
1455 pid_t pid, fr_event_pid_cb_t callback, void *uctx)
1456{
1457 fr_event_pid_t *ev;
1458 struct kevent evset;
1459
1460 ev = talloc(ctx, fr_event_pid_t);
1461 if (unlikely(ev == NULL)) {
1462 fr_strerror_const("Out of memory");
1463 return -1;
1464 }
1465 *ev = (fr_event_pid_t) {
1466 .el = el,
1467 .pid = pid,
1468 .callback = callback,
1469 .uctx = uctx,
1470 .parent = ev_p,
1471#ifndef NDEBUG
1472 .file = file,
1473 .line = line,
1474#endif
1475 };
1476 talloc_set_destructor(ev, _event_pid_free);
1477
1478 /*
1479 * macOS only, on FreeBSD NOTE_EXIT always provides
1480 * the status anyway.
1481 */
1482#ifndef NOTE_EXITSTATUS
1483#define NOTE_EXITSTATUS (0)
1484#endif
1485
1486 EVENT_DEBUG("%p - Adding exit waiter for PID %u", el, (unsigned int)pid);
1487
1488 EV_SET(&evset, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT | NOTE_EXITSTATUS, 0, ev);
1489 ev->is_registered = true;
1490
1491 /*
1492 * This deals with the race where the process exited
1493 * before we could add it to the kqueue.
1494 *
1495 * Unless our caller is broken, the process should
1496 * still be available for reaping, so we check
1497 * waitid to see if there is a pending process and
1498 * then call the callback as kqueue would have done.
1499 */
1500 if (unlikely(kevent(el->kq, &evset, 1, NULL, 0, NULL) < 0)) {
1501 siginfo_t info;
1502 int ret;
1503
1504 /*
1505 * Ensure we don't accidentally pick up the error
1506 * from kevent.
1507 */
1509
1510 ev->is_registered = false;
1511
1512 /*
1513 * If the child exited before kevent() was
1514 * called, we need to get its status via
1515 * waitid().
1516 *
1517 * We don't reap the process here to emulate
1518 * what kqueue does (notify but not reap).
1519 *
1520 * waitid returns >0 on success, 0 if the
1521 * process is still running, and -1 on failure.
1522 *
1523 * If we get a 0, then that's extremely strange
1524 * as adding the kevent failed for a reason
1525 * other than the process already having exited.
1526 *
1527 * On Linux waitid will always return 1 to
1528 * indicate the process exited.
1529 *
1530 * On macOS we seem to get a mix of 1 or 0,
1531 * even if the si_code is one of the values
1532 * we'd consider to indicate that the process
1533 * had completed.
1534 */
1535 ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
1536 if (ret > 0) {
1537 static fr_table_num_sorted_t const si_codes[] = {
1538 { L("exited"), CLD_EXITED },
1539 { L("killed"), CLD_KILLED },
1540 { L("dumped"), CLD_DUMPED },
1541 { L("trapped"), CLD_TRAPPED },
1542 { L("stopped"), CLD_STOPPED },
1543 { L("continued"), CLD_CONTINUED }
1544 };
1545 static size_t si_codes_len = NUM_ELEMENTS(si_codes);
1546
1547 switch (info.si_code) {
1548 case CLD_EXITED:
1549 case CLD_KILLED:
1550 case CLD_DUMPED:
1551 EVENT_DEBUG("%p - PID %ld early exit - code %s (%d), status %d",
1552 el, (long)pid, fr_table_str_by_value(si_codes, info.si_code, "<UNKOWN>"),
1553 info.si_code, info.si_status);
1554
1555 /*
1556 * Record the status for later
1557 */
1558 ev->early_exit.status = info.si_status;
1559
1560 /*
1561 * The user event acts as a surrogate for
1562 * an EVFILT_PROC event, and will be evaluated
1563 * during the next loop through the event loop.
1564 *
1565 * It will be automatically deleted when the
1566 * fr_event_pid_t is freed.
1567 *
1568 * Previously we tried to evaluate the proc
1569 * callback here directly, but this lead to
1570 * multiple problems, the biggest being that
1571 * setting requests back to resumable failed
1572 * because they were not yet yielded,
1573 * leading to hangs.
1574 */
1575 early_exit:
1576 if (fr_event_user_insert(ev, el, &ev->early_exit.ev, true, _fr_event_pid_early_exit, ev) < 0) {
1577 fr_strerror_printf_push("Failed adding wait for PID %ld, and failed adding "
1578 "backup user event", (long) pid);
1579 error:
1580 talloc_free(ev);
1581 return -1;
1582 }
1583 break;
1584
1585 default:
1586 fr_strerror_printf("Unexpected code %s (%d) whilst waiting on PID %ld",
1587 fr_table_str_by_value(si_codes, info.si_code, "<UNKOWN>"),
1588 info.si_code, (long) pid);
1589
1590 goto error;
1591 }
1592 /*
1593 * Failed adding waiter for process, but process has not completed...
1594 *
1595 * This weird, but seems to happen on macOS occasionally.
1596 *
1597 * Add an event to run early exit...
1598 *
1599 * Man pages for waitid say if it returns 0 the info struct can be in
1600 * a nondeterministic state, so there's nothing more to do.
1601 */
1602 } else if (ret == 0) {
1603 goto early_exit;
1604 } else {
1605 /*
1606 * Print this error here, so that the caller gets
1607 * the error from kevent(), and not waitpid().
1608 */
1609 fr_strerror_printf("Failed adding waiter for PID %ld - kevent %s, waitid %s",
1610 (long) pid, fr_syserror(evset.flags), fr_syserror(errno));
1611
1612 goto error;
1613 }
1614 }
1615
1616 /*
1617 * Sometimes the caller doesn't care about getting the
1618 * PID. But we still want to clean it up.
1619 */
1620 if (ev_p) *ev_p = ev;
1621
1622 return 0;
1623}
1624
1625/** Saves some boilerplate...
1626 *
1627 */
1628static inline CC_HINT(always_inline)
1629void event_list_reap_run_callback(fr_event_pid_reap_t *reap, pid_t pid, int status)
1630{
1631 if (reap->callback) reap->callback(reap->el, pid, status, reap->uctx);
1632}
1633
1634/** Does the actual reaping of PIDs
1635 *
1636 */
1637static void _fr_event_pid_reap_cb(UNUSED fr_event_list_t *el, pid_t pid, int status, void *uctx)
1638{
1639 fr_event_pid_reap_t *reap = talloc_get_type_abort(uctx, fr_event_pid_reap_t);
1640
1641 waitpid(pid, &status, WNOHANG); /* Don't block the process if there's a logic error somewhere */
1642
1643 EVENT_DEBUG("%s - Reaper reaped PID %u, status %u - %p", __FUNCTION__, pid, status, reap);
1644
1645 event_list_reap_run_callback(reap, pid, status);
1646
1647 talloc_free(reap);
1648}
1649
1651{
1652 /*
1653 * Clear out the entry in the pid_to_reap
1654 * list if the event was inserted.
1655 */
1656 if (fr_dlist_entry_in_list(&reap->entry)) {
1657 EVENT_DEBUG("%s - Removing entry from pid_to_reap %i - %p", __FUNCTION__,
1658 reap->pid_ev ? reap->pid_ev->pid : -1, reap);
1659 fr_dlist_remove(&reap->el->pid_to_reap, reap);
1660 }
1661
1662 return 0;
1663}
1664
1665/** Asynchronously wait for a PID to exit, then reap it
1666 *
1667 * This is intended to be used when we no longer care about a process
1668 * exiting, but we still want to clean up its state so we don't have
1669 * zombie processes sticking around.
1670 *
1671 * @param[in] el to use to reap the process.
1672 * @param[in] pid to reap.
1673 * @param[in] callback to call when the process is reaped.
1674 * May be NULL.
1675 * @param[in] uctx to pass to callback.
1676 * @return
1677 * - -1 if we couldn't find the process or it has already exited/been reaped.
1678 * - 0 on success (we setup a process handler).
1679 */
1681{
1682 int ret;
1683 fr_event_pid_reap_t *reap;
1684
1685 reap = talloc_zero(NULL, fr_event_pid_reap_t);
1686 if (unlikely(!reap)) {
1687 fr_strerror_const("Out of memory");
1688 return -1;
1689 }
1690 talloc_set_destructor(reap, _fr_event_reap_free);
1691
1693 if (ret < 0) {
1694 talloc_free(reap);
1695 return ret;
1696 }
1697
1698 reap->el = el;
1699 reap->callback = callback;
1700 reap->uctx = uctx;
1701
1702 EVENT_DEBUG("%s - Adding reaper for PID %u - %p", __FUNCTION__, pid, reap);
1703
1705
1706 return ret;
1707}
1708
1709/** Send a signal to all the processes we have in our reap list, and reap them
1710 *
1711 * @param[in] el containing the processes to reap.
1712 * @param[in] timeout how long to wait before we signal the processes.
1713 * @param[in] signal to send to processes. Should be a fatal signal.
1714 * @return The number of processes reaped.
1715 */
1717{
1718 unsigned int processed = fr_dlist_num_elements(&el->pid_to_reap);
1719 fr_event_pid_reap_t *reap = NULL;
1720
1721 /*
1722 * If we've got a timeout, our best option
1723 * is to use a kqueue instance to monitor
1724 * for process exit.
1725 */
1727 int status;
1728 struct kevent evset;
1729 int waiting = 0;
1730 int kq = kqueue();
1731 fr_time_t now, start = el->pub.tl->time(), end = fr_time_add(start, timeout);
1732
1733 if (unlikely(kq < 0)) goto force;
1734
1736 if (!i->pid_ev) {
1737 EVENT_DEBUG("%p - %s - Reaper already called (logic error)... - %p",
1738 el, __FUNCTION__, i);
1739
1740 event_list_reap_run_callback(i, -1, SIGKILL);
1741 talloc_free(i);
1742 continue;
1743 }
1744
1745 /*
1746 * See if any processes have exited already
1747 */
1748 if (waitpid(i->pid_ev->pid, &status, WNOHANG) == i->pid_ev->pid) { /* reap */
1749 EVENT_DEBUG("%p - %s - Reaper PID %u already exited - %p",
1750 el, __FUNCTION__, i->pid_ev->pid, i);
1751 event_list_reap_run_callback(i, i->pid_ev->pid, SIGKILL);
1752 talloc_free(i);
1753 continue;
1754 }
1755
1756 /*
1757 * Add the rest to a temporary event loop
1758 */
1759 EV_SET(&evset, i->pid_ev->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, i);
1760 if (kevent(kq, &evset, 1, NULL, 0, NULL) < 0) {
1761 EVENT_DEBUG("%p - %s - Failed adding reaper PID %u to tmp event loop - %p",
1762 el, __FUNCTION__, i->pid_ev->pid, i);
1763 event_list_reap_run_callback(i, i->pid_ev->pid, SIGKILL);
1764 talloc_free(i);
1765 continue;
1766 }
1767 waiting++;
1768 }
1769
1770 /*
1771 * Keep draining process exits as they come in...
1772 */
1773 while ((waiting > 0) && fr_time_gt(end, (now = el->pub.tl->time()))) {
1774 struct kevent kev;
1775 int ret;
1776
1777 ret = kevent(kq, NULL, 0, &kev, 1, &fr_time_delta_to_timespec(fr_time_sub(end, now)));
1778 switch (ret) {
1779 default:
1780 EVENT_DEBUG("%p - %s - Reaper tmp loop error %s, forcing process reaping",
1781 el, __FUNCTION__, fr_syserror(errno));
1782 close(kq);
1783 goto force;
1784
1785 case 0:
1786 EVENT_DEBUG("%p - %s - Reaper timeout waiting for process exit, forcing process reaping",
1787 el, __FUNCTION__);
1788 close(kq);
1789 goto force;
1790
1791 case 1:
1792 reap = talloc_get_type_abort(kev.udata, fr_event_pid_reap_t);
1793
1794 EVENT_DEBUG("%p - %s - Reaper reaped PID %u, status %u - %p",
1795 el, __FUNCTION__, (unsigned int)kev.ident, (unsigned int)kev.data, reap);
1796 waitpid(reap->pid_ev->pid, &status, WNOHANG); /* reap */
1797
1798 event_list_reap_run_callback(reap, reap->pid_ev->pid, status);
1799 talloc_free(reap);
1800 break;
1801 }
1802 waiting--;
1803 }
1804
1805 close(kq);
1806 }
1807
1808force:
1809 /*
1810 * Deal with any lingering reap requests
1811 */
1812 while ((reap = fr_dlist_head(&el->pid_to_reap))) {
1813 int status;
1814
1815 EVENT_DEBUG("%s - Reaper forcefully reaping PID %u - %p", __FUNCTION__, reap->pid_ev->pid, reap);
1816
1817 if (kill(reap->pid_ev->pid, signal) < 0) {
1818 /*
1819 * Make sure we don't hang if the
1820 * process has actually exited.
1821 *
1822 * We could check for ESRCH but it's
1823 * not clear if that'd be returned
1824 * for a PID in the unreaped state
1825 * or not...
1826 */
1827 waitpid(reap->pid_ev->pid, &status, WNOHANG);
1828 event_list_reap_run_callback(reap, reap->pid_ev->pid, status);
1829 talloc_free(reap);
1830 continue;
1831 }
1832
1833 /*
1834 * Wait until the child process exits
1835 */
1836 waitpid(reap->pid_ev->pid, &status, 0);
1837 event_list_reap_run_callback(reap, reap->pid_ev->pid, status);
1838 talloc_free(reap);
1839 }
1840
1841 return processed;
1842}
1843
1844/** Memory will not be freed if we fail to remove the event from the kqueue
1845 *
1846 * It's easier to debug memory leaks with modern tooling than it is
1847 * to determine why we get random failures and event leaks inside of kqueue.
1848 *
1849 * @return
1850 * - 0 on success.
1851 * - -1 on failure.
1852 */
1854{
1855 if (ev->is_registered) {
1856 struct kevent evset;
1857
1858 EV_SET(&evset, (uintptr_t)ev, EVFILT_USER, EV_DELETE, 0, 0, 0);
1859
1860 if (unlikely(kevent(ev->el->kq, &evset, 1, NULL, 0, NULL) < 0)) {
1861 fr_strerror_printf("Failed removing user event - kevent %s", fr_syserror(evset.flags));
1862 return -1;
1863 }
1864 ev->is_registered = false;
1865 }
1866
1867 return 0;
1868}
1869
1870static inline CC_HINT(always_inline)
1871void event_user_eval(fr_event_list_t *el, struct kevent *kev)
1872{
1873 fr_event_user_t *ev;
1874
1875 /*
1876 * This is just a "wakeup" event, which
1877 * is always ignored.
1878 */
1879 if (kev->ident == 0) return;
1880
1881 ev = talloc_get_type_abort((void *)kev->ident, fr_event_user_t);
1882 fr_assert((uintptr_t)ev == kev->ident);
1883
1884 ev->callback(el, ev->uctx);
1885}
1886
1887/** Add a user callback to the event list.
1888 *
1889 * @param[in] ctx to allocate the event in.
1890 * @param[in] el Containing the timer events.
1891 * @param[out] ev_p Where to write a pointer.
1892 * @param[in] trigger Whether the user event is triggered initially.
1893 * @param[in] callback for EVFILT_USER.
1894 * @param[in] uctx for the callback.
1895 * @return
1896 * - 0 on success.
1897 * - -1 on error.
1898 */
1900 TALLOC_CTX *ctx, fr_event_list_t *el, fr_event_user_t **ev_p,
1901 bool trigger, fr_event_user_cb_t callback, void *uctx)
1902{
1903 fr_event_user_t *ev;
1904 struct kevent evset;
1905
1906 ev = talloc(ctx, fr_event_user_t);
1907 if (unlikely(ev == NULL)) {
1908 fr_strerror_const("Out of memory");
1909 return -1;
1910 }
1911 *ev = (fr_event_user_t) {
1912 .el = el,
1913 .callback = callback,
1914 .uctx = uctx,
1915#ifndef NDEBUG
1916 .file = file,
1917 .line = line,
1918#endif
1919 };
1920
1921 EV_SET(&evset, (uintptr_t)ev,
1922 EVFILT_USER, EV_ADD | EV_DISPATCH, (trigger * NOTE_TRIGGER), 0, ev);
1923
1924 if (unlikely(kevent(el->kq, &evset, 1, NULL, 0, NULL) < 0)) {
1925 fr_strerror_printf("Failed adding user event - kevent %s", fr_syserror(evset.flags));
1926 talloc_free(ev);
1927 return -1;
1928 }
1929 ev->is_registered = true;
1930 talloc_set_destructor(ev, _event_user_delete);
1931
1932 if (ev_p) *ev_p = ev;
1933
1934 return 0;
1935}
1936
1937/** Trigger a user event
1938 *
1939 * @param[in] ev Handle for the user event.
1940 * @return
1941 * - 0 on success.
1942 * - -1 on error.
1943 */
1945{
1946 struct kevent evset;
1947
1948 /*
1949 * The event was registered with EV_DISPATCH, which makes
1950 * kqueue auto-disable it each time it fires. A plain
1951 * NOTE_TRIGGER on a disabled event is accepted by the kernel
1952 * but not delivered to userspace until the event is
1953 * re-enabled, so we pass EV_ENABLE alongside the trigger to
1954 * re-arm it atomically. Without this, the second and
1955 * subsequent trigger calls would silently never wake the
1956 * consumer.
1957 */
1958 EV_SET(&evset, (uintptr_t)ev, EVFILT_USER, EV_ENABLE, NOTE_TRIGGER, 0, NULL);
1959
1960 if (unlikely(kevent(ev->el->kq, &evset, 1, NULL, 0, NULL) < 0)) {
1961 fr_strerror_printf("Failed triggering user event - kevent %s", fr_syserror(evset.flags));
1962 return -1;
1963 }
1964
1965 return 0;
1966}
1967
1968/** Add a pre-event callback to the event list.
1969 *
1970 * Events are serviced in insert order. i.e. insert A, B, we then
1971 * have A running before B.
1972 *
1973 * @param[in] el Containing the timer events.
1974 * @param[in] callback The pre-processing callback.
1975 * @param[in] uctx for the callback.
1976 * @return
1977 * - < 0 on error
1978 * - 0 on success
1979 */
1981{
1982 fr_event_pre_t *pre;
1983
1984 pre = talloc(el, fr_event_pre_t);
1985 pre->callback = callback;
1986 pre->uctx = uctx;
1987
1989
1990 return 0;
1991}
1992
1993/** Delete a pre-event callback from the event list.
1994 *
1995 * @param[in] el Containing the timer events.
1996 * @param[in] callback The pre-processing callback.
1997 * @param[in] uctx for the callback.
1998 * @return
1999 * - < 0 on error
2000 * - 0 on success
2001 */
2003{
2005 if ((pre->callback == callback) &&
2006 (pre->uctx == uctx)) {
2008 return talloc_free(pre);
2009 }
2010 }
2011
2012 return -1;
2013}
2014
2015/** Add a post-event callback to the event list.
2016 *
2017 * Events are serviced in insert order. i.e. insert A, B, we then
2018 * have A running before B.
2019 *
2020 * @param[in] el Containing the timer events.
2021 * @param[in] callback The post-processing callback.
2022 * @param[in] uctx for the callback.
2023 * @return
2024 * - < 0 on error
2025 * - 0 on success
2026 */
2028{
2029 fr_event_post_t *post;
2030
2031 post = talloc(el, fr_event_post_t);
2032 if (!post) return -1;
2033 post->callback = callback;
2034 post->uctx = uctx;
2035
2037
2038 return 0;
2039}
2040
2041/** Delete a post-event callback from the event list.
2042 *
2043 * @param[in] el Containing the timer events.
2044 * @param[in] callback The post-processing callback.
2045 * @param[in] uctx for the callback.
2046 * @return
2047 * - < 0 on error
2048 * - 0 on success
2049 */
2051{
2053 if ((post->callback == callback) &&
2054 (post->uctx == uctx)) {
2056 return talloc_free(post);
2057 }
2058 }
2059
2060 return -1;
2061}
2062
2063/** Gather outstanding timer and file descriptor events
2064 *
2065 * @param[in] el to process events for.
2066 * @param[in] now The current time.
2067 * @param[in] wait if true, block on the kevent() call until a timer or file descriptor event occurs.
2068 * @return
2069 * - <0 error, or the event loop is exiting
2070 * - the number of outstanding I/O events, +1 if at least one timer will fire.
2071 */
2073{
2074 fr_time_delta_t when, *wake;
2075 struct timespec ts_when, *ts_wake;
2076 int num_fd_events;
2077 bool timer_event_ready = false;
2078 fr_time_t next;
2079
2080 el->num_fd_events = 0;
2081
2082 if (el->will_exit || el->exit) {
2083 el->exit = el->will_exit;
2084
2085 fr_strerror_const("Event loop exiting");
2086 return -1;
2087 }
2088
2089 /*
2090 * By default we wait for 0ns, which means returning
2091 * immediately from kevent().
2092 */
2093 when = fr_time_delta_wrap(0);
2094 wake = &when;
2095
2096 /*
2097 * See when we have to wake up. Either now, if the timer
2098 * events are in the past. Or, we wait for a future
2099 * timer event.
2100 */
2101 next = fr_timer_list_when(el->pub.tl);
2102 if (fr_time_neq(next, fr_time_wrap(0))) {
2103 if (fr_time_lteq(next, now)) {
2104 timer_event_ready = true;
2105
2106 } else if (wait) {
2107 when = fr_time_sub(next, now);
2108
2109 } /* else we're not waiting, leave "when == 0" */
2110
2111 } else if (wait) {
2112 /*
2113 * We're asked to wait, but there's no timer
2114 * event. We can then sleep forever.
2115 */
2116 wake = NULL;
2117 }
2118
2119 /*
2120 * Run the status callbacks. It may tell us that the
2121 * application has more work to do, in which case we
2122 * re-set the timeout to be instant.
2123 *
2124 * We only run these callbacks if the caller is otherwise
2125 * idle.
2126 */
2127 if (wait) {
2129 if (pre->callback(now, wake ? *wake : fr_time_delta_wrap(0), pre->uctx) > 0) {
2130 wake = &when;
2131 when = fr_time_delta_wrap(0);
2132 }
2133 }
2134 }
2135
2136 /*
2137 * Wake is the delta between el->now
2138 * (the event loops view of the current time)
2139 * and when the event should occur.
2140 */
2141 if (wake) {
2142 ts_when = fr_time_delta_to_timespec(when);
2143 ts_wake = &ts_when;
2144 } else {
2145 ts_wake = NULL;
2146 }
2147
2148 /*
2149 * Populate el->events with the list of I/O events
2150 * that occurred since this function was last called
2151 * or wait for the next timer event.
2152 */
2153 num_fd_events = kevent(el->kq, NULL, 0, el->events, FR_EV_BATCH_FDS, ts_wake);
2154
2155 /*
2156 * Interrupt is different from timeout / FD events.
2157 */
2158 if (unlikely(num_fd_events < 0)) {
2159 if (errno == EINTR) {
2160 return 0;
2161 } else {
2162 fr_strerror_printf("Failed calling kevent: %s", fr_syserror(errno));
2163 return -1;
2164 }
2165 }
2166
2167 el->num_fd_events = num_fd_events;
2168
2169 EVENT_DEBUG("%p - %s - kevent returned %u FD events", el, __FUNCTION__, el->num_fd_events);
2170
2171 /*
2172 * If there are no FD events, we must have woken up from a timer
2173 */
2174 if (!num_fd_events) {
2175 if (wait) timer_event_ready = true;
2176 }
2177 /*
2178 * The caller doesn't really care what the value of the
2179 * return code is. Just that it's greater than zero if
2180 * events needs servicing.
2181 *
2182 * num_fd_events > 0 - if kevent() returns FD events
2183 * timer_event_ready > 0 - if there were timers ready BEFORE or AFTER calling kevent()
2184 */
2185 return num_fd_events + timer_event_ready;
2186}
2187
2188CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_event_list_t trips --fsanitize=function*/
2189static inline CC_HINT(always_inline)
2190void event_callback(fr_event_list_t *el, fr_event_fd_t *ef, int *filter, int flags, int *fflags)
2191{
2192 fr_event_fd_cb_t fd_cb;
2193
2194 while ((fd_cb = event_fd_func(ef, filter, fflags))) {
2195 fd_cb(el, ef->fd, flags, ef->uctx);
2196 }
2197}
2198
2199/** Service any outstanding timer or file descriptor events
2200 *
2201 * @param[in] el containing events to service.
2202 */
2203CC_NO_UBSAN(function) /* UBSAN: false positive - Public/private version of fr_event_list_t trips -fsanitize=function */
2205{
2206 fr_timer_list_t *etl = el->pub.tl;
2207 int i;
2208 fr_event_post_t *post;
2209 fr_time_t when, now;
2210
2211 if (unlikely(el->exit)) return;
2212
2213 EVENT_DEBUG("%p - %s - Servicing %u FD events", el, __FUNCTION__, el->num_fd_events);
2214
2215 /*
2216 * Run all of the file descriptor events.
2217 */
2218 el->in_handler = true;
2219 for (i = 0; i < el->num_fd_events; i++) {
2220 /*
2221 * Process any user events
2222 */
2223 switch (el->events[i].filter) {
2224 case EVFILT_USER:
2225 event_user_eval(el, &el->events[i]);
2226 continue;
2227
2228 /*
2229 * Process proc events
2230 */
2231 case EVFILT_PROC:
2232 event_pid_eval(el, &el->events[i]);
2233 continue;
2234
2235 /*
2236 * Process various types of file descriptor events
2237 */
2238 default:
2239 {
2240 fr_event_fd_t *ef = talloc_get_type_abort(el->events[i].udata, fr_event_fd_t);
2241 int fd_errno = 0;
2242
2243 int fflags = el->events[i].fflags; /* mutable */
2244 int filter = el->events[i].filter;
2245 int flags = el->events[i].flags;
2246
2247 if (!ef->is_registered) continue; /* Was deleted between corral and service */
2248
2249 if (unlikely(flags & EV_ERROR)) {
2250 fd_errno = el->events[i].data;
2251 ev_error:
2252 /*
2253 * Call the error handler, but only if the socket hasn't been deleted at EOF
2254 * below.
2255 */
2256 if (ef->is_registered && ef->error) ef->error(el, ef->fd, flags, fd_errno, ef->uctx);
2257 TALLOC_FREE(ef);
2258 continue;
2259 }
2260
2261 /*
2262 * EOF can indicate we've actually reached
2263 * the end of a file, but for sockets it usually
2264 * indicates the other end of the connection
2265 * has gone away.
2266 */
2267 if (flags & EV_EOF) {
2268 /*
2269 * This is fine, the callback will get notified
2270 * via the flags field.
2271 */
2272 if (ef->type == FR_EVENT_FD_FILE) goto service;
2273#if defined(__linux__) && defined(SO_GET_FILTER)
2274 /*
2275 * There seems to be an issue with the
2276 * ioctl(...SIOCNQ...) call libkqueue
2277 * uses to determine the number of bytes
2278 * readable. When ioctl returns, the number
2279 * of bytes available is set to zero, which
2280 * libkqueue interprets as EOF.
2281 *
2282 * As a workaround, if we're not reading
2283 * a file, and are operating on a raw socket
2284 * with a packet filter attached, we ignore
2285 * the EOF flag and continue.
2286 */
2287 if ((ef->sock_type == SOCK_RAW) && (ef->type == FR_EVENT_FD_PCAP)) goto service;
2288#endif
2289
2290 /*
2291 * If we see an EV_EOF flag that means the
2292 * read side of the socket has been closed
2293 * but there may still be pending data.
2294 *
2295 * Dispatch the read event and then error.
2296 */
2297 if ((el->events[i].filter == EVFILT_READ) && (el->events[i].data > 0)) {
2298 event_callback(el, ef, &filter, flags, &fflags);
2299 }
2300
2301 fd_errno = el->events[i].fflags;
2302
2303 goto ev_error;
2304 }
2305
2306 service:
2307#ifndef NDEBUG
2308 EVENT_DEBUG("Running event for fd %d, from %s[%d]", ef->fd, ef->file, ef->line);
2309#endif
2310
2311 /*
2312 * Service the event_fd events
2313 */
2314 event_callback(el, ef, &filter, flags, &fflags);
2315 }
2316 }
2317 }
2318
2319 /*
2320 * Process any deferred frees performed
2321 * by the I/O handlers.
2322 *
2323 * The events are removed from the FD rbtree
2324 * and kevent immediately, but frees are
2325 * deferred to allow stale events to be
2326 * skipped sans SEGV.
2327 */
2328 el->in_handler = false; /* Allow events to be deleted */
2329 {
2330 fr_event_fd_t *ef;
2331
2332 while ((ef = fr_dlist_head(&el->fd_to_free))) talloc_free(ef);
2333 }
2334
2335 /*
2336 * We must call el->time() again here, else the event
2337 * list's time gets updated too infrequently, and we
2338 * can end up with a situation where timers are
2339 * serviced much later than they should be, which can
2340 * cause strange interaction effects, spurious calls
2341 * to kevent, and busy loops.
2342 */
2343 now = etl->time();
2344
2345 /*
2346 * Run all of the timer events. Note that these can add
2347 * new timers!
2348 */
2350 int ret;
2351
2352 when = now;
2353
2354 ret = fr_timer_list_run(etl, &when);
2355 if (!fr_cond_assert(ret >= 0)) { /* catastrophic error, trigger event loop exit */
2356 el->exit = 1;
2357 return;
2358 }
2359
2360 EVENT_DEBUG("%p - %s - Serviced %u timer(s)", el, __FUNCTION__, (unsigned int)ret);
2361 }
2362
2363 now = etl->time();
2364
2365 /*
2366 * Run all of the post-processing events.
2367 */
2368 for (post = fr_dlist_head(&el->post_callbacks);
2369 post != NULL;
2370 post = fr_dlist_next(&el->post_callbacks, post)) {
2371 post->callback(el, now, post->uctx);
2372 }
2373}
2374
2375/** Signal an event loop exit with the specified code
2376 *
2377 * The event loop will complete its current iteration, and then exit with the specified code.
2378 *
2379 * @param[in] el to signal to exit.
2380 * @param[in] code for #fr_event_loop to return.
2381 */
2383{
2384 if (unlikely(!el)) return;
2385
2386 el->will_exit = code;
2387}
2388
2389/** Check to see whether the event loop is in the process of exiting
2390 *
2391 * @param[in] el to check.
2392 */
2394{
2395 return ((el->will_exit != 0) || (el->exit != 0));
2396}
2397
2398/** Run an event loop
2399 *
2400 * @note Will not return until #fr_event_loop_exit is called.
2401 *
2402 * @param[in] el to start processing.
2403 */
2404CC_HINT(flatten) int fr_event_loop(fr_event_list_t *el)
2405{
2406 el->will_exit = el->exit = 0;
2407
2408 el->dispatch = true;
2409 while (!el->exit) {
2410 if (unlikely(fr_event_corral(el, el->pub.tl->time(), true)) < 0) break;
2412 }
2413
2414 /*
2415 * Give processes five seconds to exit.
2416 * This means any triggers that we may
2417 * have issued when the server exited
2418 * have a chance to complete.
2419 */
2421 el->dispatch = false;
2422
2423 return el->exit;
2424}
2425
2426/** Cleanup an event list
2427 *
2428 * Frees/destroys any resources associated with an event list
2429 *
2430 * @param[in] el to free resources for.
2431 */
2433{
2435
2436 talloc_free_children(el);
2437
2438 if (el->kq >= 0) close(el->kq);
2439
2440 return 0;
2441}
2442
2443/** Free any memory we allocated for indexes
2444 *
2445 */
2446static int _event_free_indexes(UNUSED void *uctx)
2447{
2448 unsigned int i;
2449
2450 for (i = 0; i < NUM_ELEMENTS(filter_maps); i++) if (talloc_free(filter_maps[i].ev_to_func) < 0) return -1;
2451 return 0;
2452}
2453
2454static int _event_build_indexes(UNUSED void *uctx)
2455{
2456 unsigned int i;
2457
2459 return 0;
2460}
2461
2462#ifdef EVFILT_LIBKQUEUE
2463/** kqueue logging wrapper function
2464 *
2465 */
2466static CC_HINT(format (printf, 1, 2)) CC_HINT(nonnull)
2467void _event_kqueue_log(char const *fmt, ...)
2468{
2469 va_list ap;
2470
2471 va_start(ap, fmt);
2472 fr_vlog(&default_log, L_DBG, __FILE__, __LINE__, fmt, ap);
2473 va_end(ap);
2474}
2475
2476/** If we're building with libkqueue, and at debug level 4 or higher, enable libkqueue debugging output
2477 *
2478 * This requires a debug build of libkqueue
2479 */
2480static int _event_kqueue_logging(UNUSED void *uctx)
2481{
2482 struct kevent kev, receipt;
2483
2484 log_conf_kq = kqueue();
2485 if (unlikely(log_conf_kq < 0)) {
2486 fr_strerror_const("Failed initialising logging configuration kqueue");
2487 return -1;
2488 }
2489
2490 EV_SET(&kev, 0, EVFILT_LIBKQUEUE, EV_ADD, NOTE_DEBUG_FUNC, (intptr_t)_event_kqueue_log, NULL);
2491 if (kevent(log_conf_kq, &kev, 1, &receipt, 1, &(struct timespec){}) != 1) {
2492 close(log_conf_kq);
2493 log_conf_kq = -1;
2494 return 1;
2495 }
2496
2497 if (fr_debug_lvl >= L_DBG_LVL_3) {
2498 EV_SET(&kev, 0, EVFILT_LIBKQUEUE, EV_ADD, NOTE_DEBUG, 1, NULL);
2499 if (kevent(log_conf_kq, &kev, 1, &receipt, 1, &(struct timespec){}) != 1) {
2500 fr_strerror_const("Failed enabling libkqueue debug logging");
2501 close(log_conf_kq);
2502 log_conf_kq = -1;
2503 return -1;
2504 }
2505 }
2506
2507 return 0;
2508}
2509
2510static int _event_kqueue_logging_stop(UNUSED void *uctx)
2511{
2512 struct kevent kev, receipt;
2513
2514 EV_SET(&kev, 0, EVFILT_LIBKQUEUE, EV_ADD, NOTE_DEBUG_FUNC, 0, NULL);
2515 (void)kevent(log_conf_kq, &kev, 1, &receipt, 1, &(struct timespec){});
2516
2517 close(log_conf_kq);
2518 log_conf_kq = -1;
2519
2520 return 0;
2521}
2522#endif
2523
2524/** Initialise a new event list
2525 *
2526 * @param[in] ctx to allocate memory in.
2527 * @param[in] status callback, called on each iteration of the event list.
2528 * @param[in] status_uctx context for the status callback
2529 * @return
2530 * - A pointer to a new event list on success (free with talloc_free).
2531 * - NULL on error.
2532 */
2533fr_event_list_t *fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
2534{
2536 struct kevent kev;
2537 int ret;
2538
2539 /*
2540 * Build the map indexes the first time this
2541 * function is called.
2542 */
2543 fr_atexit_global_once_ret(&ret, _event_build_indexes, _event_free_indexes, NULL);
2544#ifdef EVFILT_LIBKQUEUE
2545 fr_atexit_global_once_ret(&ret, _event_kqueue_logging, _event_kqueue_logging_stop, NULL);
2546#endif
2547
2548 el = talloc_zero(ctx, fr_event_list_t);
2549 if (!fr_cond_assert(el)) {
2550 fr_strerror_const("Out of memory");
2551 return NULL;
2552 }
2553 el->kq = -1; /* So destructor can be used before kqueue() provides us with fd */
2554 talloc_set_destructor(el, _event_list_free);
2555
2557 if (!el->pub.tl) {
2558 fr_strerror_const("Failed allocating timer list");
2559 error:
2560 talloc_free(el);
2561 return NULL;
2562 }
2563
2565 if (!el->fds) {
2566 fr_strerror_const("Failed allocating FD tree");
2567 goto error;
2568 }
2569
2570 el->kq = kqueue();
2571 if (el->kq < 0) {
2572 fr_strerror_printf("Failed allocating kqueue: %s", fr_syserror(errno));
2573 goto error;
2574 }
2575
2580 if (status) (void) fr_event_pre_insert(el, status, status_uctx);
2581
2582 /*
2583 * Set our "exit" callback as ident 0.
2584 */
2585 EV_SET(&kev, 0, EVFILT_USER, EV_ADD | EV_CLEAR, NOTE_FFNOP, 0, NULL);
2586 if (kevent(el->kq, &kev, 1, NULL, 0, NULL) < 0) {
2587 fr_strerror_printf("Failed adding exit callback to kqueue: %s", fr_syserror(errno));
2588 goto error;
2589 }
2590
2591 return el;
2592}
2593
2594/** Return whether the event loop has any active events
2595 *
2596 */
2601#ifdef TESTING
2602/*
2603 * cc -g -I .. -c rb.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
2604 *
2605 * ./event
2606 *
2607 * And hit CTRL-S to stop the output, CTRL-Q to continue.
2608 * It normally alternates printing the time and sleeping,
2609 * but when you hit CTRL-S/CTRL-Q, you should see a number
2610 * of events run right after each other.
2611 *
2612 * OR
2613 *
2614 * valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./event
2615 */
2616
2617static void print_time(void *ctx)
2618{
2619 fr_time_t when;
2620 int64_t usec;
2621
2622 when = *(fr_time_t *) ctx;
2623 usec = fr_time_to_usec(when);
2624
2625 printf("%d.%06d\n", usec / USEC, usec % USEC);
2626 fflush(stdout);
2627}
2628
2629static fr_randctx rand_pool;
2630
2631static uint32_t event_rand(void)
2632{
2633 uint32_t num;
2634
2635 num = rand_pool.randrsl[rand_pool.randcnt++];
2636 if (rand_pool.randcnt == 256) {
2637 fr_isaac(&rand_pool);
2638 rand_pool.randcnt = 0;
2639 }
2640
2641 return num;
2642}
2643
2644
2645#define MAX 100
2646int main(int argc, char **argv)
2647{
2648 int i, rcode;
2649 fr_time_t array[MAX];
2650 fr_time_t now, when;
2652
2653 el = fr_event_list_alloc(NULL, NULL);
2654 if (!el) fr_exit_now(1);
2655
2656 memset(&rand_pool, 0, sizeof(rand_pool));
2657 rand_pool.randrsl[1] = time(NULL);
2658
2659 fr_rand_init(&rand_pool, 1);
2660 rand_pool.randcnt = 0;
2661
2662 array[0] = el->time();
2663 for (i = 1; i < MAX; i++) {
2664 array[i] = array[i - 1];
2665 array[i] += event_rand() & 0xffff;
2666
2667 fr_timer_at(NULL, el, array[i], false, print_time, array[i]);
2668 }
2669
2670 while (fr_event_list_num_timers(el)) {
2671 now = el->time();
2672 when = now;
2673 if (!fr_timer_run(el, &when)) {
2674 int delay = (when - now) / 1000; /* nanoseconds to microseconds */
2675
2676 printf("\tsleep %d microseconds\n", delay);
2677 fflush(stdout);
2678 usleep(delay);
2679 }
2680 }
2681
2682 talloc_free(el);
2683
2684 return 0;
2685}
2686#endif
int const char * file
Definition acutest.h:702
va_end(args)
static int const char * fmt
Definition acutest.h:573
int const char int line
Definition acutest.h:702
va_start(args, fmt)
#define RCSID(id)
Definition build.h:512
#define DIAG_UNKNOWN_PRAGMAS
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define typeof_field(_type, _field)
Typeof field.
Definition build.h:193
#define DIAG_ON(_x)
Definition build.h:487
#define CC_NO_UBSAN(_sanitize)
Definition build.h:455
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition build.h:122
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:407
#define NDEBUG_LOCATION_VALS
Definition build.h:283
#define NDEBUG_LOCATION_ARGS
Pass caller information to the function.
Definition build.h:282
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define DIAG_OFF(_x)
Definition build.h:486
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:208
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:148
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
int main(int argc, char **argv)
Definition dhcpclient.c:530
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition dlist.h:145
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
fr_event_io_func_t io
Read/write functions.
Definition event.h:214
struct fr_event_user_s fr_event_user_t
An opaque user event handle.
Definition event.h:78
void(* fr_event_fd_cb_t)(fr_event_list_t *el, int fd, int flags, void *uctx)
Called when an IO event occurs on a file descriptor.
Definition event.h:150
@ FR_EVENT_OP_SUSPEND
Temporarily remove the relevant filter from kevent.
Definition event.h:90
@ FR_EVENT_OP_RESUME
Reinsert the filter into kevent.
Definition event.h:91
fr_event_filter_t
The type of filter to install for an FD.
Definition event.h:82
@ FR_EVENT_FILTER_VNODE
Filter for vnode subfilters.
Definition event.h:84
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
size_t offset
Offset of function in func struct.
Definition event.h:97
fr_timer_list_t * tl
The timer list associated with this event loop.
Definition event.h:47
struct fr_event_pid fr_event_pid_t
An opaque PID status handle.
Definition event.h:74
fr_event_fd_cb_t read
Callback for when data is available.
Definition event.h:189
void(* fr_event_pid_cb_t)(fr_event_list_t *el, pid_t pid, int status, void *uctx)
Called when a child process has exited.
Definition event.h:169
void(* fr_event_error_cb_t)(fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
Called when an IO error event occurs on a file descriptor.
Definition event.h:160
int(* fr_event_status_cb_t)(fr_time_t now, fr_time_delta_t wake, void *uctx)
Called after each event loop cycle.
Definition event.h:141
void(* fr_event_post_cb_t)(fr_event_list_t *el, fr_time_t now, void *uctx)
Called when a post event fires.
Definition event.h:184
fr_event_op_t op
Operation to perform on function/filter.
Definition event.h:98
void(* fr_event_user_cb_t)(fr_event_list_t *el, void *uctx)
Called when a user kevent occurs.
Definition event.h:176
#define EVENT_DEBUG(...)
Definition event.h:65
#define fr_event_user_insert(_ctx, _el, _ev_p, _trigger, _callback, _uctx)
Definition event.h:281
Callbacks for the FR_EVENT_FILTER_IO filter.
Definition event.h:188
Public event list structure.
Definition event.h:46
Structure describing a modification to a filter's state.
Definition event.h:96
Callbacks for the FR_EVENT_FILTER_VNODE filter.
Definition event.h:195
Union of all filter functions.
Definition event.h:213
free(array)
talloc_free(hp)
void fr_isaac(fr_randctx *ctx)
Definition isaac.c:46
fr_dlist_head_t pre_callbacks
callbacks when we may be idle...
Definition event.c:392
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2204
fr_dlist_head_t post_callbacks
post-processing callbacks
Definition event.c:393
static fr_event_func_map_t filter_maps[]
Definition event.c:138
static int fr_event_fd_type_set(fr_event_fd_t *ef, int fd)
Discover the type of a file descriptor.
Definition event.c:754
fr_event_func_map_entry_t * func_to_ev
Function -> Event maps coalesced, out of order.
Definition event.c:134
fr_event_error_cb_t error
Callback for when an error occurs on the FD.
Definition event.c:276
char const * file
Source file this event was last updated in.
Definition event.c:318
static int8_t fr_event_fd_cmp(void const *one, void const *two)
Compare two file descriptor handles.
Definition event.c:536
fr_event_pid_cb_t callback
callback to run when the child exits
Definition event.c:334
fr_event_funcs_t stored
Stored (set, but inactive) filter functions.
Definition event.c:274
static ssize_t fr_event_build_evset(UNUSED fr_event_list_t *el, struct kevent out_kev[], size_t outlen, fr_event_funcs_t *active, fr_event_fd_t *ef, fr_event_funcs_t const *new, fr_event_funcs_t const *prev)
Build a new evset based on function pointers present.
Definition event.c:621
fr_rb_tree_t * fds
Tree used to track FDs with filters in kqueue.
Definition event.c:379
bool is_registered
Whether this fr_event_fd_t's FD has been registered with kevent.
Definition event.c:280
char const * file
Source file this event was last updated in.
Definition event.c:293
fr_time_t fr_event_list_time(fr_event_list_t *el)
Get the current server time according to the event list.
Definition event.c:590
int fr_event_pre_delete(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Delete a pre-event callback from the event list.
Definition event.c:2002
fr_event_list_t * el
Event list this event belongs to.
Definition event.c:299
static void event_list_reap_run_callback(fr_event_pid_reap_t *reap, pid_t pid, int status)
Saves some boilerplate...
Definition event.c:1629
int line
Line this event was last updated on.
Definition event.c:352
static int _event_fd_delete(fr_event_fd_t *ef)
Remove a file descriptor from the event loop and rbtree but don't explicitly free it.
Definition event.c:811
int _fr_event_pid_reap(NDEBUG_LOCATION_ARGS fr_event_list_t *el, pid_t pid, fr_event_pid_cb_t callback, void *uctx)
Asynchronously wait for a PID to exit, then reap it.
Definition event.c:1680
fr_event_filter_t filter
Definition event.c:266
#define FR_EVENT_FD_PCAP
Definition event.c:111
void * uctx
Context pointer to pass to each file descriptor callback.
Definition event.c:335
fr_event_status_cb_t callback
The callback to call.
Definition event.c:361
static void _fr_event_pid_reap_cb(UNUSED fr_event_list_t *el, pid_t pid, int status, void *uctx)
Does the actual reaping of PIDs.
Definition event.c:1637
int line
Line this event was last updated on.
Definition event.c:319
static size_t kevent_filter_table_len
Definition event.c:84
int fr_event_fd_delete_handle(fr_event_fd_t *ef)
Remove a file descriptor from the event loop, by handle.
Definition event.c:1228
struct fr_event_list_pub_s pub
Next event list in the chain.
Definition event.c:378
int _fr_event_user_insert(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_event_list_t *el, fr_event_user_t **ev_p, bool trigger, fr_event_user_cb_t callback, void *uctx)
Add a user callback to the event list.
Definition event.c:1899
fr_event_fd_type_t type
Type of events we're interested in.
Definition event.c:269
static fr_table_num_sorted_t const fr_event_fd_type_table[]
Definition event.c:249
static size_t fr_event_fd_type_table_len
Definition event.c:255
uint16_t flags
Flags to use for inserting event.
Definition event.c:125
fr_event_pid_cb_t callback
callback to run when the child exits
Definition event.c:307
static int _event_list_free(fr_event_list_t *el)
Cleanup an event list.
Definition event.c:2432
bool dispatch
Whether the event list is currently dispatching events.
Definition event.c:386
fr_dlist_head_t fd_to_free
File descriptor events pending deletion.
Definition event.c:403
bool coalesce
Coalesce this map with the next.
Definition event.c:128
fr_dlist_t entry
Entry in free list.
Definition event.c:286
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition event.c:2072
static int _event_free_indexes(UNUSED void *uctx)
Free any memory we allocated for indexes.
Definition event.c:2446
fr_event_fd_cb_t fr_event_fd_cb(fr_event_fd_t *ef, int kq_filter, int kq_fflags)
Returns the appropriate callback function for a given event.
Definition event.c:1278
void * uctx
Context for the callback.
Definition event.c:371
bool is_registered
Whether this user event has been registered with the event loop.
Definition event.c:344
int type
Type this filter applies to.
Definition event.c:127
uint64_t fr_event_list_num_timers(fr_event_list_t *el)
Return the number of timer events currently scheduled.
Definition event.c:560
fr_event_func_map_t const * map
Function map between fr_event_funcs_t and kevent filters.
Definition event.c:278
void * uctx
Context for the callback.
Definition event.c:362
fr_event_post_cb_t callback
The callback to call.
Definition event.c:370
int _fr_event_pid_wait(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_event_list_t *el, fr_event_pid_t const **ev_p, pid_t pid, fr_event_pid_cb_t callback, void *uctx)
Insert a PID event into an event list.
Definition event.c:1453
char const * name
Name of the event.
Definition event.c:123
int line
Line this event was last updated on.
Definition event.c:294
uintptr_t armour
protection flag from being deleted.
Definition event.c:289
fr_event_user_cb_t callback
The callback to call.
Definition event.c:347
int fr_event_fd_unarmour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
Unarmour an FD.
Definition event.c:1332
int sock_type
The type of socket SOCK_STREAM, SOCK_RAW etc...
Definition event.c:271
fr_dlist_head_t pid_to_reap
A list of all orphaned child processes we're waiting to reap.
Definition event.c:395
uint64_t fr_event_list_num_fds(fr_event_list_t *el)
Return the number of file descriptors is_registered with this event loop.
Definition event.c:548
int fr_event_post_delete(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Delete a post-event callback from the event list.
Definition event.c:2050
void * uctx
Context pointer to pass to each file descriptor callback.
Definition event.c:283
fr_event_func_idx_type_t idx_type
What type of index we use for event to function mapping.
Definition event.c:132
#define GET_FUNC(_ef, _offset)
void * fr_event_fd_uctx(fr_event_fd_t *ef)
Returns the uctx associated with an fr_event_fd_t handle.
Definition event.c:1286
static fr_event_fd_cb_t event_fd_func(fr_event_fd_t *ef, int *filter, int *fflags)
Figure out which function to call given a kevent.
Definition event.c:492
static int _fr_event_reap_free(fr_event_pid_reap_t *reap)
Definition event.c:1650
int kq
instance associated with this event list.
Definition event.c:390
pid_t pid
child to wait for
Definition event.c:304
static void event_pid_eval(fr_event_list_t *el, struct kevent *kev)
Evaluate a EVFILT_PROC event.
Definition event.c:1375
int fr_event_list_kq(fr_event_list_t *el)
Return the kq associated with an event list.
Definition event.c:572
void * uctx
Context for the callback.
Definition event.c:348
struct fr_event_pid::@138 early_exit
Fields that are only used if we're being triggered by a user event.
bool is_registered
Whether this user event has been registered with the event loop.
Definition event.c:301
fr_event_list_t * el
Event list this event belongs to.
Definition event.c:342
int will_exit
Will exit on next call to fr_event_corral.
Definition event.c:381
bool fr_event_list_empty(fr_event_list_t *el)
Return whether the event loop has any active events.
Definition event.c:2597
static int _event_build_indexes(UNUSED void *uctx)
Definition event.c:2454
unsigned int fr_event_list_reap_signal(fr_event_list_t *el, fr_time_delta_t timeout, int signal)
Send a signal to all the processes we have in our reap list, and reap them.
Definition event.c:1716
int16_t filter
Filter to apply.
Definition event.c:124
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition event.c:2533
static void event_fd_func_index_build(fr_event_func_map_t *map)
Definition event.c:410
static void fr_event_fd_noop(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED void *uctx)
Placeholder callback to avoid branches in service loop.
Definition event.c:600
fr_dlist_t entry
If the fr_event_pid is in the detached, reap state, it's inserted into a list associated with the eve...
Definition event.c:329
bool fr_event_loop_exiting(fr_event_list_t *el)
Check to see whether the event loop is in the process of exiting.
Definition event.c:2393
fr_dlist_t entry
Linked list of callback.
Definition event.c:360
int _fr_event_filter_update(NDEBUG_LOCATION_ARGS fr_event_list_t *el, int fd, fr_event_filter_t filter, fr_event_update_t const updates[])
Suspend/resume a subset of filters.
Definition event.c:943
char const * file
Source file this event was last updated in.
Definition event.c:351
int num_fd_events
Number of events in this event list.
Definition event.c:388
int _fr_event_fd_move(NDEBUG_LOCATION_ARGS fr_event_list_t *dst, fr_event_list_t *src, int fd, fr_event_filter_t filter)
Move a file descriptor event from one event list to another.
Definition event.c:892
fr_event_func_map_entry_t ** ev_to_func
Function -> Event maps in index order.
Definition event.c:135
int _fr_event_fd_insert(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_event_fd_t **ef_out, fr_event_list_t *el, int fd, fr_event_fd_cb_t read_fn, fr_event_fd_cb_t write_fn, fr_event_error_cb_t error, void *uctx)
Associate I/O callbacks with a file descriptor.
Definition event.c:1176
fr_event_fd_type_t
Definition event.c:90
@ FR_EVENT_FD_FILE
is a file.
Definition event.c:92
@ FR_EVENT_FD_DIRECTORY
is a directory.
Definition event.c:93
@ FR_EVENT_FD_SOCKET
is a socket.
Definition event.c:91
fr_event_pid_t const * pid_ev
pid_ev this reaper is bound to.
Definition event.c:327
fr_event_funcs_t active
Active filter functions.
Definition event.c:273
int fr_event_pre_insert(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Add a pre-event callback to the event list.
Definition event.c:1980
static void _fr_event_pid_early_exit(fr_event_list_t *el, void *uctx)
Called on the next loop through the event loop when inserting an EVFILT_PROC event fails.
Definition event.c:1422
static void event_user_eval(fr_event_list_t *el, struct kevent *kev)
Definition event.c:1871
int exit
If non-zero event loop will prevent the addition of new events, and will return immediately from the ...
Definition event.c:382
fr_event_list_t * el
Event list this event belongs to.
Definition event.c:265
static fr_table_num_sorted_t const kevent_filter_table[]
Definition event.c:67
TALLOC_CTX * linked_ctx
talloc ctx this event was bound to.
Definition event.c:284
static void event_callback(fr_event_list_t *el, fr_event_fd_t *ef, int *filter, int flags, int *fflags)
Definition event.c:2190
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2382
#define FR_EV_BATCH_FDS
Definition event.c:57
void * uctx
Context pointer to pass to each file descriptor callback.
Definition event.c:308
static int _event_pid_free(fr_event_pid_t *ev)
Remove PID wait event from kevent if the fr_event_pid_t is freed.
Definition event.c:1354
fr_event_list_t * el
Event list this event belongs to.
Definition event.c:326
int fd
File descriptor we're listening for events on.
Definition event.c:267
size_t offset
Offset of function pointer in structure.
Definition event.c:122
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
fr_dlist_t entry
Linked list of callback.
Definition event.c:369
int fr_event_loop(fr_event_list_t *el)
Run an event loop.
Definition event.c:2404
int fr_event_user_trigger(fr_event_user_t *ev)
Trigger a user event.
Definition event.c:1944
fr_event_fd_t * fr_event_fd_handle(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Get the opaque event handle from a file descriptor.
Definition event.c:1256
fr_rb_node_t node
Entry in the tree of file descriptor handles.
Definition event.c:261
int _fr_event_filter_insert(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_event_fd_t **ef_out, fr_event_list_t *el, int fd, fr_event_filter_t filter, void *funcs, fr_event_error_cb_t error, void *uctx)
Insert a filter for the specified fd.
Definition event.c:1020
#define NOTE_EXITSTATUS
int fr_event_post_insert(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Add a post-event callback to the event list.
Definition event.c:2027
fr_event_pid_t const ** parent
Definition event.c:305
static int _event_user_delete(fr_event_user_t *ev)
Memory will not be freed if we fail to remove the event from the kqueue.
Definition event.c:1853
struct kevent events[FR_EV_BATCH_FDS]
Definition event.c:398
fr_event_func_idx_type_t
Definition event.c:100
@ FR_EVENT_FUNC_IDX_FILTER
Sign flip is performed i.e. -1 = 0The filter is used / as the index in the ev to func index.
Definition event.c:103
@ FR_EVENT_FUNC_IDX_NONE
Definition event.c:101
@ FR_EVENT_FUNC_IDX_FFLAGS
The bit position of the flags in FFLAGS is used to provide the index.
Definition event.c:105
int fr_event_fd_armour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
Armour an FD.
Definition event.c:1302
bool in_handler
Deletes should be deferred until after the handlers complete.
Definition event.c:400
uint32_t fflags
fflags to pass to filter.
Definition event.c:126
A file descriptor/filter event.
Definition event.c:260
Specifies a mapping between a function pointer in a structure and its respective event.
Definition event.c:121
Stores all information relating to an event list.
Definition event.c:377
Hold additional information for automatically reaped PIDs.
Definition event.c:325
Callbacks to perform after all timers and FDs have been checked.
Definition event.c:368
Callbacks to perform when the event handler is about to check the events.
Definition event.c:359
Callbacks for kevent() user events.
Definition event.c:341
int fr_debug_lvl
Definition log.c:41
fr_log_t default_log
Definition log.c:306
void fr_vlog(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt, va_list ap)
Send a server log message to its destination.
Definition log.c:380
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition log.h:69
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:56
static uint8_t fr_high_bit_pos(uint64_t num)
Find the highest order high bit in an unsigned 64 bit integer.
Definition math.h:94
unsigned short uint16_t
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
#define fr_assert(_expr)
Definition rad_assert.h:37
void fr_rand_init(void)
Definition rand.c:33
uint32_t randrsl[256]
Definition rand.h:40
uint32_t randcnt
Definition rand.h:39
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:781
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:741
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:244
The main red black tree structure.
Definition rb.h:71
return count
Definition module.c:155
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition talloc.c:168
#define fr_time_delta_to_timespec(_delta)
Convert a delta to a timespec.
Definition time.h:666
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_lteq(_a, _b)
Definition time.h:240
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_eq(_a, _b)
Definition time.h:241
static int64_t fr_time_to_usec(fr_time_t when)
Convert an fr_time_t (internal time) to number of usec since the unix epoch (wallclock time)
Definition time.h:701
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_gt(_a, _b)
Definition time.h:237
#define USEC
Definition time.h:380
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
#define fr_time_neq(_a, _b)
Definition time.h:242
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
Definition timer.c:938
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
Definition timer.c:1148
fr_time_t fr_timer_list_when(fr_timer_list_t *tl)
Return the time of the next event.
Definition timer.c:1185
fr_timer_list_t * fr_timer_list_lst_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new lst based timer list.
Definition timer.c:1262
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define fr_timer_at(...)
Definition timer.h:81
int trigger(unlang_interpret_t *intp, CONF_SECTION const *cs, CONF_PAIR **trigger_cp, char const *name, bool rate_limit, fr_pair_list_t *args)
Execute a trigger - call an executable to process an event.
Definition trigger.c:155
static fr_event_list_t * el
#define add(_type, _out, _in)
Definition stats.c:187
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1030