25 #define LOG_PREFIX conn->pub.name
28 #define _CONNECTION_PRIVATE 1
29 #include <freeradius-devel/server/connection.h>
31 #include <freeradius-devel/server/log.h>
32 #include <freeradius-devel/server/trigger.h>
34 #include <freeradius-devel/util/debug.h>
35 #include <freeradius-devel/util/event.h>
36 #include <freeradius-devel/util/talloc.h>
37 #include <freeradius-devel/util/syserror.h>
38 #include <freeradius-devel/util/log.h>
40 #ifdef HAVE_STDATOMIC_H
43 # include <freeradius-devel/util/stdatomic.h>
126 #define CONN_TRIGGER(_state) do { \
127 if (conn->pub.triggers) { \
128 trigger_exec(unlang_interpret_get_thread_default(), \
129 NULL, fr_table_str_by_value(connection_trigger_names, _state, "<INVALID>"), true, NULL); \
133 #define STATE_TRANSITION(_new) \
135 DEBUG2("Connection changed state %s -> %s", \
136 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"), \
137 fr_table_str_by_value(connection_states, _new, "<INVALID>")); \
138 conn->pub.prev = conn->pub.state; \
139 conn->pub.state = _new; \
140 CONN_TRIGGER(_new); \
143 #define BAD_STATE_TRANSITION(_new) \
145 if (!fr_cond_assert_msg(0, "Connection %" PRIu64 " invalid transition %s -> %s", \
147 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"), \
148 fr_table_str_by_value(connection_states, _new, "<INVALID>"))) return; \
151 #define DEFER_SIGNALS(_conn) ((_conn)->in_handler || (_conn)->signals_pause)
214 if (prev && (prev->
signal == signal))
return;
261 DEBUG4(
"Processing deferred signal - %s",
337 #define HANDLER_BEGIN(_conn, _func) \
338 void *_prev_handler = (_conn)->in_handler; \
340 (_conn)->in_handler = (void *)(_func); \
346 #define HANDLER_END(_conn) \
348 (_conn)->in_handler = _prev_handler; \
349 if (!(_conn)->signals_pause && (!(_conn)->in_handler)) connection_deferred_signal_process(_conn); \
366 while ((conn->next_watcher =
fr_dlist_next(list, conn->next_watcher))) {
383 entry->
func(conn, conn->pub.prev, conn->pub.state, entry->
uctx);
387 conn->next_watcher = NULL;
393 #define WATCH_PRE(_conn) \
395 if (fr_dlist_empty(&(_conn)->watch_pre[(_conn)->pub.state])) break; \
397 HANDLER_BEGIN(conn, &(_conn)->watch_pre[(_conn)->pub.state]); \
398 connection_watch_call((_conn), &(_conn)->watch_pre[(_conn)->pub.state]); \
406 #define WATCH_POST(_conn) \
408 if (fr_dlist_empty(&(_conn)->watch_post[(_conn)->pub.state])) break; \
410 HANDLER_BEGIN(conn, &(_conn)->watch_post[(_conn)->pub.state]); \
411 connection_watch_call((_conn), &(_conn)->watch_post[(_conn)->pub.state]); \
426 if (entry->
func == watch) {
750 PERROR(
"Failed setting connection_timeout timer, closing connection");
798 DEBUG4(
"Calling failed(h=%p, state=%s, uctx=%p)", conn->
pub.
h,
861 PERROR(
"Failed inserting reconnection_delay timer event, halting connection");
971 DEBUG2(
"Connection established");
980 PERROR(
"Connection failed");
1016 PERROR(
"Failed setting connection_timeout event, failing connection");
1073 DEBUG4(
"Calling init(h_out=%p, conn=%p, uctx=%p)", &conn->
pub.
h, conn, conn->
uctx);
1098 PERROR(
"Connection initialisation failed");
1109 DEBUG2(
"Signalled to start from %s state",
1141 DEBUG2(
"Signalled connected from %s state",
1169 DEBUG2(
"Signalled to reconnect from %s state",
1230 DEBUG2(
"Signalled to shutdown from %s state",
1293 DEBUG2(
"Signalled to halt from %s state",
1372 int fd = *(talloc_get_type_abort(
uctx,
int));
1418 PERROR(
"Failed inserting fd (%u) into event loop %p",
1429 MEM(fd_s = talloc_zero(conn,
int));
1515 char const *log_prefix,
1535 .reconnection_delay =
conf->reconnection_delay,
1536 .connection_timeout =
conf->connection_timeout,
1537 .init = funcs->
init,
1538 .open = funcs->
open,
1539 .close = funcs->
close,
1543 .pub.name = talloc_asprintf(conn,
"%s - [%" PRIu64
"]", log_prefix,
id)
#define L(_str)
Helper for initialising arrays of string literals.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
void(* connection_watch_t)(connection_t *conn, connection_state_t prev, connection_state_t state, void *uctx)
Receive a notification when a connection enters a particular state.
connection_state_t(* connection_failed_t)(void *h, connection_state_t state, void *uctx)
Notification that a connection attempt has failed.
uint64_t _CONST timed_out
How many times has this connection timed out when connecting.
void(* connection_close_t)(fr_event_list_t *el, void *h, void *uctx)
Notification that the connection has errored and must be closed.
fr_event_list_t *_CONST el
Event list for timers and I/O events.
connection_state_t(* connection_init_t)(void **h_out, connection_t *conn, void *uctx)
Callback for the initialise state.
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
@ CONNECTION_STATE_CLOSED
Connection has been closed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ CONNECTION_STATE_TIMEOUT
Timeout during CONNECTION_STATE_CONNECTING.
@ CONNECTION_STATE_INIT
Init state, sets up connection.
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
@ CONNECTION_STATE_SHUTDOWN
Connection is shutting down.
uint64_t _CONST reconnected
How many times we've attempted to establish or re-establish this connection.
void *_CONST h
Connection handle.
@ CONNECTION_EXPIRED
Connection is being reconnected because it's at the end of its life.
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
connection_state_t(* connection_open_t)(fr_event_list_t *el, void *h, void *uctx)
Notification that the connection is now open.
connection_state_t _CONST state
Current connection state.
connection_shutdown_t shutdown
connection_failed_t failed
uint64_t _CONST id
Unique identifier for the connection.
connection_state_t(* connection_shutdown_t)(fr_event_list_t *el, void *h, void *uctx)
Start the process of gracefully shutting down the connection.
Holds a complete set of functions for a connection.
Public fields for the connection.
fr_dcursor_eval_t void const * uctx
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
static void * fr_dlist_tail(fr_dlist_head_t const *list_head)
Return the TAIL item of a list or NULL if the list is empty.
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 int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Head of a doubly linked list.
Entry in a doubly linked list.
#define fr_event_fd_insert(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
#define fr_event_timer_in(...)
CC_NO_UBSAN(function)
(Re-)Initialises the libldap side of the connection handle
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
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.
Stores all information relating to an event list.
struct connection_watch_entry_s connection_watch_entry_t
An entry in a watch function list.
void connection_signal_shutdown(connection_t *conn)
Shuts down a connection gracefully.
fr_dlist_head_t watch_post[CONNECTION_STATE_MAX]
Function called after state callback.
void connection_watch_enable(connection_watch_entry_t *entry)
Enable a watcher.
static size_t connection_trigger_names_len
static int connection_del_watch(connection_t *conn, fr_dlist_head_t *state_lists, connection_state_t state, connection_watch_t watch)
Remove a watch function from a pre/post[state] list.
uint64_t connection_get_num_timed_out(connection_t const *conn)
Return the number of times this connection has timed out whilst connecting.
fr_dlist_t entry
List entry.
bool enabled
Whether the watch entry is enabled.
void * in_handler
Connection is currently in a callback.
static void _connection_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Connection timeout.
#define DEFER_SIGNALS(_conn)
static fr_table_num_ordered_t const connection_dsignals[]
static void _deferred_signal_connection_on_halted(UNUSED connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Notification function to tell connection_deferred_signal_process that the connection has been freed.
void connection_watch_disable(connection_watch_entry_t *entry)
Disable a watcher.
int connection_del_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch)
Remove a watch function from a post list.
connection_shutdown_t shutdown
Signal the connection handle to start shutting down.
static void _connection_signal_on_fd_cleanup(connection_t *conn, UNUSED connection_state_t prev, connection_state_t state, void *uctx)
Remove the FD we were watching for connection open/fail from the event loop.
fr_time_delta_t connection_timeout
How long to wait in the CONNECTION_STATE_CONNECTING state.
bool connection_watch_is_enabled(connection_watch_entry_t *entry)
Return the state of a watch entry.
static atomic_uint_fast64_t connection_counter
static size_t connection_dsignals_len
fr_dlist_head_t deferred_signals
A list of signals we received whilst we were in a handler.
static int _connection_free(connection_t *conn)
Close a connection if it's freed.
static void connection_state_enter_failed(connection_t *conn)
Connection failed.
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
#define WATCH_POST(_conn)
Call the post handler watch functions.
void connection_signals_resume(connection_t *conn)
Resume processing of deferred signals.
#define HANDLER_BEGIN(_conn, _func)
Called when we enter a handler.
connection_init_t init
Callback for initialising a connection.
fr_event_timer_t const * ev
State transition timer.
connection_close_t close
Callback to close a connection.
static void connection_state_enter_init(connection_t *conn)
Initial state of the connection.
uint64_t connection_get_num_reconnected(connection_t const *conn)
Return the number of times we've attempted to establish or re-establish this connection.
static void connection_state_enter_connecting(connection_t *conn)
Enter the connecting state.
connection_watch_entry_t * next_watcher
Hack to insulate watcher iterator from deletions.
connection_watch_t func
Function to call when a connection enters the state this list belongs to.
#define WATCH_PRE(_conn)
Call the pre handler watch functions.
static void connection_deferred_signal_process(connection_t *conn)
Process any deferred signals.
connection_dsignal_t signal
Signal that was deferred.
static void _connection_error(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, int fd_errno, void *uctx)
Receive an error notification when we're connecting a socket.
connection_open_t open
Callback for 'open' notification.
connection_failed_t failed
Callback for 'failed' notification.
struct connection_s connection_t
void connection_watch_set_uctx(connection_watch_entry_t *entry, void const *uctx)
Change the uctx of an entry.
fr_time_delta_t reconnection_delay
How long to wait in the CONNECTION_STATE_FAILED state.
static void connection_state_enter_closed(connection_t *conn)
Close the connection, then wait for another state change.
void * uctx
User data to pass to the function.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
connection_dsignal_t
Deferred signals.
@ CONNECTION_DSIGNAL_RECONNECT_FAILED
Reconnect a failed connection.
@ CONNECTION_DSIGNAL_HALT
Close a connection (ungracefully).
@ CONNECTION_DSIGNAL_INIT
Restart a halted connection.
@ CONNECTION_DSIGNAL_FREE
Free a connection (no further dsignals processed).
@ CONNECTION_DSIGNAL_SHUTDOWN
Close a connection (gracefully).
@ CONNECTION_DSIGNAL_CONNECTED
Signal that a connection is connected.
@ CONNECTION_DSIGNAL_RECONNECT_EXPIRED
Reconnect an expired connection (gracefully).
bool oneshot
Remove the function after it's called once.
fr_dlist_head_t watch_pre[CONNECTION_STATE_MAX]
Function called before state callback.
connection_watch_entry_t * connection_add_watch_pre(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed before a state function has been called.
fr_table_num_ordered_t const connection_states[]
#define HANDLER_END(_conn)
Called when we exit a handler.
int connection_signal_on_fd(connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
bool is_closed
The close callback has previously been called.
#define STATE_TRANSITION(_new)
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
static fr_table_num_indexed_t const connection_trigger_names[]
Map connection states to trigger names.
static void connection_state_enter_halted(connection_t *conn)
Enter the halted state.
static void connection_deferred_signal_add(connection_t *conn, connection_dsignal_t signal)
Add a deferred signal to the signal list.
connection_watch_entry_t * connection_add_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed after a state function has been called.
fr_dlist_t entry
Entry in the signals list.
static void _connection_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Receive a write notification after a socket is connected.
size_t connection_states_len
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
static void connection_state_enter_connected(connection_t *conn)
Enter the connected state.
int connection_del_watch_pre(connection_t *conn, connection_state_t state, connection_watch_t watch)
Remove a watch function from a pre list.
static connection_watch_entry_t * connection_add_watch(connection_t *conn, fr_dlist_head_t *list, connection_watch_t watch, bool oneshot, void const *uctx)
Add a watch entry to the pre/post[state] list.
connection_watch_entry_t * on_halted
Used by the deferred signal processor to learn if a function deeper in the call stack freed the conne...
static void _reconnect_delay_done(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
The requisite period of time has passed, try and re-open the connection.
#define BAD_STATE_TRANSITION(_new)
struct connection_pub_s pub
Public fields.
void connection_watch_enable_set_uctx(connection_watch_entry_t *entry, void const *uctx)
Enable a watcher and replace the uctx.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
static void connection_state_enter_shutdown(connection_t *conn)
Gracefully shutdown the handle.
void connection_signals_pause(connection_t *conn)
Pause processing of deferred signals.
bool processing_signals
Processing deferred signals, don't let the deferred signal processor be called multiple times.
unsigned int signals_pause
Temporarily stop processing of signals.
static void connection_state_enter_timeout(connection_t *conn)
Enter the timeout state.
Holds a signal from a handler until it's safe to process it.
An entry in a watch function list.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
#define atomic_fetch_add_explicit(object, operand, order)
#define ATOMIC_VAR_INIT(value)
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 table indexed by numeric value.
An element in an arbitrarily ordered array of name to num mappings.
#define fr_time_delta_ispos(_a)
A time delta, a difference in time measured in nanoseconds.
static fr_event_list_t * el
#define fr_box_time_delta(_val)