The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
connection.c
Go to the documentation of this file.
1 /*
2  * This program is 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: c6ef11e01cf21789727f1356a646972649bff1f3 $
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 
27 typedef struct connection_s connection_t;
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/event.h>
36 #include <freeradius-devel/util/talloc.h>
37 #include <freeradius-devel/util/syserror.h>
38 #include <freeradius-devel/util/log.h>
39 
40 #ifdef HAVE_STDATOMIC_H
41 # include <stdatomic.h>
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 
73 static atomic_uint_fast64_t connection_counter = ATOMIC_VAR_INIT(1);
74 
75 /** An entry in a watch function list
76  *
77  */
78 typedef struct connection_watch_entry_s {
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 
87 struct connection_s {
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 is_closed; //!< The close callback has previously been called.
94  bool processing_signals; //!< Processing deferred signals, don't let the deferred
95  ///< signal processor be called multiple times.
96 
97  fr_dlist_head_t watch_pre[CONNECTION_STATE_MAX]; //!< Function called before state callback.
98  fr_dlist_head_t watch_post[CONNECTION_STATE_MAX]; //!< Function called after state callback.
99  connection_watch_entry_t *next_watcher; //!< Hack to insulate watcher iterator from deletions.
100 
101  connection_init_t init; //!< Callback for initialising a connection.
102  connection_open_t open; //!< Callback for 'open' notification.
103  connection_close_t close; //!< Callback to close a connection.
104  connection_shutdown_t shutdown; //!< Signal the connection handle to start shutting down.
105  connection_failed_t failed; //!< Callback for 'failed' notification.
106 
107  fr_event_timer_t const *ev; //!< State transition timer.
108 
109  fr_time_delta_t connection_timeout; //!< How long to wait in the
110  //!< #CONNECTION_STATE_CONNECTING state.
111  fr_time_delta_t reconnection_delay; //!< How long to wait in the
112  //!< #CONNECTION_STATE_FAILED state.
113 
114  fr_dlist_head_t deferred_signals; //!< A list of signals we received whilst we were in
115  ///< a handler.
116 
117 
118 
119  connection_watch_entry_t *on_halted; //!< Used by the deferred signal processor to learn
120  ///< if a function deeper in the call stack freed
121  ///< the connection.
122 
123  unsigned int signals_pause; //!< Temporarily stop processing of signals.
124 };
125 
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); \
130  } \
131 } while (0)
132 
133 #define STATE_TRANSITION(_new) \
134 do { \
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); \
141 } while (0)
142 
143 #define BAD_STATE_TRANSITION(_new) \
144 do { \
145  if (!fr_cond_assert_msg(0, "Connection %" PRIu64 " invalid transition %s -> %s", \
146  conn->pub.id, \
147  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"), \
148  fr_table_str_by_value(connection_states, _new, "<INVALID>"))) return; \
149 } while (0)
150 
151 #define DEFER_SIGNALS(_conn) ((_conn)->in_handler || (_conn)->signals_pause)
152 
153 /** Deferred signals
154  *
155  */
156 typedef enum {
157  CONNECTION_DSIGNAL_INIT, //!< Restart a halted connection.
158  CONNECTION_DSIGNAL_CONNECTED, //!< Signal that a connection is connected.
159  CONNECTION_DSIGNAL_RECONNECT_FAILED, //!< Reconnect a failed connection.
160  CONNECTION_DSIGNAL_RECONNECT_EXPIRED, //!< Reconnect an expired connection (gracefully).
161  CONNECTION_DSIGNAL_SHUTDOWN, //!< Close a connection (gracefully).
162  CONNECTION_DSIGNAL_HALT, //!< Close a connection (ungracefully).
163  CONNECTION_DSIGNAL_FREE //!< Free a connection (no further dsignals processed).
165 
167  { L("INIT"), CONNECTION_DSIGNAL_INIT },
168  { L("CONNECTED"), CONNECTION_DSIGNAL_CONNECTED },
169  { L("RECONNECT-FAILED"), CONNECTION_DSIGNAL_RECONNECT_FAILED },
170  { L("RECONNECT-EXPIRED"), CONNECTION_DSIGNAL_RECONNECT_EXPIRED },
171  { L("SHUTDOWN"), CONNECTION_DSIGNAL_SHUTDOWN },
172  { L("HALT"), CONNECTION_DSIGNAL_HALT },
173  { L("FREE"), CONNECTION_DSIGNAL_FREE }
174 };
176 
177 /** Holds a signal from a handler until it's safe to process it
178  *
179  */
180 typedef struct {
181  fr_dlist_t entry; //!< Entry in the signals list.
182  connection_dsignal_t signal; //!< Signal that was deferred.
184 
185 /*
186  * State transition functions
187  */
195 static void connection_state_enter_init(connection_t *conn);
196 
197 /** Add a deferred signal to the signal list
198  *
199  * Processing signals whilst in handlers usually leads to weird
200  * inconsistent states within the connection.
201  *
202  * If a public signal function is called, and detects its being called
203  * from within the handler, it instead adds a deferred signal entry
204  * and immediately returns.
205  *
206  * Once the handler is complete, and all pending C stack state changes
207  * are complete, the deferred signals are drained and processed.
208  */
210 {
211  connection_dsignal_entry_t *dsignal, *prev;
212 
213  prev = fr_dlist_tail(&conn->deferred_signals);
214  if (prev && (prev->signal == signal)) return; /* Don't insert duplicates */
215 
216  MEM(dsignal = talloc_zero(conn, connection_dsignal_entry_t));
217  dsignal->signal = signal;
218  fr_dlist_insert_tail(&conn->deferred_signals, dsignal);
219 
220 // DEBUG4("Adding deferred signal - %s", fr_table_str_by_value(connection_dsignals, signal, "<INVALID>"));
221 }
222 
223 /** Notification function to tell connection_deferred_signal_process that the connection has been freed
224  *
225  */
228  UNUSED connection_state_t state, void *uctx)
229 {
230  bool *freed = uctx;
231  *freed = true;
232 }
233 
234 /** Process any deferred signals
235  *
236  */
238 {
240  bool freed = false;
241 
242  /*
243  * We're inside and an instance of this function
244  * higher in the call stack. Don't do anything.
245  */
246  if (conn->processing_signals) return;
247 
248  /*
249  * Get notified if the connection gets freed
250  * out from under us...
251  */
253  conn->processing_signals = true;
254 
255  while ((dsignal = fr_dlist_head(&conn->deferred_signals))) {
256  connection_dsignal_t signal;
257  fr_dlist_remove(&conn->deferred_signals, dsignal);
258  signal = dsignal->signal;
259  talloc_free(dsignal);
260 
261  DEBUG4("Processing deferred signal - %s",
262  fr_table_str_by_value(connection_dsignals, signal, "<INVALID>"));
263 
264  switch (signal) {
267  break;
268 
271  break;
272 
273  case CONNECTION_DSIGNAL_RECONNECT_FAILED: /* Reconnect - Failed */
275  break;
276 
277  case CONNECTION_DSIGNAL_RECONNECT_EXPIRED: /* Reconnect - Expired */
279  break;
280 
283  break;
284 
287  break;
288 
289  case CONNECTION_DSIGNAL_FREE: /* Freed */
290  talloc_free(conn);
291  return;
292  }
293 
294  /*
295  * One of the signal handlers freed the connection
296  * return immediately.
297  */
298  /* coverity[dead_error_line] */
299  if (freed) return;
300  }
301 
302  conn->processing_signals = false;
304 }
305 
306 /** Pause processing of deferred signals
307  *
308  * @param[in] conn to pause signal processing for.
309  */
311 {
312  conn->signals_pause++;
313 }
314 
315 /** Resume processing of deferred signals
316  *
317  * @param[in] conn to resume signal processing for.
318  */
320 {
321  if (conn->signals_pause > 0) conn->signals_pause--;
322  if (conn->signals_pause > 0) return;
323 
324  /*
325  * If we're not in a handler process the
326  * deferred signals now.
327  */
328  if (!conn->in_handler) {
330  return;
331  }
332 }
333 
334 /** Called when we enter a handler
335  *
336  */
337 #define HANDLER_BEGIN(_conn, _func) \
338 void *_prev_handler = (_conn)->in_handler; \
339 do { \
340  (_conn)->in_handler = (void *)(_func); \
341 } while (0)
342 
343 /** Called when we exit a handler
344  *
345  */
346 #define HANDLER_END(_conn) \
347 do { \
348  (_conn)->in_handler = _prev_handler; \
349  if (!(_conn)->signals_pause && (!(_conn)->in_handler)) connection_deferred_signal_process(_conn); \
350 } while(0)
351 
352 
353 /** Call a list of watch functions associated with a state
354  *
355  */
356 CC_NO_UBSAN(function) /* UBSAN: false positive - Public/private version of connection_t trips -fsanitize=function */
357 static inline void connection_watch_call(connection_t *conn, fr_dlist_head_t *list)
358 {
359  /*
360  * Nested watcher calls are not allowed
361  * and shouldn't be possible because of
362  * deferred signal processing.
363  */
364  fr_assert(conn->next_watcher == NULL);
365 
366  while ((conn->next_watcher = fr_dlist_next(list, conn->next_watcher))) {
367  connection_watch_entry_t *entry = conn->next_watcher;
368  bool oneshot = entry->oneshot; /* Watcher could be freed, so store now */
369 
370  if (!entry->enabled) continue;
371  if (oneshot) conn->next_watcher = fr_dlist_remove(list, entry);
372 
373 /*
374  DEBUG4("Notifying %swatcher - (%p)(conn=%p, prev=%s, state=%s, uctx=%p)",
375  entry->oneshot ? "oneshot " : "",
376  entry->func,
377  conn,
378  fr_table_str_by_value(connection_states, conn->pub.prev, "<INVALID>"),
379  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"),
380  entry->uctx);
381 */
382 
383  entry->func(conn, conn->pub.prev, conn->pub.state, entry->uctx);
384 
385  if (oneshot) talloc_free(entry);
386  }
387  conn->next_watcher = NULL;
388 }
389 
390 /** Call the pre handler watch functions
391  *
392  */
393 #define WATCH_PRE(_conn) \
394 do { \
395  if (fr_dlist_empty(&(_conn)->watch_pre[(_conn)->pub.state])) break; \
396  { \
397  HANDLER_BEGIN(conn, &(_conn)->watch_pre[(_conn)->pub.state]); \
398  connection_watch_call((_conn), &(_conn)->watch_pre[(_conn)->pub.state]); \
399  HANDLER_END(conn); \
400  } \
401 } while(0)
402 
403 /** Call the post handler watch functions
404  *
405  */
406 #define WATCH_POST(_conn) \
407 do { \
408  if (fr_dlist_empty(&(_conn)->watch_post[(_conn)->pub.state])) break; \
409  { \
410  HANDLER_BEGIN(conn, &(_conn)->watch_post[(_conn)->pub.state]); \
411  connection_watch_call((_conn), &(_conn)->watch_post[(_conn)->pub.state]); \
412  HANDLER_END(conn); \
413  } \
414 } while(0)
415 
416 /** Remove a watch function from a pre/post[state] list
417  *
418  */
419 static int connection_del_watch(connection_t *conn, fr_dlist_head_t *state_lists,
421 {
422  connection_watch_entry_t *entry = NULL;
423  fr_dlist_head_t *list = &state_lists[state];
424 
425  while ((entry = fr_dlist_next(list, entry))) {
426  if (entry->func == watch) {
427 /*
428  DEBUG4("Removing %s watcher %p",
429  fr_table_str_by_value(connection_states, state, "<INVALID>"),
430  watch);
431 */
432  if (conn->next_watcher == entry) {
433  conn->next_watcher = fr_dlist_remove(list, entry);
434  } else {
435  fr_dlist_remove(list, entry);
436  }
437  talloc_free(entry);
438  return 0;
439  }
440  }
441 
442  return -1;
443 }
444 
445 /** Remove a watch function from a pre list
446  *
447  * @param[in] conn The connection to remove the watcher from.
448  * @param[in] state to remove the watch from.
449  * @param[in] watch Function to remove.
450  * @return
451  * - 0 if the function was removed successfully.
452  * - -1 if the function wasn't present in the watch list.
453  * - -2 an invalid state was passed.
454  */
456 {
457  if (state >= CONNECTION_STATE_MAX) return -2;
458 
459  return connection_del_watch(conn, conn->watch_pre, state, watch);
460 }
461 
462 /** Remove a watch function from a post list
463  *
464  * @param[in] conn The connection to remove the watcher from.
465  * @param[in] state to remove the watch from.
466  * @param[in] watch Function to remove.
467  * @return
468  * - 0 if the function was removed successfully.
469  * - -1 if the function wasn't present in the watch list.
470  * - -2 an invalid state was passed.
471  */
473 {
474  if (state >= CONNECTION_STATE_MAX) return -2;
475 
476  return connection_del_watch(conn, conn->watch_post, state, watch);
477 }
478 
479 /** Add a watch entry to the pre/post[state] list
480  *
481  */
483  connection_watch_t watch, bool oneshot, void const *uctx)
484 {
486 
487  MEM(entry = talloc_zero(conn, connection_watch_entry_t));
488 
489  entry->func = watch;
490  entry->oneshot = oneshot;
491  entry->enabled = true;
492  memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
493 
494  fr_dlist_insert_tail(list, entry);
495 
496  return entry;
497 }
498 
499 /** Add a callback to be executed before a state function has been called
500  *
501  * @param[in] conn to add watcher to.
502  * @param[in] state to call watcher on entering.
503  * @param[in] watch function to call.
504  * @param[in] oneshot If true, remove the function after calling.
505  * @param[in] uctx to pass to callbacks.
506  * @return
507  * - NULL if state value is invalid.
508  * - A new watch entry handle.
509  */
511  connection_watch_t watch, bool oneshot, void const *uctx)
512 {
513  if (state >= CONNECTION_STATE_MAX) return NULL;
514 
515  return connection_add_watch(conn, &conn->watch_pre[state], watch, oneshot, uctx);
516 }
517 
518 /** Add a callback to be executed after a state function has been called
519  *
520  * Where a user callback is executed on state change, the post function
521  * is only called if the callback succeeds.
522  *
523  * @param[in] conn to add watcher to.
524  * @param[in] state to call watcher on entering.
525  * @param[in] watch function to call.
526  * @param[in] oneshot If true, remove the function after calling.
527  * @param[in] uctx to pass to callbacks.
528  * @return
529  * - NULL if state value is invalid.
530  * - A new watch entry handle.
531  */
533  connection_watch_t watch, bool oneshot, void const *uctx)
534 {
535  if (state >= CONNECTION_STATE_MAX) return NULL;
536 
537  return connection_add_watch(conn, &conn->watch_post[state], watch, oneshot, uctx);
538 }
539 
540 /** Enable a watcher
541  *
542  * @param[in] entry to enabled.
543  */
545 {
546  (void)talloc_get_type_abort(entry, connection_watch_entry_t);
547  entry->enabled = true;
548 }
549 
550 /** Disable a watcher
551  *
552  * @param[in] entry to disable.
553  */
555 {
556  (void)talloc_get_type_abort(entry, connection_watch_entry_t);
557  entry->enabled = false;
558 }
559 
560 /** Enable a watcher and replace the uctx
561  *
562  * @param[in] entry to enabled.
563  * @param[in] uctx Opaque data to pass to the callback.
564  */
566 {
567  (void)talloc_get_type_abort(entry, connection_watch_entry_t);
568  entry->enabled = true;
569  memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
570 }
571 
572 /** Change the uctx of an entry
573  *
574  * @param[in] entry to enabled.
575  * @param[in] uctx Opaque data to pass to the callback.
576  */
578 {
579  (void)talloc_get_type_abort(entry, connection_watch_entry_t);
580  memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
581 }
582 
583 /** Return the state of a watch entry
584  *
585  * @param[in] entry to return state of.
586  * @return
587  * - true if enabled.
588  * - false if disabled.
589  */
591 {
592  (void)talloc_get_type_abort(entry, connection_watch_entry_t);
593  return entry->enabled;
594 }
595 
596 /** Return the number of times we've attempted to establish or re-establish this connection
597  *
598  * @param[in] conn to get count from.
599  * @return the number of times the connection has reconnected.
600  */
602 {
603  if (conn->pub.reconnected == 0) return 0; /* Has never been initialised */
604 
605  return conn->pub.reconnected - 1; /* We don't count the first connection attempt */
606 }
607 
608 /** Return the number of times this connection has timed out whilst connecting
609  *
610  * @param[in] conn to get count from.
611  * @return the number of times the connection has timed out whilst connecting.
612  */
614 {
615  return conn->pub.timed_out;
616 }
617 
618 /** The requisite period of time has passed, try and re-open the connection
619  *
620  * @param[in] el the time event occurred on.
621  * @param[in] now The current time.
622  * @param[in] uctx The #connection_t the fd is associated with.
623  */
625 {
626  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
627 
628  switch (conn->pub.state) {
632  break;
633 
634  default:
636  break;
637  }
638 }
639 
640 /** Close the connection, then wait for another state change
641  *
642  */
644 {
645  switch (conn->pub.state) {
649  break;
650 
651  default:
653  return;
654  }
655 
657 
658  fr_event_timer_delete(&conn->ev);
659 
660  /*
661  * If there's a close callback, call it, so that the
662  * API client can free any resources associated
663  * with the connection handle.
664  */
665  WATCH_PRE(conn);
666 
667  /*
668  * is_closed is for pure paranoia. If everything
669  * is working correctly this state should never
670  * be entered if the connection is closed.
671  */
672  fr_assert(!conn->is_closed);
673  if (conn->close && !conn->is_closed) {
674  HANDLER_BEGIN(conn, conn->close);
675  DEBUG4("Calling close(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
676  conn->close(conn->pub.el, conn->pub.h, conn->uctx);
677  conn->is_closed = true; /* Ensure close doesn't get called twice if the connection is freed */
678  HANDLER_END(conn);
679  } else {
680  conn->is_closed = true;
681  }
682  WATCH_POST(conn);
683 }
684 
685 /** Connection timeout
686  *
687  * Connection wasn't opened within the configured period of time
688  *
689  * @param[in] el the time event occurred on.
690  * @param[in] now The current time.
691  * @param[in] uctx The #connection_t the fd is associated with.
692  */
694 {
695  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
696 
698 }
699 
700 /** Gracefully shutdown the handle
701  *
702  */
704 {
705  connection_state_t ret;
706 
707  switch (conn->pub.state) {
709  break;
710 
711  default:
713  return;
714  }
715 
717 
718  WATCH_PRE(conn);
719  {
720  HANDLER_BEGIN(conn, conn->shutdown);
721  DEBUG4("Calling shutdown(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
722  ret = conn->shutdown(conn->pub.el, conn->pub.h, conn->uctx);
723  HANDLER_END(conn);
724  }
725  switch (ret) {
727  break;
728 
729  default:
731  return;
732  }
733  WATCH_POST(conn);
734 
735  /*
736  * If there's a connection timeout,
737  * set, then add the timer.
738  *
739  * The connection may be bad, in which
740  * case we want to automatically fail
741  * if it doesn't shutdown within the
742  * timeout period.
743  */
745  if (fr_event_timer_in(conn, conn->pub.el, &conn->ev,
746  conn->connection_timeout, _connection_timeout, conn) < 0) {
747  /*
748  * Can happen when the event loop is exiting
749  */
750  PERROR("Failed setting connection_timeout timer, closing connection");
752  }
753  }
754 }
755 
756 /** Connection failed
757  *
758  * Transition to the CONNECTION_STATE_FAILED state.
759  *
760  * If the connection was open, or couldn't be opened wait for reconnection_delay before transitioning
761  * back to init.
762  *
763  * If no reconnection_delay was set, transition to halted.
764  *
765  * @param[in] conn that failed.
766  */
768 {
769  connection_state_t prev;
771 
773 
774  /*
775  * Explicit error occurred, delete the connection timer
776  */
777  fr_event_timer_delete(&conn->ev);
778 
779  /*
780  * Record what state the connection is currently in
781  * so we can figure out what to do next.
782  */
783  prev = conn->pub.state;
784 
785  /*
786  * Now transition to failed
787  */
789 
790  /*
791  * If there's a failed callback, give it the
792  * opportunity to suspend/destroy the
793  * connection.
794  */
795  WATCH_PRE(conn);
796  if (conn->failed) {
797  HANDLER_BEGIN(conn, conn->failed);
798  DEBUG4("Calling failed(h=%p, state=%s, uctx=%p)", conn->pub.h,
799  fr_table_str_by_value(connection_states, prev, "<INVALID>"), conn->uctx);
800  ret = conn->failed(conn->pub.h, prev, conn->uctx);
801  HANDLER_END(conn);
802  }
803  WATCH_POST(conn);
804 
805  /*
806  * Enter the closed state if we failed during
807  * connecting, or when we were connected.
808  */
809  switch (prev) {
812  case CONNECTION_STATE_TIMEOUT: /* Timeout means the connection progress past init */
813  case CONNECTION_STATE_SHUTDOWN: /* Shutdown means the connection failed whilst shutting down */
815  break;
816 
817  default:
818  break;
819  }
820 
821  if (conn->failed) {
822  switch (ret) {
823  /*
824  * The callback signalled it wants the
825  * connection to be reinitialised
826  * after reconnection_delay, or
827  * immediately if the failure was due
828  * to a connection timeout.
829  */
831  break;
832 
833  /*
834  * The callback signalled it wants the
835  * connection to stop.
836  */
838  default:
840  return;
841  }
842  }
843 
844  /*
845  * What previous state we were in
846  * determines if we need to apply the
847  * reconnect timeout.
848  */
849  switch (prev) {
850  case CONNECTION_STATE_INIT: /* Failed during initialisation */
851  case CONNECTION_STATE_CONNECTED: /* Failed after connecting */
852  case CONNECTION_STATE_CONNECTING: /* Failed during connecting */
853  case CONNECTION_STATE_SHUTDOWN: /* Failed during shutdown */
855  DEBUG2("Delaying reconnection by %pVs", fr_box_time_delta(conn->reconnection_delay));
856  if (fr_event_timer_in(conn, conn->pub.el, &conn->ev,
857  conn->reconnection_delay, _reconnect_delay_done, conn) < 0) {
858  /*
859  * Can happen when the event loop is exiting
860  */
861  PERROR("Failed inserting reconnection_delay timer event, halting connection");
863  }
864  return;
865  }
866 
867  /*
868  * If there's no reconnection
869  * delay, then don't automatically
870  * reconnect, and wait to be
871  * signalled.
872  */
874  break;
875 
876  case CONNECTION_STATE_TIMEOUT: /* Failed during connecting due to timeout */
878  break;
879 
880  default:
881  fr_assert(0);
882  }
883 }
884 
885 /** Enter the timeout state
886  *
887  * The connection took took long to open. Timeout the attempt and transition
888  * to the failed state.
889  */
891 {
892  switch (conn->pub.state) {
895  break;
896 
897  default:
899  }
900 
901  ERROR("Connection failed - timed out after %pVs", fr_box_time_delta(conn->connection_timeout));
902 
904 
905  conn->pub.timed_out++;
906 
908 }
909 
910 /** Enter the halted state
911  *
912  * Here we wait, until signalled by connection_signal_reconnect.
913  */
915 {
916  fr_assert(conn->is_closed);
917 
918  switch (conn->pub.state) {
919  case CONNECTION_STATE_FAILED: /* Init failure */
921  break;
922 
923  default:
925  }
926 
927  fr_event_timer_delete(&conn->ev);
928 
930  WATCH_PRE(conn);
931  WATCH_POST(conn);
932 }
933 
934 /** Enter the connected state
935  *
936  * The connection is now fully connected. At this point we call the open callback
937  * so that the API client can install its normal set of I/O callbacks to deal with
938  * sending/receiving actual data.
939  *
940  * After this, the connection will only transition states if an API client
941  * explicitly calls connection_signal_reconnect.
942  *
943  * The connection API cannot monitor the connection for failure conditions.
944  *
945  * @param[in] conn Entering the connecting state.
946  */
948 {
949  int ret;
950 
952 
954 
955  fr_event_timer_delete(&conn->ev);
956  WATCH_PRE(conn);
957  if (conn->open) {
958  HANDLER_BEGIN(conn, conn->open);
959  DEBUG4("Calling open(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
960  ret = conn->open(conn->pub.el, conn->pub.h, conn->uctx);
961  HANDLER_END(conn);
962  } else {
964  }
965 
966  switch (ret) {
967  /*
968  * Callback agrees everything is connected
969  */
971  DEBUG2("Connection established");
972  WATCH_POST(conn); /* Only call if we successfully connected */
973  return;
974 
975  /*
976  * Open callback failed
977  */
979  default:
980  PERROR("Connection failed");
982  return;
983  }
984 }
985 
986 /** Enter the connecting state
987  *
988  * After this function returns we wait to be signalled with connection_singal_connected
989  * or for the connection timer to expire.
990  *
991  * @param[in] conn Entering the connecting state.
992  */
994 {
995  switch (conn->pub.state) {
997  break;
998 
999  default:
1001  return;
1002  }
1003 
1005 
1006  WATCH_PRE(conn);
1007  WATCH_POST(conn);
1008 
1009  /*
1010  * If there's a connection timeout,
1011  * set, then add the timer.
1012  */
1014  if (fr_event_timer_in(conn, conn->pub.el, &conn->ev,
1015  conn->connection_timeout, _connection_timeout, conn) < 0) {
1016  PERROR("Failed setting connection_timeout event, failing connection");
1017 
1018  /*
1019  * This can happen when the event loop
1020  * is exiting.
1021  *
1022  * Entering fail will close partially
1023  * open connection and then, if we still
1024  * can't insert a timer, then the connection
1025  * will be halted and sit idle until its
1026  * freed.
1027  */
1029  }
1030  }
1031 }
1032 
1033 /** Initial state of the connection
1034  *
1035  * Calls the init function we were passed to allocate a library specific handle or
1036  * file descriptor.
1037  *
1038  * @param[in] conn To initialise.
1039  */
1041 {
1042  connection_state_t ret;
1043 
1044  switch (conn->pub.state) {
1048  break;
1049 
1050  default:
1052  return;
1053  }
1054 
1055  /*
1056  * Increment every time we enter
1057  * We have to do this, as we don't know
1058  * whether the connection was halted by
1059  * the failed callback, and is now being
1060  * reconnected, or was automatically
1061  * reconnected.
1062  */
1063  conn->pub.reconnected++;
1064 
1066 
1067  /*
1068  * If we have an init callback, call it.
1069  */
1070  WATCH_PRE(conn);
1071  if (conn->init) {
1072  HANDLER_BEGIN(conn, conn->init);
1073  DEBUG4("Calling init(h_out=%p, conn=%p, uctx=%p)", &conn->pub.h, conn, conn->uctx);
1074  ret = conn->init(&conn->pub.h, conn, conn->uctx);
1075  HANDLER_END(conn);
1076  } else {
1078  }
1079 
1080  switch (ret) {
1082  conn->is_closed = false; /* We now have a handle */
1083  WATCH_POST(conn); /* Only call if we successfully initialised the handle */
1085  return;
1086 
1088  conn->is_closed = false; /* We now have a handle */
1089  WATCH_POST(conn); /* Only call if we successfully initialised the handle */
1091  return;
1092 
1093  /*
1094  * Initialisation callback failed
1095  */
1097  default:
1098  PERROR("Connection initialisation failed");
1100  break;
1101  }
1102 }
1103 
1104 /** Asynchronously signal a halted connection to start
1105  *
1106  */
1108 {
1109  DEBUG2("Signalled to start from %s state",
1110  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1111 
1112  if (DEFER_SIGNALS(conn)) {
1114  return;
1115  }
1116 
1117  switch (conn->pub.state) {
1120  break;
1121 
1122  default:
1123  break;
1124  }
1125 }
1126 
1127 /** Asynchronously signal that the connection is open
1128  *
1129  * Some libraries like libldap are extremely annoying and only return control
1130  * to the caller after a connection is open.
1131  *
1132  * For these libraries, we can't use an I/O handler to determine when the
1133  * connection is open so we rely on callbacks built into the library to
1134  * signal that the transition has occurred.
1135  *
1136  */
1138 {
1139  fr_assert(!conn->open); /* Use one or the other not both! */
1140 
1141  DEBUG2("Signalled connected from %s state",
1142  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1143 
1144  if (DEFER_SIGNALS(conn)) {
1146  return;
1147  }
1148 
1149  switch (conn->pub.state) {
1152  break;
1153 
1154  default:
1155  break;
1156  }
1157 }
1158 
1159 /** Asynchronously signal the connection should be reconnected
1160  *
1161  * Should be called if the caller has knowledge that the connection is bad
1162  * and should be reconnected.
1163  *
1164  * @param[in] conn to reconnect.
1165  * @param[in] reason Why the connection was signalled to reconnect.
1166  */
1168 {
1169  DEBUG2("Signalled to reconnect from %s state",
1170  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1171 
1172  if (DEFER_SIGNALS(conn)) {
1173  if ((reason == CONNECTION_EXPIRED) && conn->shutdown) {
1175  return;
1176  }
1177 
1179  return;
1180  }
1181 
1182  switch (conn->pub.state) {
1183  case CONNECTION_STATE_CLOSED: /* Don't circumvent reconnection_delay */
1184  case CONNECTION_STATE_INIT: /* Already initialising */
1185  break;
1186 
1188  connection_signal_init(conn);
1189  break;
1190 
1192  if (reason == CONNECTION_EXPIRED) break; /* Already shutting down */
1194  break;
1195 
1197  if (reason == CONNECTION_EXPIRED) {
1198  if (conn->shutdown) {
1200  break;
1201  }
1203  break;
1204  }
1205  FALL_THROUGH;
1206 
1211  break;
1212 
1213  case CONNECTION_STATE_MAX:
1214  fr_assert(0);
1215  return;
1216  }
1217 }
1218 
1219 /** Shuts down a connection gracefully
1220  *
1221  * If a shutdown function has been provided, it is called.
1222  * It's then up to the shutdown function to install I/O handlers to signal
1223  * when the connection has finished shutting down and should be closed
1224  * via #connection_signal_halt.
1225  *
1226  * @param[in] conn to shutdown.
1227  */
1229 {
1230  DEBUG2("Signalled to shutdown from %s state",
1231  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1232 
1233  if (DEFER_SIGNALS(conn)) {
1235  return;
1236  }
1237 
1238  switch (conn->pub.state) {
1241  break;
1242 
1243  case CONNECTION_STATE_INIT:
1245  break;
1246 
1247  /*
1248  * If the connection is connected it needs to be
1249  * shutdown first.
1250  *
1251  * The shutdown callback or an FD event it inserts then
1252  * to signal that the connection should be closed.
1253  */
1255  if (conn->shutdown) {
1257  break;
1258  }
1259  FALL_THROUGH;
1260 
1261  /*
1262  * If the connection is any of these states it
1263  * must have completed INIT which means it has
1264  * an active handle which needs to be closed before
1265  * the connection is halted.
1266  */
1271  fr_assert(conn->is_closed);
1272 
1273  FALL_THROUGH;
1276  break;
1277 
1278  case CONNECTION_STATE_MAX:
1279  fr_assert(0);
1280  return;
1281  }
1282 }
1283 
1284 /** Shuts down a connection ungracefully
1285  *
1286  * If a connection is in an open or connection state it will be closed immediately.
1287  * Otherwise the connection will transition directly to the halted state.
1288  *
1289  * @param[in] conn to halt.
1290  */
1292 {
1293  DEBUG2("Signalled to halt from %s state",
1294  fr_table_str_by_value(connection_states, conn->pub.state, "<INVALID>"));
1295 
1296  if (DEFER_SIGNALS(conn)) {
1298  return;
1299  }
1300 
1301  switch (conn->pub.state) {
1303  break;
1304 
1305  case CONNECTION_STATE_INIT:
1308  break;
1309 
1310  /*
1311  * If the connection is any of these states it
1312  * must have completed INIT which means it has
1313  * an active handle which needs to be closed before
1314  * the connection is halted.
1315  *
1316  * The exception is when a connection fails to open
1317  * so goes from INIT -> FAILED, means is_closed
1318  * is true, as the connection has never opened.
1319  */
1325  if (!conn->is_closed) connection_state_enter_closed(conn);
1326  fr_assert(conn->is_closed);
1328  break;
1329 
1330  case CONNECTION_STATE_MAX:
1331  fr_assert(0);
1332  return;
1333  }
1334 }
1335 /** Receive an error notification when we're connecting a socket
1336  *
1337  * @param[in] el event list the I/O event occurred on.
1338  * @param[in] fd the I/O even occurred for.
1339  * @param[in] flags from_kevent.
1340  * @param[in] fd_errno from kevent.
1341  * @param[in] uctx The #connection_t this fd is associated with.
1342  */
1343 static void _connection_error(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, int fd_errno, void *uctx)
1344 {
1345  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
1346 
1347  ERROR("Connection failed for fd (%u): %s", fd, fr_syserror(fd_errno));
1349 }
1350 
1351 /** Receive a write notification after a socket is connected
1352  *
1353  * @param[in] el event list the I/O event occurred on.
1354  * @param[in] fd the I/O even occurred for.
1355  * @param[in] flags from kevent.
1356  * @param[in] uctx The #connection_t this fd is associated with.
1357  */
1358 static void _connection_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1359 {
1360  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
1361 
1364 }
1365 
1366 /** Remove the FD we were watching for connection open/fail from the event loop
1367  *
1368  */
1370  UNUSED connection_state_t prev, connection_state_t state, void *uctx)
1371 {
1372  int fd = *(talloc_get_type_abort(uctx, int));
1373 
1374  /*
1375  * Two states can trigger a cleanup
1376  * Remove the watch on the one that didn't
1377  */
1378  switch (state) {
1381  break;
1382 
1385  break;
1386 
1387  default:
1388  fr_assert(0);
1389  break;
1390  }
1391 
1393  talloc_free(uctx);
1394 }
1395 
1396 /** Setup the connection to change states to connected or failed based on I/O events
1397  *
1398  * Will automatically cleanup after itself, in preparation for
1399  * new I/O handlers to be installed in the open() callback.
1400  *
1401  * @return
1402  * - 0 on success.
1403  * - -1 on failure.
1404  */
1406 {
1407  int *fd_s;
1408 
1409  /*
1410  * If connection becomes writable we
1411  * assume it's open.
1412  */
1413  if (fr_event_fd_insert(conn, NULL, conn->pub.el, fd,
1414  NULL,
1417  conn) < 0) {
1418  PERROR("Failed inserting fd (%u) into event loop %p",
1419  fd, conn->pub.el);
1421  return -1;
1422  }
1423 
1424  /*
1425  * Stop the static analysis tools
1426  * complaining about assigning ints
1427  * to pointers.
1428  */
1429  MEM(fd_s = talloc_zero(conn, int));
1430  *fd_s = fd;
1431 
1432  /*
1433  * Add a oneshot watcher to remove
1434  * the I/O handlers if the connection
1435  * fails, or is connected.
1436  */
1438  _connection_signal_on_fd_cleanup, true, fd_s);
1440  _connection_signal_on_fd_cleanup, true, fd_s);
1441  return 0;
1442 }
1443 
1444 /** Close a connection if it's freed
1445  *
1446  * @param[in] conn to free.
1447  * @return
1448  * - 0 connection was freed immediately.
1449  * - 1 connection free was deferred.
1450  */
1452 {
1453  /*
1454  * Explicitly cancel any pending events
1455  */
1456  fr_event_timer_delete(&conn->ev);
1457 
1458  /*
1459  * Don't allow the connection to be
1460  * arbitrarily freed by a callback.
1461  *
1462  * Add a deferred signal to free the
1463  * connection later.
1464  */
1465  if (DEFER_SIGNALS(conn)) {
1467  return -1;
1468  }
1469 
1470  switch (conn->pub.state) {
1472  break;
1473 
1474  /*
1475  * Need to close the connection first
1476  */
1480  FALL_THROUGH;
1481 
1482  default:
1484  break;
1485  }
1486  return 0;
1487 }
1488 
1489 /** Allocate a new connection
1490  *
1491  * After the connection has been allocated, it should be started with a call to #connection_signal_init.
1492  *
1493  * The connection state machine can detect when the connection is open in one of two ways.
1494  * - You can install a generic socket open/fail callback, using connection_signal_on_fd.
1495  * - You can call either #connection_signal_connected or connection_signal_recommend.
1496  * This allows the connection state machine to work with more difficult library APIs,
1497  * which may not return control to the caller as connections are opened.
1498  *
1499  * @param[in] ctx to allocate connection handle in. If the connection
1500  * handle is freed, and the #connection_state_t is
1501  * #CONNECTION_STATE_CONNECTING or #CONNECTION_STATE_CONNECTED the
1502  * close callback will be called.
1503  * @param[in] el to use for timer events, and to pass to the #connection_open_t callback.
1504  * @param[in] funcs callback functions.
1505  * @param[in] conf our configuration.
1506  * @param[in] log_prefix To prepend to log messages.
1507  * @param[in] uctx User context to pass to callbacks.
1508  * @return
1509  * - A new #connection_t on success.
1510  * - NULL on failure.
1511  */
1513  connection_funcs_t const *funcs,
1514  connection_conf_t const *conf,
1515  char const *log_prefix,
1516  void const *uctx)
1517 {
1518  size_t i;
1519  connection_t *conn;
1520  uint64_t id;
1521 
1522  fr_assert(el);
1523 
1524  MEM(conn = talloc(ctx, connection_t));
1525  talloc_set_destructor(conn, _connection_free);
1526 
1528 
1529  *conn = (connection_t){
1530  .pub = {
1531  .id = id,
1532  .state = CONNECTION_STATE_HALTED,
1533  .el = el
1534  },
1535  .reconnection_delay = conf->reconnection_delay,
1536  .connection_timeout = conf->connection_timeout,
1537  .init = funcs->init,
1538  .open = funcs->open,
1539  .close = funcs->close,
1540  .failed = funcs->failed,
1541  .shutdown = funcs->shutdown,
1542  .is_closed = true, /* Starts closed */
1543  .pub.name = talloc_asprintf(conn, "%s - [%" PRIu64 "]", log_prefix, id)
1544  };
1545  memcpy(&conn->uctx, &uctx, sizeof(conn->uctx));
1546 
1547  for (i = 0; i < NUM_ELEMENTS(conn->watch_pre); i++) {
1549  }
1550  for (i = 0; i < NUM_ELEMENTS(conn->watch_post); i++) {
1552  }
1554 
1555  /*
1556  * Pre-allocate a on_halt watcher for deferred signal processing
1557  */
1560  connection_watch_disable(conn->on_halted); /* Start disabled */
1561 
1562  return conn;
1563 }
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
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:204
connection_state_t(* connection_failed_t)(void *h, connection_state_t state, void *uctx)
Notification that a connection attempt has failed.
Definition: connection.h:167
uint64_t _CONST timed_out
How many times has this connection timed out when connecting.
Definition: connection.h:78
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:181
fr_event_list_t *_CONST el
Event list for timers and I/O events.
Definition: connection.h:74
connection_state_t(* connection_init_t)(void **h_out, connection_t *conn, void *uctx)
Callback for the initialise state.
Definition: connection.h:116
connection_state_t
Definition: connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
Definition: connection.h:46
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition: connection.h:55
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
@ CONNECTION_STATE_TIMEOUT
Timeout during CONNECTION_STATE_CONNECTING.
Definition: connection.h:51
@ CONNECTION_STATE_INIT
Init state, sets up connection.
Definition: connection.h:49
@ CONNECTION_STATE_MAX
Definition: connection.h:56
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ CONNECTION_STATE_SHUTDOWN
Connection is shutting down.
Definition: connection.h:53
uint64_t _CONST reconnected
How many times we've attempted to establish or re-establish this connection.
Definition: connection.h:76
void *_CONST h
Connection handle.
Definition: connection.h:73
connection_reason_t
Definition: connection.h:83
@ CONNECTION_EXPIRED
Connection is being reconnected because it's at the end of its life.
Definition: connection.h:85
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
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:130
connection_state_t _CONST state
Current connection state.
Definition: connection.h:70
connection_init_t init
Definition: connection.h:187
connection_shutdown_t shutdown
Definition: connection.h:189
connection_failed_t failed
Definition: connection.h:190
connection_open_t open
Definition: connection.h:188
uint64_t _CONST id
Unique identifier for the connection.
Definition: connection.h:72
connection_close_t close
Definition: connection.h:191
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:149
Holds a complete set of functions for a connection.
Definition: connection.h:186
Public fields for the connection.
Definition: connection.h:67
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
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:555
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:531
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:486
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:378
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:275
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:232
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition: event.h:62
#define fr_event_timer_in(...)
Definition: event.h:255
CC_NO_UBSAN(function)
(Re-)Initialises the libldap side of the connection handle
Definition: connection.c:341
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG4(_fmt,...)
Definition: log.h:267
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition: event.c:1611
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:1260
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
#define DEBUG2(fmt,...)
Definition: radclient.h:43
static rs_t * conf
Definition: radsniff.c:53
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.
Definition: connection.c:1228
fr_dlist_head_t watch_post[CONNECTION_STATE_MAX]
Function called after state callback.
Definition: connection.c:98
void connection_watch_enable(connection_watch_entry_t *entry)
Enable a watcher.
Definition: connection.c:544
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:419
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:613
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
static void _connection_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Connection timeout.
Definition: connection.c:693
#define DEFER_SIGNALS(_conn)
Definition: connection.c:151
static fr_table_num_ordered_t const connection_dsignals[]
Definition: connection.c:166
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:226
void connection_watch_disable(connection_watch_entry_t *entry)
Disable a watcher.
Definition: connection.c:554
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:472
connection_shutdown_t shutdown
Signal the connection handle to start shutting down.
Definition: connection.c:104
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.
Definition: connection.c:1369
fr_time_delta_t connection_timeout
How long to wait in the CONNECTION_STATE_CONNECTING state.
Definition: connection.c:109
bool connection_watch_is_enabled(connection_watch_entry_t *entry)
Return the state of a watch entry.
Definition: connection.c:590
static atomic_uint_fast64_t connection_counter
Definition: connection.c:73
static size_t connection_dsignals_len
Definition: connection.c:175
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:114
static int _connection_free(connection_t *conn)
Close a connection if it's freed.
Definition: connection.c:1451
static void connection_state_enter_failed(connection_t *conn)
Connection failed.
Definition: connection.c:767
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
Definition: connection.c:1291
#define WATCH_POST(_conn)
Call the post handler watch functions.
Definition: connection.c:406
void connection_signals_resume(connection_t *conn)
Resume processing of deferred signals.
Definition: connection.c:319
#define HANDLER_BEGIN(_conn, _func)
Called when we enter a handler.
Definition: connection.c:337
connection_init_t init
Callback for initialising a connection.
Definition: connection.c:101
fr_event_timer_t const * ev
State transition timer.
Definition: connection.c:107
connection_close_t close
Callback to close a connection.
Definition: connection.c:103
static void connection_state_enter_init(connection_t *conn)
Initial state of the connection.
Definition: connection.c:1040
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:601
static void connection_state_enter_connecting(connection_t *conn)
Enter the connecting state.
Definition: connection.c:993
connection_watch_entry_t * next_watcher
Hack to insulate watcher iterator from deletions.
Definition: connection.c:99
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:393
static void connection_deferred_signal_process(connection_t *conn)
Process any deferred signals.
Definition: connection.c:237
connection_dsignal_t signal
Signal that was deferred.
Definition: connection.c:182
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.
Definition: connection.c:1343
connection_open_t open
Callback for 'open' notification.
Definition: connection.c:102
connection_failed_t failed
Callback for 'failed' notification.
Definition: connection.c:105
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:577
fr_time_delta_t reconnection_delay
How long to wait in the CONNECTION_STATE_FAILED state.
Definition: connection.c:111
static void connection_state_enter_closed(connection_t *conn)
Close the connection, then wait for another state change.
Definition: connection.c:643
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.
Definition: connection.c:1167
connection_dsignal_t
Deferred signals.
Definition: connection.c:156
@ CONNECTION_DSIGNAL_RECONNECT_FAILED
Reconnect a failed connection.
Definition: connection.c:159
@ CONNECTION_DSIGNAL_HALT
Close a connection (ungracefully).
Definition: connection.c:162
@ CONNECTION_DSIGNAL_INIT
Restart a halted connection.
Definition: connection.c:157
@ CONNECTION_DSIGNAL_FREE
Free a connection (no further dsignals processed).
Definition: connection.c:163
@ CONNECTION_DSIGNAL_SHUTDOWN
Close a connection (gracefully).
Definition: connection.c:161
@ CONNECTION_DSIGNAL_CONNECTED
Signal that a connection is connected.
Definition: connection.c:158
@ CONNECTION_DSIGNAL_RECONNECT_EXPIRED
Reconnect an expired connection (gracefully).
Definition: connection.c:160
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:97
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:510
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:346
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.
Definition: connection.c:1405
bool is_closed
The close callback has previously been called.
Definition: connection.c:93
#define STATE_TRANSITION(_new)
Definition: connection.c:133
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
Definition: connection.c:1107
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:914
static void connection_deferred_signal_add(connection_t *conn, connection_dsignal_t signal)
Add a deferred signal to the signal list.
Definition: connection.c:209
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:532
fr_dlist_t entry
Entry in the signals list.
Definition: connection.c:181
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.
Definition: connection.c:1358
size_t connection_states_len
Definition: connection.c:56
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.
Definition: connection.c:1512
static void connection_state_enter_connected(connection_t *conn)
Enter the connected state.
Definition: connection.c:947
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:455
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:482
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:119
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.
Definition: connection.c:624
#define BAD_STATE_TRANSITION(_new)
Definition: connection.c:143
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:565
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1137
static void connection_state_enter_shutdown(connection_t *conn)
Gracefully shutdown the handle.
Definition: connection.c:703
void connection_signals_pause(connection_t *conn)
Pause processing of deferred signals.
Definition: connection.c:310
bool processing_signals
Processing deferred signals, don't let the deferred signal processor be called multiple times.
Definition: connection.c:94
unsigned int signals_pause
Temporarily stop processing of signals.
Definition: connection.c:123
static void connection_state_enter_timeout(connection_t *conn)
Enter the timeout state.
Definition: connection.c:890
Holds a signal from a handler until it's safe to process it.
Definition: connection.c:180
An entry in a watch function list.
Definition: connection.c:78
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
@ 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:772
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 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
static fr_event_list_t * el
#define fr_box_time_delta(_val)
Definition: value.h:343