The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
connection.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 (at
5 * 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/**
18 * $Id: c44d83d4584d0d64d84daa5aadb70991a53cd64e $
19 *
20 * @file src/lib/server/connection.c
21 * @brief Simple state machine for managing connection states.
22 *
23 * @copyright 2017-2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25#define LOG_PREFIX conn->pub.name
26
28#define _CONNECTION_PRIVATE 1
29#include <freeradius-devel/server/connection.h>
30
31#include <freeradius-devel/server/log.h>
32#include <freeradius-devel/server/trigger.h>
33
34#include <freeradius-devel/util/debug.h>
35#include <freeradius-devel/util/syserror.h>
36
37#ifdef HAVE_STDATOMIC_H
38# include <stdatomic.h>
39# ifndef ATOMIC_VAR_INIT
40# define ATOMIC_VAR_INIT(_x) (_x)
41# endif
42#else
43# include <freeradius-devel/util/stdatomic.h>
44#endif
45
47 { L("HALTED"), CONNECTION_STATE_HALTED },
48 { L("INIT"), CONNECTION_STATE_INIT },
49 { L("CONNECTING"), CONNECTION_STATE_CONNECTING },
50 { L("TIMEOUT"), CONNECTION_STATE_TIMEOUT },
51 { L("CONNECTED"), CONNECTION_STATE_CONNECTED },
52 { L("SHUTDOWN"), CONNECTION_STATE_SHUTDOWN },
53 { L("FAILED"), CONNECTION_STATE_FAILED },
54 { L("CLOSED"), CONNECTION_STATE_CLOSED },
55};
57
58/** Map connection states to trigger names
59 *
60 */
62 [CONNECTION_STATE_HALTED] = { L("connection.halted"), CONNECTION_STATE_HALTED },
63 [CONNECTION_STATE_INIT] = { L("connection.init"), CONNECTION_STATE_INIT },
64 [CONNECTION_STATE_CONNECTING] = { L("connection.connecting"), CONNECTION_STATE_CONNECTING },
65 [CONNECTION_STATE_TIMEOUT] = { L("connection.timeout"), CONNECTION_STATE_TIMEOUT },
66 [CONNECTION_STATE_CONNECTED] = { L("connection.connected"), CONNECTION_STATE_CONNECTED },
67 [CONNECTION_STATE_SHUTDOWN] = { L("connection.shutdown"), CONNECTION_STATE_SHUTDOWN },
68 [CONNECTION_STATE_FAILED] = { L("connection.failed"), CONNECTION_STATE_FAILED },
69 [CONNECTION_STATE_CLOSED] = { L("connection.closed"), CONNECTION_STATE_CLOSED }
70};
72
73static atomic_uint_fast64_t connection_counter = ATOMIC_VAR_INIT(1);
74
75/** An entry in a watch function list
76 *
77 */
79 fr_dlist_t entry; //!< List entry.
80 connection_watch_t func; //!< Function to call when a connection enters
81 ///< the state this list belongs to
82 bool oneshot; //!< Remove the function after it's called once.
83 bool enabled; //!< Whether the watch entry is enabled.
84 void *uctx; //!< User data to pass to the function.
86
88 struct connection_pub_s pub; //!< Public fields
89
90 void *uctx; //!< User data.
91
92 void *in_handler; //!< Connection is currently in a callback.
93 bool processing_signals; //!< Processing deferred signals, don't let the deferred
94 ///< signal processor be called multiple times.
95
96 fr_dlist_head_t watch_pre[CONNECTION_STATE_MAX]; //!< Function called before state callback.
97 fr_dlist_head_t watch_post[CONNECTION_STATE_MAX]; //!< Function called after state callback.
98 connection_watch_entry_t *next_watcher; //!< Hack to insulate watcher iterator from deletions.
99
100 connection_init_t init; //!< Callback for initialising a connection.
101 connection_open_t open; //!< Callback for 'open' notification.
102 connection_close_t close; //!< Callback to close a connection.
103 connection_shutdown_t shutdown; //!< Signal the connection handle to start shutting down.
104 connection_failed_t failed; //!< Callback for 'failed' notification.
105
106 fr_timer_t *ev; //!< State transition timer.
107
108 fr_time_delta_t connection_timeout; //!< How long to wait in the
109 //!< #CONNECTION_STATE_CONNECTING state.
110 fr_time_delta_t reconnection_delay; //!< How long to wait in the
111 //!< #CONNECTION_STATE_FAILED state.
112
113 fr_dlist_head_t deferred_signals; //!< A list of signals we received whilst we were in
114 ///< a handler.
115
116
117
118 connection_watch_entry_t *on_halted; //!< Used by the deferred signal processor to learn
119 ///< if a function deeper in the call stack freed
120 ///< the connection.
121
122 unsigned int signals_pause; //!< Temporarily stop processing of signals.
123
124 CONF_SECTION *trigger_cs; //!< Where to search locally for triggers.
125 fr_pair_list_t *trigger_args; //!< Arguments to pass to the trigger functions.
126 bool triggers; //!< Do we run triggers.
127};
128
129#define CONN_TRIGGER(_state) do { \
130 if (conn->triggers) trigger(unlang_interpret_get_thread_default(), \
131 conn->trigger_cs, NULL, fr_table_str_by_value(connection_trigger_names, _state, "<INVALID>"), true, conn->trigger_args); \
132} while (0)
133
134#define STATE_TRANSITION(_new) \
135do { \
136 DEBUG2("Connection changed state %s -> %s", \
137 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"), \
138 fr_table_str_by_value(connection_states, _new, "<INVALID>")); \
139 conn->pub.prev = conn->pub.state; \
140 conn->pub.state = _new; \
141 CONN_TRIGGER(_new); \
142} while (0)
143
144#define BAD_STATE_TRANSITION(_new) \
145do { \
146 if (!fr_cond_assert_msg(0, "Connection %" PRIu64 " invalid transition %s -> %s", \
147 conn->pub.id, \
148 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"), \
149 fr_table_str_by_value(connection_states, _new, "<INVALID>"))) return; \
150} while (0)
151
152#define DEFER_SIGNALS(_conn) ((_conn)->in_handler || (_conn)->signals_pause)
153
154/** Deferred signals
155 *
156 */
157typedef enum {
158 CONNECTION_DSIGNAL_INIT, //!< Restart a halted connection.
159 CONNECTION_DSIGNAL_CONNECTED, //!< Signal that a connection is connected.
160 CONNECTION_DSIGNAL_RECONNECT_FAILED, //!< Reconnect a failed connection.
161 CONNECTION_DSIGNAL_RECONNECT_EXPIRED, //!< Reconnect an expired connection (gracefully).
162 CONNECTION_DSIGNAL_SHUTDOWN, //!< Close a connection (gracefully).
163 CONNECTION_DSIGNAL_HALT, //!< Close a connection (ungracefully).
164 CONNECTION_DSIGNAL_FREE //!< Free a connection (no further dsignals processed).
166
168 { L("INIT"), CONNECTION_DSIGNAL_INIT },
169 { L("CONNECTED"), CONNECTION_DSIGNAL_CONNECTED },
170 { L("RECONNECT-FAILED"), CONNECTION_DSIGNAL_RECONNECT_FAILED },
171 { L("RECONNECT-EXPIRED"), CONNECTION_DSIGNAL_RECONNECT_EXPIRED },
172 { L("SHUTDOWN"), CONNECTION_DSIGNAL_SHUTDOWN },
173 { L("HALT"), CONNECTION_DSIGNAL_HALT },
174 { L("FREE"), CONNECTION_DSIGNAL_FREE }
175};
177
178/** Holds a signal from a handler until it's safe to process it
179 *
180 */
181typedef struct {
182 fr_dlist_t entry; //!< Entry in the signals list.
183 connection_dsignal_t signal; //!< Signal that was deferred.
185
186/*
187 * State transition functions
188 */
197
198/** Add a deferred signal to the signal list
199 *
200 * Processing signals whilst in handlers usually leads to weird
201 * inconsistent states within the connection.
202 *
203 * If a public signal function is called, and detects its being called
204 * from within the handler, it instead adds a deferred signal entry
205 * and immediately returns.
206 *
207 * Once the handler is complete, and all pending C stack state changes
208 * are complete, the deferred signals are drained and processed.
209 */
211{
212 connection_dsignal_entry_t *dsignal, *prev;
213
214 /*
215 * We only suppresses consecutive duplicates at the
216 * tail. A sequence such as [INIT, HALT, INIT] will push
217 * the second INIT, because the tail is HALT. The signal
218 * handlers are generally idempotent (they check current
219 * state), so redundant signals are harmless. Avoiding
220 * this corner case involves doing more work in the
221 * common case, in order to avoid a small amount of work
222 * in the rare case.
223 */
224 prev = fr_dlist_tail(&conn->deferred_signals);
225 if (prev && (prev->signal == signal)) return; /* Don't insert duplicates */
226
227 MEM(dsignal = talloc_zero(conn, connection_dsignal_entry_t));
228 dsignal->signal = signal;
229 fr_dlist_insert_tail(&conn->deferred_signals, dsignal);
230
231// DEBUG4("Adding deferred signal - %s", fr_table_str_by_value(connection_dsignals, signal, "<INVALID>"));
232}
233
234/** Notification function to tell connection_deferred_signal_process that the connection has been freed
235 *
236 */
239 UNUSED connection_state_t state, void *uctx)
240{
241 bool *freed = uctx;
242 *freed = true;
243}
244
245/** Process any deferred signals
246 *
247 */
249{
251 bool freed = false;
252
253 /*
254 * We're inside and an instance of this function
255 * higher in the call stack. Don't do anything.
256 */
257 if (conn->processing_signals) return;
258
259 /*
260 * Get notified if the connection gets freed
261 * out from under us...
262 */
264 conn->processing_signals = true;
265
266 while ((dsignal = fr_dlist_head(&conn->deferred_signals))) {
268 fr_dlist_remove(&conn->deferred_signals, dsignal);
269 signal = dsignal->signal;
270 talloc_free(dsignal);
271
272 DEBUG4("Processing deferred signal - %s",
273 fr_table_str_by_value(connection_dsignals, signal, "<INVALID>"));
274
275 switch (signal) {
278 break;
279
282 break;
283
284 case CONNECTION_DSIGNAL_RECONNECT_FAILED: /* Reconnect - Failed */
286 break;
287
288 case CONNECTION_DSIGNAL_RECONNECT_EXPIRED: /* Reconnect - Expired */
290 break;
291
294 break;
295
298 break;
299
300 case CONNECTION_DSIGNAL_FREE: /* Freed */
301 talloc_free(conn);
302 return;
303 }
304
305 /*
306 * One of the signal handlers freed the connection,
307 * reset the processing signals and return.
308 */
309 if (freed) break;
310 }
311
312 conn->processing_signals = false;
314}
315
316/** Pause processing of deferred signals
317 *
318 * @param[in] conn to pause signal processing for.
319 */
321{
322 conn->signals_pause++;
323}
324
325/** Resume processing of deferred signals
326 *
327 * @param[in] conn to resume signal processing for.
328 */
330{
331 if (conn->signals_pause > 0) conn->signals_pause--;
332 if (conn->signals_pause > 0) return;
333
334 /*
335 * If we're not in a handler process the
336 * deferred signals now.
337 */
338 if (!conn->in_handler) {
340 return;
341 }
342}
343
344/** Called when we enter a handler
345 *
346 */
347#define HANDLER_BEGIN(_conn, _func) \
348void *_prev_handler = (_conn)->in_handler; \
349do { \
350 (_conn)->in_handler = (void *)(_func); \
351} while (0)
352
353/** Called when we exit a handler
354 *
355 */
356#define HANDLER_END(_conn) \
357do { \
358 (_conn)->in_handler = _prev_handler; \
359 if (!(_conn)->signals_pause && (!(_conn)->in_handler)) connection_deferred_signal_process(_conn); \
360} while(0)
361
362
363/** Call a list of watch functions associated with a state
364 *
365 */
366CC_NO_UBSAN(function) /* UBSAN: false positive - Public/private version of connection_t trips -fsanitize=function */
367static inline void connection_watch_call(connection_t *conn, fr_dlist_head_t *list)
368{
369 /*
370 * Nested watcher calls are not allowed
371 * and shouldn't be possible because of
372 * deferred signal processing.
373 */
374 fr_assert(conn->next_watcher == NULL);
375
376 while ((conn->next_watcher = fr_dlist_next(list, conn->next_watcher))) {
377 connection_watch_entry_t *entry = conn->next_watcher;
378 bool oneshot = entry->oneshot; /* Watcher could be freed, so store now */
379
380 if (!entry->enabled) continue;
381 if (oneshot) conn->next_watcher = fr_dlist_remove(list, entry);
382
383/*
384 DEBUG4("Notifying %swatcher - (%p)(conn=%p, prev=%s, state=%s, uctx=%p)",
385 entry->oneshot ? "oneshot " : "",
386 entry->func,
387 conn,
388 fr_table_str_by_value(connection_states, conn->pub.prev, "<INVALID>"),
389 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"),
390 entry->uctx);
391*/
392
393 entry->func(conn, conn->pub.prev, conn->pub.state, entry->uctx);
394
395 if (oneshot) talloc_free(entry);
396 }
397 conn->next_watcher = NULL;
398}
399
400/** Call the pre handler watch functions
401 *
402 */
403#define WATCH_PRE(_conn) \
404do { \
405 if (fr_dlist_empty(&(_conn)->watch_pre[(_conn)->pub.state])) break; \
406 { \
407 HANDLER_BEGIN(conn, &(_conn)->watch_pre[(_conn)->pub.state]); \
408 connection_watch_call((_conn), &(_conn)->watch_pre[(_conn)->pub.state]); \
409 HANDLER_END(conn); \
410 } \
411} while(0)
412
413/** Call the post handler watch functions
414 *
415 */
416#define WATCH_POST(_conn) \
417do { \
418 if (fr_dlist_empty(&(_conn)->watch_post[(_conn)->pub.state])) break; \
419 { \
420 HANDLER_BEGIN(conn, &(_conn)->watch_post[(_conn)->pub.state]); \
421 connection_watch_call((_conn), &(_conn)->watch_post[(_conn)->pub.state]); \
422 HANDLER_END(conn); \
423 } \
424} while(0)
425
426/** Remove a watch function from a pre/post[state] list
427 *
428 */
429static int connection_del_watch(connection_t *conn, fr_dlist_head_t *state_lists,
431{
432 connection_watch_entry_t *entry = NULL;
433 fr_dlist_head_t *list = &state_lists[state];
434
435 while ((entry = fr_dlist_next(list, entry))) {
436 if (entry->func == watch) {
437/*
438 DEBUG4("Removing %s watcher %p",
439 fr_table_str_by_value(connection_states, state, "<INVALID>"),
440 watch);
441*/
442 if (conn->next_watcher == entry) {
443 conn->next_watcher = fr_dlist_remove(list, entry);
444 } else {
445 fr_dlist_remove(list, entry);
446 }
447 talloc_free(entry);
448 return 0;
449 }
450 }
451
452 return -1;
453}
454
455/** Remove a watch function from a pre list
456 *
457 * @param[in] conn The connection to remove the watcher from.
458 * @param[in] state to remove the watch from.
459 * @param[in] watch Function to remove.
460 * @return
461 * - 0 if the function was removed successfully.
462 * - -1 if the function wasn't present in the watch list.
463 * - -2 an invalid state was passed.
464 */
466{
467 if (state >= CONNECTION_STATE_MAX) return -2;
468
469 return connection_del_watch(conn, conn->watch_pre, state, watch);
470}
471
472/** Remove a watch function from a post list
473 *
474 * @param[in] conn The connection to remove the watcher from.
475 * @param[in] state to remove the watch from.
476 * @param[in] watch Function to remove.
477 * @return
478 * - 0 if the function was removed successfully.
479 * - -1 if the function wasn't present in the watch list.
480 * - -2 an invalid state was passed.
481 */
483{
484 if (state >= CONNECTION_STATE_MAX) return -2;
485
486 return connection_del_watch(conn, conn->watch_post, state, watch);
487}
488
489/** Add a watch entry to the pre/post[state] list
490 *
491 */
493 connection_watch_t watch, bool oneshot, void const *uctx)
494{
496
497 MEM(entry = talloc_zero(conn, connection_watch_entry_t));
498
499 entry->func = watch;
500 entry->oneshot = oneshot;
501 entry->enabled = true;
502 memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
503
504 fr_dlist_insert_tail(list, entry);
505
506 return entry;
507}
508
509/** Add a callback to be executed before a state function has been called
510 *
511 * @param[in] conn to add watcher to.
512 * @param[in] state to call watcher on entering.
513 * @param[in] watch function to call.
514 * @param[in] oneshot If true, remove the function after calling.
515 * @param[in] uctx to pass to callbacks.
516 * @return
517 * - NULL if state value is invalid.
518 * - A new watch entry handle.
519 */
521 connection_watch_t watch, bool oneshot, void const *uctx)
522{
523 if (state >= CONNECTION_STATE_MAX) return NULL;
524
525 return connection_add_watch(conn, &conn->watch_pre[state], watch, oneshot, uctx);
526}
527
528/** Add a callback to be executed after a state function has been called
529 *
530 * Where a user callback is executed on state change, the post function
531 * is only called if the callback succeeds.
532 *
533 * @param[in] conn to add watcher to.
534 * @param[in] state to call watcher on entering.
535 * @param[in] watch function to call.
536 * @param[in] oneshot If true, remove the function after calling.
537 * @param[in] uctx to pass to callbacks.
538 * @return
539 * - NULL if state value is invalid.
540 * - A new watch entry handle.
541 */
543 connection_watch_t watch, bool oneshot, void const *uctx)
544{
545 if (state >= CONNECTION_STATE_MAX) return NULL;
546
547 return connection_add_watch(conn, &conn->watch_post[state], watch, oneshot, uctx);
548}
549
550/** Enable a watcher
551 *
552 * @param[in] entry to enabled.
553 */
555{
556 (void)talloc_get_type_abort(entry, connection_watch_entry_t);
557 entry->enabled = true;
558}
559
560/** Disable a watcher
561 *
562 * @param[in] entry to disable.
563 */
565{
566 (void)talloc_get_type_abort(entry, connection_watch_entry_t);
567 entry->enabled = false;
568}
569
570/** Enable a watcher and replace the uctx
571 *
572 * @param[in] entry to enabled.
573 * @param[in] uctx Opaque data to pass to the callback.
574 */
576{
577 (void)talloc_get_type_abort(entry, connection_watch_entry_t);
578 entry->enabled = true;
579 memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
580}
581
582/** Change the uctx of an entry
583 *
584 * @param[in] entry to enabled.
585 * @param[in] uctx Opaque data to pass to the callback.
586 */
588{
589 (void)talloc_get_type_abort(entry, connection_watch_entry_t);
590 memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
591}
592
593/** Return the state of a watch entry
594 *
595 * @param[in] entry to return state of.
596 * @return
597 * - true if enabled.
598 * - false if disabled.
599 */
601{
602 (void)talloc_get_type_abort(entry, connection_watch_entry_t);
603 return entry->enabled;
604}
605
606/** Return the number of times we've attempted to establish or re-establish this connection
607 *
608 * @param[in] conn to get count from.
609 * @return the number of times the connection has reconnected.
610 */
612{
613 if (conn->pub.reconnected == 0) return 0; /* Has never been initialised */
614
615 return conn->pub.reconnected - 1; /* We don't count the first connection attempt */
616}
617
618/** Return the number of times this connection has timed out whilst connecting
619 *
620 * @param[in] conn to get count from.
621 * @return the number of times the connection has timed out whilst connecting.
622 */
624{
625 return conn->pub.timed_out;
626}
627
628/** The requisite period of time has passed, try and re-open the connection
629 *
630 * @param[in] tl containing the timer event.
631 * @param[in] now The current time.
632 * @param[in] uctx The #connection_t the fd is associated with.
633 */
635{
636 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
637
638 switch (conn->pub.state) {
642 break;
643
644 default:
646 break;
647 }
648}
649
650/** Close the connection, then wait for another state change
651 *
652 */
654{
655 switch (conn->pub.state) {
661 break;
662
663 default:
665 return;
666 }
667
669
670 FR_TIMER_DISARM(conn->ev);
671
672 /*
673 * If there's a close callback, call it, so that the
674 * API client can free any resources associated
675 * with the connection handle.
676 */
677 WATCH_PRE(conn);
678
679 /*
680 * We can reach "is_closed" if a connection is halted,
681 * then signaled to INIT, which fails, and then sits in
682 * the FAILED state. Eventually the connection is
683 * shutdown, and enter_shutdown calls this function.
684 */
685 if (conn->close && !conn->pub.is_closed) {
686 HANDLER_BEGIN(conn, conn->close);
687 DEBUG4("Calling close(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
688 conn->close(conn->pub.el, conn->pub.h, conn->uctx);
689 conn->pub.is_closed = true; /* Ensure close doesn't get called twice if the connection is freed */
690 HANDLER_END(conn);
691
692 /*
693 * A deferred signal may have moved the connection to a
694 * different state. If so, that signal handler already
695 * took care of the transition.
696 */
697 if (conn->pub.state != CONNECTION_STATE_CLOSED) return;
698 } else {
699 conn->pub.is_closed = true;
700 }
701 WATCH_POST(conn);
702}
703
704/** Connection timeout
705 *
706 * Connection wasn't opened within the configured period of time
707 *
708 * @param[in] tl timer list the event belonged to.
709 * @param[in] now The current time.
710 * @param[in] uctx The #connection_t the fd is associated with.
711 */
713{
714 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
715
717}
718
719/** Gracefully shutdown the handle
720 *
721 */
723{
725
726 switch (conn->pub.state) {
728 break;
729
730 default:
732 return;
733 }
734
736
737 WATCH_PRE(conn);
738 if (conn->shutdown) {
739 HANDLER_BEGIN(conn, conn->shutdown);
740 DEBUG4("Calling shutdown(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
741 ret = conn->shutdown(conn->pub.el, conn->pub.h, conn->uctx);
742 HANDLER_END(conn);
743
744 /*
745 * A deferred signal may have moved the connection to a
746 * different state. If so, that signal handler already
747 * took care of the transition.
748 */
749 if (conn->pub.state != CONNECTION_STATE_SHUTDOWN) return;
750 }
751 switch (ret) {
753 break;
754
755 default:
757 return;
758 }
759 WATCH_POST(conn);
760
761 /*
762 * If a deferred signal changed the connection state, we're done.
763 */
764 if (conn->pub.state != CONNECTION_STATE_SHUTDOWN) return;
765
766 /*
767 * If there's a connection timeout,
768 * set, then add the timer.
769 *
770 * The connection may be bad, in which
771 * case we want to automatically fail
772 * if it doesn't shutdown within the
773 * timeout period.
774 */
776 if (fr_timer_in(conn, conn->pub.el->tl, &conn->ev,
777 conn->connection_timeout, false, _connection_timeout, conn) < 0) {
778 /*
779 * Can happen when the event loop is exiting
780 */
781 PERROR("Failed setting connection_timeout timer, closing connection");
783 }
784 }
785}
786
787/** Connection failed
788 *
789 * Transition to the CONNECTION_STATE_FAILED state.
790 *
791 * If the connection was open, or couldn't be opened wait for reconnection_delay before transitioning
792 * back to init.
793 *
794 * If no reconnection_delay was set, transition to halted.
795 *
796 * @param[in] conn that failed.
797 */
799{
802
804
805 /*
806 * Explicit error occurred, delete the connection timer
807 */
808 FR_TIMER_DISARM(conn->ev);
809
810 /*
811 * Record what state the connection is currently in
812 * so we can figure out what to do next.
813 */
814 prev = conn->pub.state;
815
816 /*
817 * Now transition to failed
818 */
820
821 /*
822 * If there's a failed callback, give it the
823 * opportunity to suspend/destroy the
824 * connection.
825 */
826 WATCH_PRE(conn);
827 if (conn->failed) {
828 HANDLER_BEGIN(conn, conn->failed);
829 DEBUG4("Calling failed(h=%p, state=%s, uctx=%p)", conn->pub.h,
830 fr_table_str_by_value(connection_states, prev, "<INVALID>"), conn->uctx);
831 ret = conn->failed(conn->pub.h, prev, conn->uctx);
832 HANDLER_END(conn);
833
834 /*
835 * A deferred signal may have moved the connection to a
836 * different state. If so, that signal handler already
837 * took care of the transition.
838 */
839 if (conn->pub.state != CONNECTION_STATE_FAILED) return;
840 }
841 WATCH_POST(conn);
842
843 /*
844 * Enter the closed state if we failed during
845 * connecting, or when we were connected.
846 */
847 switch (prev) {
850 case CONNECTION_STATE_TIMEOUT: /* Timeout means the connection progress past init */
851 case CONNECTION_STATE_SHUTDOWN: /* Shutdown means the connection failed whilst shutting down */
853 break;
854
855 default:
856 break;
857 }
858
859 if (conn->failed) {
860 switch (ret) {
861 /*
862 * The callback signalled it wants the
863 * connection to be reinitialised
864 * after reconnection_delay, or
865 * immediately if the failure was due
866 * to a connection timeout.
867 */
869 break;
870
871 /*
872 * The callback signalled it wants the
873 * connection to stop.
874 */
876 default:
878 return;
879 }
880 }
881
882 /*
883 * What previous state we were in
884 * determines if we need to apply the
885 * reconnect timeout.
886 */
887 switch (prev) {
888 case CONNECTION_STATE_INIT: /* Failed during initialisation */
889 case CONNECTION_STATE_CONNECTED: /* Failed after connecting */
890 case CONNECTION_STATE_CONNECTING: /* Failed during connecting */
891 case CONNECTION_STATE_SHUTDOWN: /* Failed during shutdown */
893 DEBUG2("Delaying reconnection by %pVs", fr_box_time_delta(conn->reconnection_delay));
894 if (fr_timer_in(conn, conn->pub.el->tl, &conn->ev,
895 conn->reconnection_delay, false, _reconnect_delay_done, conn) < 0) {
896 /*
897 * Can happen when the event loop is exiting
898 */
899 PERROR("Failed inserting reconnection_delay timer event, halting connection");
901 }
902 return;
903 }
904
905 /*
906 * If there's no reconnection
907 * delay, then don't automatically
908 * reconnect, and wait to be
909 * signalled.
910 */
912 break;
913
914 case CONNECTION_STATE_TIMEOUT: /* Failed during connecting due to timeout */
916 break;
917
918 default:
919 fr_assert(0);
920 }
921}
922
923/** Enter the timeout state
924 *
925 * The connection took took long to open. Timeout the attempt and transition
926 * to the failed state.
927 */
929{
930 switch (conn->pub.state) {
933 break;
934
935 default:
937 break;
938 }
939
940 ERROR("Connection failed - timed out after %pVs", fr_box_time_delta(conn->connection_timeout));
941
943
944 conn->pub.timed_out++;
945
947}
948
949/** Enter the halted state
950 *
951 * Here we wait, until signalled by connection_signal_reconnect.
952 */
954{
955 fr_assert(conn->pub.is_closed);
956
957 switch (conn->pub.state) {
959 case CONNECTION_STATE_FAILED: /* Init failure */
961 break;
962
963 default:
965 break;
966 }
967
968 FR_TIMER_DISARM(conn->ev);
969
971 WATCH_PRE(conn);
972 WATCH_POST(conn);
973}
974
975/** Enter the connected state
976 *
977 * The connection is now fully connected. At this point we call the open callback
978 * so that the API client can install its normal set of I/O callbacks to deal with
979 * sending/receiving actual data.
980 *
981 * After this, the connection will only transition states if an API client
982 * explicitly calls connection_signal_reconnect.
983 *
984 * The connection API cannot monitor the connection for failure conditions.
985 *
986 * @param[in] conn Entering the connecting state.
987 */
989{
990 int ret;
991
993
995
996 FR_TIMER_DISARM(conn->ev);
997 WATCH_PRE(conn);
998 if (conn->open) {
999 HANDLER_BEGIN(conn, conn->open);
1000 DEBUG4("Calling open(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
1001 ret = conn->open(conn->pub.el, conn->pub.h, conn->uctx);
1002 HANDLER_END(conn);
1003
1004 /*
1005 * A deferred signal may have moved the connection to a
1006 * different state. If so, that signal handler already
1007 * took care of the transition.
1008 */
1009 if (conn->pub.state != CONNECTION_STATE_CONNECTED) return;
1010 } else {
1012 }
1013
1014 switch (ret) {
1015 /*
1016 * Callback agrees everything is connected
1017 */
1019 DEBUG2("Connection established");
1020 WATCH_POST(conn); /* Only call if we successfully connected */
1021 return;
1022
1023 /*
1024 * Open callback failed
1025 */
1027 default:
1028 PERROR("Connection failed");
1030 return;
1031 }
1032}
1033
1034/** Enter the connecting state
1035 *
1036 * After this function returns we wait to be signalled with connection_singal_connected
1037 * or for the connection timer to expire.
1038 *
1039 * @param[in] conn Entering the connecting state.
1040 */
1042{
1043 switch (conn->pub.state) {
1045 break;
1046
1047 default:
1049 return;
1050 }
1051
1053
1054 WATCH_PRE(conn);
1055 WATCH_POST(conn);
1056
1057 /*
1058 * If there's a connection timeout,
1059 * set, then add the timer.
1060 */
1062 if (fr_timer_in(conn, conn->pub.el->tl, &conn->ev,
1063 conn->connection_timeout, false, _connection_timeout, conn) < 0) {
1064 PERROR("Failed setting connection_timeout event, failing connection");
1065
1066 /*
1067 * This can happen when the event loop
1068 * is exiting.
1069 *
1070 * Entering fail will close partially
1071 * open connection and then, if we still
1072 * can't insert a timer, then the connection
1073 * will be halted and sit idle until its
1074 * freed.
1075 */
1077 }
1078 }
1079}
1080
1081/** Initial state of the connection
1082 *
1083 * Calls the init function we were passed to allocate a library specific handle or
1084 * file descriptor.
1085 *
1086 * @param[in] conn To initialise.
1087 */
1089{
1091
1092 switch (conn->pub.state) {
1096 break;
1097
1098 default:
1100 return;
1101 }
1102
1103 /*
1104 * Increment every time we enter
1105 * We have to do this, as we don't know
1106 * whether the connection was halted by
1107 * the failed callback, and is now being
1108 * reconnected, or was automatically
1109 * reconnected.
1110 */
1111 conn->pub.reconnected++;
1112
1114
1115 /*
1116 * If we have an init callback, call it.
1117 */
1118 WATCH_PRE(conn);
1119 if (conn->init) {
1120 HANDLER_BEGIN(conn, conn->init);
1121 DEBUG4("Calling init(h_out=%p, conn=%p, uctx=%p)", &conn->pub.h, conn, conn->uctx);
1122 ret = conn->init(&conn->pub.h, conn, conn->uctx);
1123 HANDLER_END(conn);
1124
1125 /*
1126 * A deferred signal may have moved the connection to a
1127 * different state. If so, that signal handler already
1128 * took care of the transition.
1129 */
1130 if (conn->pub.state != CONNECTION_STATE_INIT) return;
1131 } else {
1133 }
1134
1135 switch (ret) {
1137 conn->pub.is_closed = false; /* We now have a handle */
1138 WATCH_POST(conn); /* Only call if we successfully initialised the handle */
1140 return;
1141
1143 conn->pub.is_closed = false; /* We now have a handle */
1144 WATCH_POST(conn); /* Only call if we successfully initialised the handle */
1146 return;
1147
1148 /*
1149 * Initialisation callback failed
1150 */
1152 default:
1153 PERROR("Connection initialisation failed");
1155 break;
1156 }
1157}
1158
1159/** Asynchronously signal a halted connection to start
1160 *
1161 */
1163{
1164 DEBUG2("Signalled to start from %s state",
1165 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1166
1167 if (DEFER_SIGNALS(conn)) {
1169 return;
1170 }
1171
1172 switch (conn->pub.state) {
1175 break;
1176
1177 default:
1178 break;
1179 }
1180}
1181
1182/** Asynchronously signal that the connection is open
1183 *
1184 * Some libraries like libldap are extremely annoying and only return control
1185 * to the caller after a connection is open.
1186 *
1187 * For these libraries, we can't use an I/O handler to determine when the
1188 * connection is open so we rely on callbacks built into the library to
1189 * signal that the transition has occurred.
1190 *
1191 */
1193{
1194 fr_assert(!conn->open); /* Use one or the other not both! */
1195
1196 DEBUG2("Signalled connected from %s state",
1197 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1198
1199 if (DEFER_SIGNALS(conn)) {
1201 return;
1202 }
1203
1204 switch (conn->pub.state) {
1207 break;
1208
1209 default:
1210 break;
1211 }
1212}
1213
1214/** Asynchronously signal the connection should be reconnected
1215 *
1216 * Should be called if the caller has knowledge that the connection is bad
1217 * and should be reconnected.
1218 *
1219 * @param[in] conn to reconnect.
1220 * @param[in] reason Why the connection was signalled to reconnect.
1221 */
1223{
1224 DEBUG2("Signalled to reconnect from %s state",
1225 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1226
1227 if (DEFER_SIGNALS(conn)) {
1228 if ((reason == CONNECTION_EXPIRED) && conn->shutdown) {
1230 return;
1231 }
1232
1234 return;
1235 }
1236
1237 switch (conn->pub.state) {
1238 case CONNECTION_STATE_CLOSED: /* Don't circumvent reconnection_delay */
1239 case CONNECTION_STATE_INIT: /* Already initialising */
1240 break;
1241
1244 break;
1245
1247 if (reason == CONNECTION_EXPIRED) break; /* Already shutting down */
1249 break;
1250
1252 if (reason == CONNECTION_EXPIRED) {
1253 if (conn->shutdown) {
1255 break;
1256 }
1258 break;
1259 }
1261
1265 break;
1266
1267 case CONNECTION_STATE_FAILED: /* already entered failed, don't re-enter */
1268 break;
1269
1271 fr_assert(0);
1272 return;
1273 }
1274}
1275
1276/** Shuts down a connection gracefully
1277 *
1278 * If a shutdown function has been provided, it is called.
1279 * It's then up to the shutdown function to install I/O handlers to signal
1280 * when the connection has finished shutting down and should be closed
1281 * via #connection_signal_halt.
1282 *
1283 * @param[in] conn to shutdown.
1284 */
1286{
1287 DEBUG2("Signalled to shutdown from %s state",
1288 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1289
1290 if (DEFER_SIGNALS(conn)) {
1292 return;
1293 }
1294
1295 switch (conn->pub.state) {
1298 break;
1299
1302 break;
1303
1304 /*
1305 * If the connection is connected it needs to be
1306 * shutdown first.
1307 *
1308 * The shutdown callback or an FD event it inserts then
1309 * to signal that the connection should be closed.
1310 */
1312 if (conn->shutdown) {
1314 break;
1315 }
1317
1318 /*
1319 * If the connection is any of these states it
1320 * must have completed INIT which means it has
1321 * an active handle which needs to be closed before
1322 * the connection is halted.
1323 */
1328 fr_assert(conn->pub.is_closed);
1329
1333 break;
1334
1336 fr_assert(0);
1337 return;
1338 }
1339}
1340
1341/** Shuts down a connection ungracefully
1342 *
1343 * If a connection is in an open or connection state it will be closed immediately.
1344 * Otherwise the connection will transition directly to the halted state.
1345 *
1346 * @param[in] conn to halt.
1347 */
1349{
1350 DEBUG2("Signalled to halt from %s state",
1351 fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1352
1353 if (DEFER_SIGNALS(conn)) {
1355 return;
1356 }
1357
1358 switch (conn->pub.state) {
1360 break;
1361
1365 break;
1366
1367 /*
1368 * If the connection is any of these states it
1369 * must have completed INIT which means it has
1370 * an active handle which needs to be closed before
1371 * the connection is halted.
1372 *
1373 * The exception is when a connection fails to open
1374 * so goes from INIT -> FAILED, means is_closed
1375 * is true, as the connection has never opened.
1376 */
1383 fr_assert(conn->pub.is_closed);
1385 break;
1386
1388 fr_assert(0);
1389 return;
1390 }
1391}
1392/** Receive an error notification when we're connecting a socket
1393 *
1394 * @param[in] el event list the I/O event occurred on.
1395 * @param[in] fd the I/O event occurred for.
1396 * @param[in] flags from kevent.
1397 * @param[in] fd_errno from kevent.
1398 * @param[in] uctx The #connection_t this fd is associated with.
1399 */
1400static void _connection_error(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, int fd_errno, void *uctx)
1401{
1402 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
1403
1404 ERROR("Connection failed for fd (%d): %s", fd, fr_syserror(fd_errno));
1406}
1407
1408/** Receive a write notification after a socket is connected
1409 *
1410 * @param[in] el event list the I/O event occurred on.
1411 * @param[in] fd the I/O event occurred for.
1412 * @param[in] flags from kevent.
1413 * @param[in] uctx The #connection_t this fd is associated with.
1414 */
1415static void _connection_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1416{
1417 connection_t *conn = talloc_get_type_abort(uctx, connection_t);
1418
1421}
1422
1423/** Remove the FD we were watching for connection open/fail from the event loop
1424 *
1425 */
1427 UNUSED connection_state_t prev, connection_state_t state, void *uctx)
1428{
1429 int fd = *(talloc_get_type_abort(uctx, int));
1430
1431 /*
1432 * Two states can trigger a cleanup
1433 * Remove the watch on the one that didn't
1434 */
1435 switch (state) {
1438 break;
1439
1442 break;
1443
1444 default:
1445 fr_assert(0);
1446 break;
1447 }
1448
1450 talloc_free(uctx);
1451}
1452
1453/** Setup the connection to change states to connected or failed based on I/O events
1454 *
1455 * Will automatically cleanup after itself, in preparation for
1456 * new I/O handlers to be installed in the open() callback.
1457 *
1458 * @return
1459 * - 0 on success.
1460 * - -1 on failure.
1461 */
1463{
1464 int *fd_s;
1465
1466 /*
1467 * If connection becomes writable we
1468 * assume it's open.
1469 */
1470 if (fr_event_fd_insert(conn, NULL, conn->pub.el, fd,
1471 NULL,
1474 conn) < 0) {
1475 PERROR("Failed inserting fd (%d) into event loop %p",
1476 fd, conn->pub.el);
1478 return -1;
1479 }
1480
1481 /*
1482 * Stop the static analysis tools
1483 * complaining about assigning ints
1484 * to pointers.
1485 */
1486 MEM(fd_s = talloc_zero(conn, int));
1487 *fd_s = fd;
1488
1489 /*
1490 * Add a oneshot watcher to remove
1491 * the I/O handlers if the connection
1492 * fails, or is connected.
1493 */
1498 return 0;
1499}
1500
1501/** Close a connection if it's freed
1502 *
1503 * @param[in] conn to free.
1504 * @return
1505 * - 0 connection was freed immediately.
1506 * - 1 connection free was deferred.
1507 */
1509{
1510 /*
1511 * Explicitly cancel any pending events
1512 */
1513 FR_TIMER_DELETE_RETURN(&conn->ev);
1514 /*
1515 * Don't allow the connection to be
1516 * arbitrarily freed by a callback.
1517 *
1518 * Add a deferred signal to free the
1519 * connection later.
1520 */
1521 if (DEFER_SIGNALS(conn)) {
1523 return -1;
1524 }
1525
1526 switch (conn->pub.state) {
1528 break;
1529
1530 /*
1531 * Need to close the connection first
1532 */
1539
1540 default:
1542 break;
1543 }
1544 return 0;
1545}
1546
1547/** Allocate a new connection
1548 *
1549 * After the connection has been allocated, it should be started with a call to #connection_signal_init.
1550 *
1551 * The connection state machine can detect when the connection is open in one of two ways.
1552 * - You can install a generic socket open/fail callback, using connection_signal_on_fd.
1553 * - You can call either #connection_signal_connected or connection_signal_recommend.
1554 * This allows the connection state machine to work with more difficult library APIs,
1555 * which may not return control to the caller as connections are opened.
1556 *
1557 * @param[in] ctx to allocate connection handle in. If the connection
1558 * handle is freed, and the #connection_state_t is
1559 * #CONNECTION_STATE_CONNECTING or #CONNECTION_STATE_CONNECTED the
1560 * close callback will be called.
1561 * @param[in] el to use for timer events, and to pass to the #connection_open_t callback.
1562 * @param[in] funcs callback functions.
1563 * @param[in] conf our configuration.
1564 * @param[in] log_prefix To prepend to log messages.
1565 * @param[in] uctx User context to pass to callbacks.
1566 * @return
1567 * - A new #connection_t on success.
1568 * - NULL on failure.
1569 */
1571 connection_funcs_t const *funcs,
1572 connection_conf_t const *conf,
1573 char const *log_prefix,
1574 void const *uctx)
1575{
1576 size_t i;
1577 connection_t *conn;
1578 uint64_t id;
1579
1580 fr_assert_msg(el, "No event list provided");
1581
1582 MEM(conn = talloc(ctx, connection_t));
1583 talloc_set_destructor(conn, _connection_free);
1584
1586
1587 *conn = (connection_t){
1588 .pub = {
1589 .id = id,
1590 .state = CONNECTION_STATE_HALTED,
1591 .el = el,
1592 .is_closed = true /* Starts closed */
1593 },
1594 .reconnection_delay = conf->reconnection_delay,
1595 .connection_timeout = conf->connection_timeout,
1596 .init = funcs->init,
1597 .open = funcs->open,
1598 .close = funcs->close,
1599 .failed = funcs->failed,
1600 .shutdown = funcs->shutdown,
1601 .triggers = conf->triggers,
1602 .trigger_args = conf->trigger_args,
1603 .trigger_cs = conf->trigger_cs,
1604 .pub.name = talloc_asprintf(conn, "%s - [%" PRIu64 "]", log_prefix, id)
1605 };
1606 memcpy(&conn->uctx, &uctx, sizeof(conn->uctx));
1607
1608 for (i = 0; i < NUM_ELEMENTS(conn->watch_pre); i++) {
1610 }
1611 for (i = 0; i < NUM_ELEMENTS(conn->watch_post); i++) {
1613 }
1615
1616 /*
1617 * Pre-allocate a on_halt watcher for deferred signal processing
1618 *
1619 * Note that we do NOT set "oneshot". This lets the watcher remain valid after the oneshot
1620 * watcher fires. Otherwise the connection is freed out from under the watcher.
1621 */
1624 connection_watch_disable(conn->on_halted); /* Start disabled */
1625
1626 return conn;
1627}
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define CC_NO_UBSAN(_sanitize)
Definition build.h:503
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
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.
Definition connection.h:217
connection_state_t(* connection_failed_t)(void *h, connection_state_t state, void *uctx)
Notification that a connection attempt has failed.
Definition connection.h:180
uint64_t _CONST timed_out
How many times has this connection timed out when connecting.
Definition connection.h:84
void(* connection_close_t)(fr_event_list_t *el, void *h, void *uctx)
Notification that the connection has errored and must be closed.
Definition connection.h:194
fr_event_list_t *_CONST el
Event list for timers and I/O events.
Definition connection.h:76
connection_state_t(* connection_init_t)(void **h_out, connection_t *conn, void *uctx)
Callback for the initialise state.
Definition connection.h:129
connection_state_t
Definition connection.h:47
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:56
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
Definition connection.h:48
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:57
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:54
@ CONNECTION_STATE_TIMEOUT
Timeout during CONNECTION_STATE_CONNECTING.
Definition connection.h:53
@ CONNECTION_STATE_INIT
Init state, sets up connection.
Definition connection.h:51
@ CONNECTION_STATE_MAX
Definition connection.h:58
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:52
@ CONNECTION_STATE_SHUTDOWN
Connection is shutting down.
Definition connection.h:55
bool _CONST is_closed
The close callback has previously been called, so the handle is gone.
Definition connection.h:78
uint64_t _CONST reconnected
How many times we've attempted to establish or re-establish this connection.
Definition connection.h:82
void *_CONST h
Connection handle.
Definition connection.h:75
connection_reason_t
Definition connection.h:88
@ CONNECTION_EXPIRED
Connection is being reconnected because it's at the end of its life.
Definition connection.h:90
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:89
connection_state_t(* connection_open_t)(fr_event_list_t *el, void *h, void *uctx)
Notification that the connection is now open.
Definition connection.h:143
connection_state_t _CONST state
Current connection state.
Definition connection.h:72
connection_init_t init
Definition connection.h:200
connection_shutdown_t shutdown
Definition connection.h:202
connection_failed_t failed
Definition connection.h:203
connection_open_t open
Definition connection.h:201
uint64_t _CONST id
Unique identifier for the connection.
Definition connection.h:74
connection_close_t close
Definition connection.h:204
connection_state_t(* connection_shutdown_t)(fr_event_list_t *el, void *h, void *uctx)
Start the process of gracefully shutting down the connection.
Definition connection.h:162
Holds a complete set of functions for a connection.
Definition connection.h:199
Public fields for the connection.
Definition connection.h:69
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:202
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
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
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
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.
Definition dlist.h:513
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
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
talloc_free(hp)
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG4(_fmt,...)
Definition log.h:272
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
Stores all information relating to an event list.
Definition event.c:377
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
static rs_t * conf
Definition radsniff.c:52
static void connection_watch_call(connection_t *conn, fr_dlist_head_t *list)
Call a list of watch functions associated with a state.
Definition connection.c:367
CONF_SECTION * trigger_cs
Where to search locally for triggers.
Definition connection.c:124
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.
Definition connection.c:97
void connection_watch_enable(connection_watch_entry_t *entry)
Enable a watcher.
Definition connection.c:554
static size_t connection_trigger_names_len
Definition connection.c:71
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.
Definition connection.c:429
uint64_t connection_get_num_timed_out(connection_t const *conn)
Return the number of times this connection has timed out whilst connecting.
Definition connection.c:623
fr_dlist_t entry
List entry.
Definition connection.c:79
bool enabled
Whether the watch entry is enabled.
Definition connection.c:83
void * in_handler
Connection is currently in a callback.
Definition connection.c:92
#define DEFER_SIGNALS(_conn)
Definition connection.c:152
static fr_table_num_ordered_t const connection_dsignals[]
Definition connection.c:167
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.
Definition connection.c:237
void connection_watch_disable(connection_watch_entry_t *entry)
Disable a watcher.
Definition connection.c:564
fr_pair_list_t * trigger_args
Arguments to pass to the trigger functions.
Definition connection.c:125
int connection_del_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch)
Remove a watch function from a post list.
Definition connection.c:482
connection_shutdown_t shutdown
Signal the connection handle to start shutting down.
Definition connection.c:103
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.
Definition connection.c:108
static void _connection_timeout(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Connection timeout.
Definition connection.c:712
bool connection_watch_is_enabled(connection_watch_entry_t *entry)
Return the state of a watch entry.
Definition connection.c:600
static atomic_uint_fast64_t connection_counter
Definition connection.c:73
static size_t connection_dsignals_len
Definition connection.c:176
void * uctx
User data.
Definition connection.c:90
fr_dlist_head_t deferred_signals
A list of signals we received whilst we were in a handler.
Definition connection.c:113
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.
Definition connection.c:798
static void _reconnect_delay_done(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
The requisite period of time has passed, try and re-open the connection.
Definition connection.c:634
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
#define WATCH_POST(_conn)
Call the post handler watch functions.
Definition connection.c:416
void connection_signals_resume(connection_t *conn)
Resume processing of deferred signals.
Definition connection.c:329
#define HANDLER_BEGIN(_conn, _func)
Called when we enter a handler.
Definition connection.c:347
connection_init_t init
Callback for initialising a connection.
Definition connection.c:100
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.
Definition connection.c:492
connection_close_t close
Callback to close a connection.
Definition connection.c:102
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.
Definition connection.c:611
bool triggers
Do we run triggers.
Definition connection.c:126
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.
Definition connection.c:98
connection_watch_t func
Function to call when a connection enters the state this list belongs to.
Definition connection.c:80
#define WATCH_PRE(_conn)
Call the pre handler watch functions.
Definition connection.c:403
static void connection_deferred_signal_process(connection_t *conn)
Process any deferred signals.
Definition connection.c:248
connection_dsignal_t signal
Signal that was deferred.
Definition connection.c:183
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.
Definition connection.c:101
connection_failed_t failed
Callback for 'failed' notification.
Definition connection.c:104
struct connection_s connection_t
Definition connection.c:27
void connection_watch_set_uctx(connection_watch_entry_t *entry, void const *uctx)
Change the uctx of an entry.
Definition connection.c:587
fr_time_delta_t reconnection_delay
How long to wait in the CONNECTION_STATE_FAILED state.
Definition connection.c:110
static void connection_state_enter_closed(connection_t *conn)
Close the connection, then wait for another state change.
Definition connection.c:653
void * uctx
User data to pass to the function.
Definition connection.c:84
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
connection_dsignal_t
Deferred signals.
Definition connection.c:157
@ CONNECTION_DSIGNAL_RECONNECT_FAILED
Reconnect a failed connection.
Definition connection.c:160
@ CONNECTION_DSIGNAL_HALT
Close a connection (ungracefully).
Definition connection.c:163
@ CONNECTION_DSIGNAL_INIT
Restart a halted connection.
Definition connection.c:158
@ CONNECTION_DSIGNAL_FREE
Free a connection (no further dsignals processed).
Definition connection.c:164
@ CONNECTION_DSIGNAL_SHUTDOWN
Close a connection (gracefully).
Definition connection.c:162
@ CONNECTION_DSIGNAL_CONNECTED
Signal that a connection is connected.
Definition connection.c:159
@ CONNECTION_DSIGNAL_RECONNECT_EXPIRED
Reconnect an expired connection (gracefully).
Definition connection.c:161
bool oneshot
Remove the function after it's called once.
Definition connection.c:82
fr_dlist_head_t watch_pre[CONNECTION_STATE_MAX]
Function called before state callback.
Definition connection.c:96
fr_table_num_ordered_t const connection_states[]
Definition connection.c:46
#define HANDLER_END(_conn)
Called when we exit a handler.
Definition connection.c:356
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.
#define STATE_TRANSITION(_new)
Definition connection.c:134
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.
Definition connection.c:61
static void connection_state_enter_halted(connection_t *conn)
Enter the halted state.
Definition connection.c:953
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_deferred_signal_add(connection_t *conn, connection_dsignal_t signal)
Add a deferred signal to the signal list.
Definition connection.c:210
fr_dlist_t entry
Entry in the signals list.
Definition connection.c:182
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.
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.
Definition connection.c:520
size_t connection_states_len
Definition connection.c:56
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.
Definition connection.c:542
static void connection_state_enter_connected(connection_t *conn)
Enter the connected state.
Definition connection.c:988
int connection_del_watch_pre(connection_t *conn, connection_state_t state, connection_watch_t watch)
Remove a watch function from a pre list.
Definition connection.c:465
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...
Definition connection.c:118
#define BAD_STATE_TRANSITION(_new)
Definition connection.c:144
struct connection_pub_s pub
Public fields.
Definition connection.c:88
void connection_watch_enable_set_uctx(connection_watch_entry_t *entry, void const *uctx)
Enable a watcher and replace the uctx.
Definition connection.c:575
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.
Definition connection.c:722
void connection_signals_pause(connection_t *conn)
Pause processing of deferred signals.
Definition connection.c:320
bool processing_signals
Processing deferred signals, don't let the deferred signal processor be called multiple times.
Definition connection.c:93
unsigned int signals_pause
Temporarily stop processing of signals.
Definition connection.c:122
fr_timer_t * ev
State transition timer.
Definition connection.c:106
static void connection_state_enter_timeout(connection_t *conn)
Enter the timeout state.
Definition connection.c:928
Holds a signal from a handler until it's safe to process it.
Definition connection.c:181
An entry in a watch function list.
Definition connection.c:78
@ memory_order_relaxed
Definition stdatomic.h:127
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:302
#define ATOMIC_VAR_INIT(value)
Definition stdatomic.h:88
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 table indexed by numeric value.
Definition table.h:92
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
#define talloc_asprintf
Definition talloc.h:151
#define fr_time_delta_ispos(_a)
Definition time.h:290
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define FR_TIMER_DELETE_RETURN(_ev_p)
Definition timer.h:110
#define fr_timer_in(...)
Definition timer.h:87
#define FR_TIMER_DISARM(_ev)
Definition timer.h:91
static fr_event_list_t * el
#define fr_box_time_delta(_val)
Definition value.h:366