30RCSID(
"$Id: f0d41fbc2ca603903bde4bf671fcee3b95cf9093 $")
32#define _EVENT_LIST_PRIVATE 1
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>
57#define FR_EV_BATCH_FDS (256)
60#define fr_time() static_assert(0, "Use el->time for event loop timing")
63#if !defined(SO_GET_FILTER) && defined(SO_ATTACH_FILTER)
64# define SO_GET_FILTER SO_ATTACH_FILTER
69 {
L(
"EVFILT_AIO"), EVFILT_AIO },
72 {
L(
"EVFILT_EXCEPT"), EVFILT_EXCEPT },
75 {
L(
"EVFILT_MACHPORT"), EVFILT_MACHPORT },
77 {
L(
"EVFILT_PROC"), EVFILT_PROC },
78 {
L(
"EVFILT_READ"), EVFILT_READ },
79 {
L(
"EVFILT_SIGNAL"), EVFILT_SIGNAL },
80 {
L(
"EVFILT_TIMER"), EVFILT_TIMER },
81 {
L(
"EVFILT_VNODE"), EVFILT_VNODE },
82 {
L(
"EVFILT_WRITE"), EVFILT_WRITE }
86#ifdef EVFILT_LIBKQUEUE
87static int log_conf_kq;
111# define FR_EVENT_FD_PCAP 0
145 .filter = EVFILT_READ,
146 .flags = EV_ADD | EV_ENABLE,
157 .filter = EVFILT_WRITE,
158 .flags = EV_ADD | EV_ENABLE,
171 .filter = EVFILT_VNODE,
172 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
173 .fflags = NOTE_DELETE,
180 .filter = EVFILT_VNODE,
181 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
182 .fflags = NOTE_WRITE,
189 .filter = EVFILT_VNODE,
190 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
191 .fflags = NOTE_EXTEND,
198 .filter = EVFILT_VNODE,
199 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
200 .fflags = NOTE_ATTRIB,
207 .filter = EVFILT_VNODE,
208 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
216 .filter = EVFILT_VNODE,
217 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
218 .fflags = NOTE_RENAME,
226 .filter = EVFILT_VNODE,
227 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
228 .fflags = NOTE_REVOKE,
237 .filter = EVFILT_VNODE,
238 .flags = EV_ADD | EV_ENABLE | EV_CLEAR,
239 .fflags = NOTE_FUNLOCK,
405#ifdef WITH_EVENT_DEBUG
448 if (pos > high) high = pos;
471 fflags &= ~(1 << pos);
496#define GET_FUNC(_ef, _offset) *((fr_event_fd_cb_t const *)((uint8_t const *)&(_ef)->active + _offset))
507 if (!*filter)
return NULL;
517 int our_fflags = *fflags;
520 if (!pos)
return NULL;
523 *fflags = our_fflags & ~(1 << pos);
622#ifndef WITH_EVENT_DEBUG
630 struct kevent *
out = out_kev, *end =
out + outlen;
632 struct kevent add[10], *add_p = add;
635 EVENT_DEBUG(
"%p - Building new evset for FD %i (new %p, prev %p)",
el, ef->
fd,
new, prev);
642 bool has_current_func =
false;
643 bool has_prev_func =
false;
658 prev_fflags |= map->
fflags;
659 has_prev_func =
true;
667 current_fflags |= map->
fflags;
668 has_current_func =
true;
679 map->
type,
"<INVALID>"));
698 if (!(map + 1)->coalesce)
break;
710 if (has_current_func &&
711 (!has_prev_func || (current_fflags != prev_fflags))) {
716 EVENT_DEBUG(
"\tEV_SET EV_ADD filter %s (%i), flags %i, fflags %i",
719 EV_SET(add_p++, ef->
fd, map->
filter, map->
flags, current_fflags, 0, ef);
724 }
else if (!has_current_func && has_prev_func) {
725 EVENT_DEBUG(
"\tEV_SET EV_DELETE filter %s (%i), flags %i, fflags %i",
727 map->
filter, EV_DELETE, 0);
728 EV_SET(
out++, ef->
fd, map->
filter, EV_DELETE, 0, 0, ef);
738 for (i = 0; i < (
size_t)(add_p - add); i++) memcpy(
out++, &add[i],
sizeof(*
out));
740 return out - out_kev;
756 socklen_t opt_len =
sizeof(ef->
sock_type);
761 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &ef->
sock_type, &opt_len) == 0) {
764 if (
unlikely(getsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, NULL, &opt_len) < 0)) {
782 if (errno != ENOTSOCK) {
787 if (fstat(fd, &buf) < 0) {
792 if (S_ISDIR(buf.st_mode)) {
813 struct kevent evset[10];
823 memset(&funcs, 0,
sizeof(funcs));
838 ret = kevent(
el->
kq, evset,
count, NULL, 0, NULL);
840 "FD %i was closed without being removed from the KQ: %s",
915 if (ret < 0)
return -1;
949 struct kevent evset[10];
967 memcpy(&curr_active, &ef->
active,
sizeof(curr_active));
968 memcpy(&curr_stored, &ef->
stored,
sizeof(curr_stored));
973 for (i = 0; updates[i].
op; i++) {
974 switch (updates[i].op) {
992 ef, &ef->
active, &curr_active);
995 memcpy(&ef->
active, &curr_active,
sizeof(curr_active));
996 memcpy(&ef->
stored, &curr_stored,
sizeof(curr_stored));
1030 struct kevent evset[10];
1047 if (!ef_out || !*ef_out) {
1157 if (ef_out) *ef_out = ef;
1185 if (
unlikely(!read_fn && !write_fn)) {
1339 struct kevent evset;
1344 EVENT_DEBUG(
"%p - Disabling event for PID %u - %p was freed", ev->
el, (
unsigned int)ev->
pid, ev);
1346 EV_SET(&evset, ev->
pid, EVFILT_PROC, EV_DELETE, NOTE_EXIT, 0, ev);
1348 (void) kevent(ev->
el->
kq, &evset, 1, NULL, 0, NULL);
1357static inline CC_HINT(always_inline)
1366 el, (
unsigned int)kev->ident, (
unsigned int)kev->data);
1371 fr_assert((kev->fflags & NOTE_EXIT) != 0);
1394 if (callback) callback(
el, pid, (
int) kev->data, uctx);
1409 EVENT_DEBUG(
"%p - PID %ld exited early, triggered through user event",
el, (
long)ev->
pid);
1441 struct kevent evset;
1451 .callback = callback,
1465#ifndef NOTE_EXITSTATUS
1466#define NOTE_EXITSTATUS (0)
1469 EVENT_DEBUG(
"%p - Adding exit waiter for PID %u",
el, (
unsigned int)pid);
1471 EV_SET(&evset, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT |
NOTE_EXITSTATUS, 0, ev);
1483 if (
unlikely(kevent(
el->
kq, &evset, 1, NULL, 0, NULL) < 0)) {
1518 ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
1521 {
L(
"exited"), CLD_EXITED },
1522 {
L(
"killed"), CLD_KILLED },
1523 {
L(
"dumped"), CLD_DUMPED },
1524 {
L(
"trapped"), CLD_TRAPPED },
1525 {
L(
"stopped"), CLD_STOPPED },
1526 {
L(
"continued"), CLD_CONTINUED }
1530 switch (info.si_code) {
1534 EVENT_DEBUG(
"%p - PID %ld early exit - code %s (%d), status %d",
1536 info.si_code, info.si_status);
1561 "backup user event", (
long) pid);
1571 info.si_code, (
long) pid);
1585 }
else if (ret == 0) {
1603 if (ev_p) *ev_p = ev;
1611static inline CC_HINT(always_inline)
1614 if (reap->callback) reap->
callback(reap->el, pid, status, reap->uctx);
1624 waitpid(pid, &status, WNOHANG);
1626 EVENT_DEBUG(
"%s - Reaper reaped PID %u, status %u - %p", __FUNCTION__, pid, status, reap);
1640 EVENT_DEBUG(
"%s - Removing entry from pid_to_reap %i - %p", __FUNCTION__,
1685 EVENT_DEBUG(
"%s - Adding reaper for PID %u - %p", __FUNCTION__, pid, reap);
1711 struct kevent evset;
1720 EVENT_DEBUG(
"%p - %s - Reaper already called (logic error)... - %p",
1721 el, __FUNCTION__, i);
1731 if (
waitpid(i->pid_ev->pid, &status, WNOHANG) == i->pid_ev->pid) {
1732 EVENT_DEBUG(
"%p - %s - Reaper PID %u already exited - %p",
1733 el, __FUNCTION__, i->pid_ev->pid, i);
1742 EV_SET(&evset, i->pid_ev->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, i);
1743 if (kevent(
kq, &evset, 1, NULL, 0, NULL) < 0) {
1744 EVENT_DEBUG(
"%p - %s - Failed adding reaper PID %u to tmp event loop - %p",
1745 el, __FUNCTION__, i->pid_ev->pid, i);
1763 EVENT_DEBUG(
"%p - %s - Reaper tmp loop error %s, forcing process reaping",
1769 EVENT_DEBUG(
"%p - %s - Reaper timeout waiting for process exit, forcing process reaping",
1777 EVENT_DEBUG(
"%p - %s - Reaper reaped PID %u, status %u - %p",
1778 el, __FUNCTION__, (
unsigned int)kev.ident, (
unsigned int)kev.data, reap);
1798 EVENT_DEBUG(
"%s - Reaper forcefully reaping PID %u - %p", __FUNCTION__, reap->pid_ev->pid, reap);
1800 if (kill(reap->pid_ev->pid, signal) < 0) {
1810 waitpid(reap->pid_ev->pid, &status, WNOHANG);
1839 struct kevent evset;
1841 EV_SET(&evset, (uintptr_t)ev, EVFILT_USER, EV_DELETE, 0, 0, 0);
1843 if (
unlikely(kevent(ev->el->kq, &evset, 1, NULL, 0, NULL) < 0)) {
1847 ev->is_registered =
false;
1853static inline CC_HINT(always_inline)
1862 if (kev->ident == 0)
return;
1887 struct kevent evset;
1896 .callback = callback,
1904 EV_SET(&evset, (uintptr_t)ev,
1905 EVFILT_USER, EV_ADD | EV_DISPATCH, (trigger * NOTE_TRIGGER), 0, ev);
1907 if (
unlikely(kevent(
el->
kq, &evset, 1, NULL, 0, NULL) < 0)) {
1915 if (ev_p) *ev_p = ev;
1930 struct kevent evset;
1932 EV_SET(&evset, (uintptr_t)ev, EVFILT_USER, EV_ENABLE, NOTE_TRIGGER, 0, NULL);
1934 if (
unlikely(kevent(
el->
kq, &evset, 1, NULL, 0, NULL) < 0)) {
1986 (pre->
uctx == uctx)) {
2039 if ((post->
callback == callback) &&
2040 (post->
uctx == uctx)) {
2062 struct timespec ts_when, *ts_wake;
2065 bool timer_event_ready =
false;
2092 timer_event_ready =
true;
2149 if (errno == EINTR) {
2164 if (!num_fd_events) {
2165 if (wait) timer_event_ready =
true;
2175 return num_fd_events + timer_event_ready;
2179static inline CC_HINT(always_inline)
2185 fd_cb(
el, ef->fd, flags, ef->uctx);
2257 if (flags & EV_EOF) {
2263#if defined(__linux__) && defined(SO_GET_FILTER)
2350 EVENT_DEBUG(
"%p - %s - Serviced %u timer(s)",
el, __FUNCTION__, (
unsigned int)ret);
2426 talloc_free_children(
el);
2452#ifdef EVFILT_LIBKQUEUE
2456static CC_HINT(format (printf, 1, 2)) CC_HINT(
nonnull)
2457void _event_kqueue_log(
char const *
fmt, ...)
2470static int _event_kqueue_logging(
UNUSED void *uctx)
2472 struct kevent kev, receipt;
2474 log_conf_kq = kqueue();
2480 EV_SET(&kev, 0, EVFILT_LIBKQUEUE, EV_ADD, NOTE_DEBUG_FUNC, (intptr_t)_event_kqueue_log, NULL);
2481 if (kevent(log_conf_kq, &kev, 1, &receipt, 1, &(
struct timespec){}) != 1) {
2488 EV_SET(&kev, 0, EVFILT_LIBKQUEUE, EV_ADD, NOTE_DEBUG, 1, NULL);
2489 if (kevent(log_conf_kq, &kev, 1, &receipt, 1, &(
struct timespec){}) != 1) {
2500static int _event_kqueue_logging_stop(
UNUSED void *uctx)
2502 struct kevent kev, receipt;
2504 EV_SET(&kev, 0, EVFILT_LIBKQUEUE, EV_ADD, NOTE_DEBUG_FUNC, 0, NULL);
2505 (void)kevent(log_conf_kq, &kev, 1, &receipt, 1, &(
struct timespec){});
2534#ifdef EVFILT_LIBKQUEUE
2535 fr_atexit_global_once_ret(&ret, _event_kqueue_logging, _event_kqueue_logging_stop, NULL);
2575 EV_SET(&kev, 0, EVFILT_USER, EV_ADD | EV_CLEAR, NOTE_FFNOP, 0, NULL);
2576 if (kevent(
el->
kq, &kev, 1, NULL, 0, NULL) < 0) {
2607static void print_time(
void *ctx)
2615 printf(
"%d.%06d\n", usec /
USEC, usec %
USEC);
2626 if (rand_pool.
randcnt == 256) {
2636int main(
int argc,
char **argv)
2646 memset(&rand_pool, 0,
sizeof(rand_pool));
2647 rand_pool.
randrsl[1] = time(NULL);
2652 array[0] =
el->time();
2653 for (i = 1; i < MAX; i++) {
2654 array[i] = array[i - 1];
2655 array[i] += event_rand() & 0xffff;
2657 fr_timer_at(NULL,
el, array[i],
false, print_time, array[i]);
2663 if (!fr_timer_run(
el, &when)) {
2664 int delay = (when - now) / 1000;
2666 printf(
"\tsleep %d microseconds\n", delay);
static int const char * fmt
#define DIAG_UNKNOWN_PRAGMAS
#define L(_str)
Helper for initialising arrays of string literals.
#define typeof_field(_type, _field)
Typeof field.
#define CC_NO_UBSAN(_sanitize)
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
#define NDEBUG_LOCATION_VALS
#define NDEBUG_LOCATION_ARGS
Pass caller information to the function.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
int main(int argc, char **argv)
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.
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
#define fr_dlist_foreach_safe(_list_head, _type, _iter)
Iterate over the contents of a list allowing for removals.
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Head of a doubly linked list.
Entry in a doubly linked list.
#define fr_event_user_insert(_ctx, _ev_p, _el, _trigger, _callback, _uctx)
fr_event_io_func_t io
Read/write functions.
struct fr_event_user_s fr_event_user_t
An opaque user event handle.
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.
@ FR_EVENT_OP_SUSPEND
Temporarily remove the relevant filter from kevent.
@ FR_EVENT_OP_RESUME
Reinsert the filter into kevent.
fr_event_filter_t
The type of filter to install for an FD.
@ FR_EVENT_FILTER_VNODE
Filter for vnode subfilters.
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
size_t offset
Offset of function in func struct.
fr_timer_list_t * tl
The timer list associated with this event loop.
struct fr_event_pid fr_event_pid_t
An opaque PID status handle.
fr_event_fd_cb_t read
Callback for when data is available.
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.
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.
int(* fr_event_status_cb_t)(fr_time_t now, fr_time_delta_t wake, void *uctx)
Called after each event loop cycle.
void(* fr_event_post_cb_t)(fr_event_list_t *el, fr_time_t now, void *uctx)
Called when a post event fires.
fr_event_op_t op
Operation to perform on function/filter.
void(* fr_event_user_cb_t)(fr_event_list_t *el, void *uctx)
Called when a user kevent occurs.
Callbacks for the FR_EVENT_FILTER_IO filter.
Public event list structure.
Structure describing a modification to a filter's state.
Callbacks for the FR_EVENT_FILTER_VNODE filter.
Union of all filter functions.
void fr_isaac(fr_randctx *ctx)
fr_dlist_head_t pre_callbacks
callbacks when we may be idle...
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
fr_dlist_head_t post_callbacks
post-processing callbacks
static fr_event_func_map_t filter_maps[]
static int fr_event_fd_type_set(fr_event_fd_t *ef, int fd)
Discover the type of a file descriptor.
fr_event_func_map_entry_t * func_to_ev
Function -> Event maps coalesced, out of order.
fr_event_error_cb_t error
Callback for when an error occurs on the FD.
char const * file
Source file this event was last updated in.
static int8_t fr_event_fd_cmp(void const *one, void const *two)
Compare two file descriptor handles.
fr_event_pid_cb_t callback
callback to run when the child exits
fr_event_funcs_t stored
Stored (set, but inactive) filter functions.
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.
fr_rb_tree_t * fds
Tree used to track FDs with filters in kqueue.
bool is_registered
Whether this fr_event_fd_t's FD has been registered with kevent.
char const * file
Source file this event was last updated in.
fr_time_t fr_event_list_time(fr_event_list_t *el)
Get the current server time according to the event list.
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.
fr_event_list_t * el
Event list this event belongs to.
static void event_list_reap_run_callback(fr_event_pid_reap_t *reap, pid_t pid, int status)
Saves some boilerplate...
int line
Line this event was last updated on.
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.
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.
void * uctx
Context pointer to pass to each file descriptor callback.
fr_event_status_cb_t callback
The callback to call.
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.
int line
Line this event was last updated on.
static size_t kevent_filter_table_len
struct fr_event_list_pub_s pub
Next event list in the chain.
struct fr_event_pid::@132 early_exit
Fields that are only used if we're being triggered by a user event.
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.
fr_event_fd_type_t type
Type of events we're interested in.
static fr_table_num_sorted_t const fr_event_fd_type_table[]
static size_t fr_event_fd_type_table_len
uint16_t flags
Flags to use for inserting event.
waitpid(reap->pid_ev->pid, &status, 0)
fr_event_pid_cb_t callback
callback to run when the child exits
static int _event_list_free(fr_event_list_t *el)
Cleanup an event list.
bool dispatch
Whether the event list is currently dispatching events.
fr_dlist_head_t fd_to_free
File descriptor events pending deletion.
bool coalesce
Coalesce this map with the next.
fr_dlist_t entry
Entry in free list.
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
static int _event_free_indexes(UNUSED void *uctx)
Free any memory we allocated for indexes.
fr_event_fd_cb_t fr_event_fd_cb(fr_event_fd_t *ef, int kq_filter, int kq_fflags)
Returns the appropriate callback function for a given event.
void * uctx
Context for the callback.
bool is_registered
Whether this user event has been registered with the event loop.
int type
Type this filter applies to.
uint64_t fr_event_list_num_timers(fr_event_list_t *el)
Return the number of timer events currently scheduled.
fr_event_func_map_t const * map
Function map between fr_event_funcs_t and kevent filters.
void * uctx
Context for the callback.
fr_event_post_cb_t callback
The callback to call.
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.
char const * name
Name of the event.
int fr_event_user_trigger(fr_event_list_t *el, fr_event_user_t *ev)
Trigger a user event.
int line
Line this event was last updated on.
uintptr_t armour
protection flag from being deleted.
fr_event_user_cb_t callback
The callback to call.
int fr_event_fd_unarmour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
Unarmour an FD.
int sock_type
The type of socket SOCK_STREAM, SOCK_RAW etc...
fr_dlist_head_t pid_to_reap
A list of all orphaned child processes we're waiting to reap.
uint64_t fr_event_list_num_fds(fr_event_list_t *el)
Return the number of file descriptors is_registered with this event loop.
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.
void * uctx
Context pointer to pass to each file descriptor callback.
fr_event_func_idx_type_t idx_type
What type of index we use for event to function mapping.
#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.
static fr_event_fd_cb_t event_fd_func(fr_event_fd_t *ef, int *filter, int *fflags)
Figure out which function to call given a kevent.
static int _fr_event_reap_free(fr_event_pid_reap_t *reap)
int kq
instance associated with this event list.
pid_t pid
child to wait for
static void event_pid_eval(fr_event_list_t *el, struct kevent *kev)
Evaluate a EVFILT_PROC event.
int fr_event_list_kq(fr_event_list_t *el)
Return the kq associated with an event list.
void * uctx
Context for the callback.
bool is_registered
Whether this user event has been registered with the event loop.
fr_event_list_t * el
Event list this event belongs to.
int will_exit
Will exit on next call to fr_event_corral.
bool fr_event_list_empty(fr_event_list_t *el)
Return whether the event loop has any active events.
static int _event_build_indexes(UNUSED void *uctx)
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.
int16_t filter
Filter to apply.
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.
static void event_fd_func_index_build(fr_event_func_map_t *map)
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.
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...
bool fr_event_loop_exiting(fr_event_list_t *el)
Check to see whether the event loop is in the process of exiting.
fr_dlist_t entry
Linked list of callback.
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.
char const * file
Source file this event was last updated in.
int num_fd_events
Number of events in this event list.
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.
fr_event_func_map_entry_t ** ev_to_func
Function -> Event maps in index order.
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.
@ FR_EVENT_FD_FILE
is a file.
@ FR_EVENT_FD_DIRECTORY
is a directory.
@ FR_EVENT_FD_SOCKET
is a socket.
fr_event_pid_t const * pid_ev
pid_ev this reaper is bound to.
fr_event_funcs_t active
Active filter functions.
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.
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.
static void event_user_eval(fr_event_list_t *el, struct kevent *kev)
int exit
If non-zero event loop will prevent the addition of new events, and will return immediately from the ...
fr_event_list_t * el
Event list this event belongs to.
static fr_table_num_sorted_t const kevent_filter_table[]
TALLOC_CTX * linked_ctx
talloc ctx this event was bound to.
static void event_callback(fr_event_list_t *el, fr_event_fd_t *ef, int *filter, int flags, int *fflags)
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
void * uctx
Context pointer to pass to each file descriptor callback.
static int _event_pid_free(fr_event_pid_t *ev)
Remove PID wait event from kevent if the fr_event_pid_t is freed.
fr_event_list_t * el
Event list this event belongs to.
int fd
File descriptor we're listening for events on.
size_t offset
Offset of function pointer in structure.
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.
fr_dlist_t entry
Linked list of callback.
int fr_event_loop(fr_event_list_t *el)
Run an event loop.
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.
fr_rb_node_t node
Entry in the tree of file descriptor handles.
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.
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.
fr_event_pid_t const ** parent
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.
struct kevent events[FR_EV_BATCH_FDS]
@ 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.
@ FR_EVENT_FUNC_IDX_FFLAGS
The bit position of the flags in FFLAGS is used to provide the index.
int fr_event_fd_armour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
Armour an FD.
bool in_handler
Deletes should be deferred until after the handlers complete.
uint32_t fflags
fflags to pass to filter.
A file descriptor/filter event.
Specifies a mapping between a function pointer in a structure and its respective event.
Stores all information relating to an event list.
Hold additional information for automatically reaped PIDs.
Callbacks to perform after all timers and FDs have been checked.
Callbacks to perform when the event handler is about to check the events.
Callbacks for kevent() user events.
void fr_vlog(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt, va_list ap)
Send a server log message to its destination.
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
@ L_DBG
Only displayed when debugging is enabled.
static uint8_t fr_high_bit_pos(uint64_t num)
Find the highest order high bit in an unsigned 64 bit integer.
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
#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.
The main red black tree structure.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
An element in a lexicographically sorted array of name to num mappings.
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.
#define fr_time_delta_to_timespec(_delta)
Convert a delta to a timespec.
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
#define fr_time_delta_wrap(_time)
#define fr_time_wrap(_time)
#define fr_time_lteq(_a, _b)
#define fr_time_delta_ispos(_a)
#define fr_time_eq(_a, _b)
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)
#define fr_time_add(_a, _b)
Add a time/time delta together.
#define fr_time_gt(_a, _b)
#define fr_time_sub(_a, _b)
Subtract one time from another.
#define fr_time_neq(_a, _b)
A time delta, a difference in time measured in nanoseconds.
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
fr_time_t fr_timer_list_when(fr_timer_list_t *tl)
Return the time of the next event.
fr_timer_list_t * fr_timer_list_lst_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new lst based timer list.
static fr_event_list_t * el
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
#define fr_strerror_const(_msg)
static size_t char ** out