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