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: bb0103faab76e79d3cecf1da45086b4afc1a8708 $
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 fr_connection_s fr_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"), FR_CONNECTION_STATE_HALTED },
48  { L("INIT"), FR_CONNECTION_STATE_INIT },
49  { L("CONNECTING"), FR_CONNECTION_STATE_CONNECTING },
50  { L("TIMEOUT"), FR_CONNECTION_STATE_TIMEOUT },
51  { L("CONNECTED"), FR_CONNECTION_STATE_CONNECTED },
52  { L("SHUTDOWN"), FR_CONNECTION_STATE_SHUTDOWN },
53  { L("FAILED"), FR_CONNECTION_STATE_FAILED },
54  { L("CLOSED"), FR_CONNECTION_STATE_CLOSED },
55 };
57 
58 /** Map connection states to trigger names
59  *
60  */
62  [FR_CONNECTION_STATE_HALTED] = { L("connection.halted"), FR_CONNECTION_STATE_HALTED },
63  [FR_CONNECTION_STATE_INIT] = { L("connection.init"), FR_CONNECTION_STATE_INIT },
64  [FR_CONNECTION_STATE_CONNECTING]= { L("connection.connecting"), FR_CONNECTION_STATE_CONNECTING },
65  [FR_CONNECTION_STATE_TIMEOUT] = { L("connection.timeout"), FR_CONNECTION_STATE_TIMEOUT },
66  [FR_CONNECTION_STATE_CONNECTED] = { L("connection.connected"), FR_CONNECTION_STATE_CONNECTED },
67  [FR_CONNECTION_STATE_SHUTDOWN] = { L("connection.shutdown"), FR_CONNECTION_STATE_SHUTDOWN },
68  [FR_CONNECTION_STATE_FAILED] = { L("connection.failed"), FR_CONNECTION_STATE_FAILED },
69  [FR_CONNECTION_STATE_CLOSED] = { L("connection.closed"), FR_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  */
79  fr_dlist_t entry; //!< List entry.
80  fr_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 fr_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[FR_CONNECTION_STATE_MAX]; //!< Function called before state callback.
98  fr_dlist_head_t watch_post[FR_CONNECTION_STATE_MAX]; //!< Function called after state callback.
99  fr_connection_watch_entry_t *next_watcher; //!< Hack to insulate watcher iterator from deletions.
100 
101  fr_connection_init_t init; //!< Callback for initialising a connection.
102  fr_connection_open_t open; //!< Callback for 'open' notification.
103  fr_connection_close_t close; //!< Callback to close a connection.
104  fr_connection_shutdown_t shutdown; //!< Signal the connection handle to start shutting down.
105  fr_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  //!< #FR_CONNECTION_STATE_CONNECTING state.
111  fr_time_delta_t reconnection_delay; //!< How long to wait in the
112  //!< #FR_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  fr_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(fr_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(fr_connection_states, conn->pub.state, "<INVALID>"), \
137  fr_table_str_by_value(fr_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(fr_connection_states, conn->pub.state, "<INVALID>"), \
148  fr_table_str_by_value(fr_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("CONNECTING"), 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  */
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  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 fr_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 static inline void connection_watch_call(fr_connection_t *conn, fr_dlist_head_t *list)
357 {
358  /*
359  * Nested watcher calls are not allowed
360  * and shouldn't be possible because of
361  * deferred signal processing.
362  */
363  fr_assert(conn->next_watcher == NULL);
364 
365  while ((conn->next_watcher = fr_dlist_next(list, conn->next_watcher))) {
367  bool oneshot = entry->oneshot; /* Watcher could be freed, so store now */
368 
369  if (!entry->enabled) continue;
370  if (oneshot) conn->next_watcher = fr_dlist_remove(list, entry);
371 
372 /*
373  DEBUG4("Notifying %swatcher - (%p)(conn=%p, prev=%s, state=%s, uctx=%p)",
374  entry->oneshot ? "oneshot " : "",
375  entry->func,
376  conn,
377  fr_table_str_by_value(fr_connection_states, conn->pub.prev, "<INVALID>"),
378  fr_table_str_by_value(fr_connection_states, conn->pub.state, "<INVALID>"),
379  entry->uctx);
380 */
381 
382  entry->func(conn, conn->pub.prev, conn->pub.state, entry->uctx);
383 
384  if (oneshot) talloc_free(entry);
385  }
386  conn->next_watcher = NULL;
387 }
388 
389 /** Call the pre handler watch functions
390  *
391  */
392 #define WATCH_PRE(_conn) \
393 do { \
394  if (fr_dlist_empty(&(_conn)->watch_pre[(_conn)->pub.state])) break; \
395  { \
396  HANDLER_BEGIN(conn, &(_conn)->watch_pre[(_conn)->pub.state]); \
397  connection_watch_call((_conn), &(_conn)->watch_pre[(_conn)->pub.state]); \
398  HANDLER_END(conn); \
399  } \
400 } while(0)
401 
402 /** Call the post handler watch functions
403  *
404  */
405 #define WATCH_POST(_conn) \
406 do { \
407  if (fr_dlist_empty(&(_conn)->watch_post[(_conn)->pub.state])) break; \
408  { \
409  HANDLER_BEGIN(conn, &(_conn)->watch_post[(_conn)->pub.state]); \
410  connection_watch_call((_conn), &(_conn)->watch_post[(_conn)->pub.state]); \
411  HANDLER_END(conn); \
412  } \
413 } while(0)
414 
415 /** Remove a watch function from a pre/post[state] list
416  *
417  */
418 static int connection_del_watch(fr_connection_t *conn, fr_dlist_head_t *state_lists,
420 {
421  fr_connection_watch_entry_t *entry = NULL;
422  fr_dlist_head_t *list = &state_lists[state];
423 
424  while ((entry = fr_dlist_next(list, entry))) {
425  if (entry->func == watch) {
426 /*
427  DEBUG4("Removing %s watcher %p",
428  fr_table_str_by_value(fr_connection_states, state, "<INVALID>"),
429  watch);
430 */
431  if (conn->next_watcher == entry) {
432  conn->next_watcher = fr_dlist_remove(list, entry);
433  } else {
434  fr_dlist_remove(list, entry);
435  }
436  talloc_free(entry);
437  return 0;
438  }
439  }
440 
441  return -1;
442 }
443 
444 /** Remove a watch function from a pre list
445  *
446  * @param[in] conn The connection to remove the watcher from.
447  * @param[in] state to remove the watch from.
448  * @param[in] watch Function to remove.
449  * @return
450  * - 0 if the function was removed successfully.
451  * - -1 if the function wasn't present in the watch list.
452  * - -2 an invalid state was passed.
453  */
455 {
456  if (state >= FR_CONNECTION_STATE_MAX) return -2;
457 
458  return connection_del_watch(conn, conn->watch_pre, state, watch);
459 }
460 
461 /** Remove a watch function from a post list
462  *
463  * @param[in] conn The connection to remove the watcher from.
464  * @param[in] state to remove the watch from.
465  * @param[in] watch Function to remove.
466  * @return
467  * - 0 if the function was removed successfully.
468  * - -1 if the function wasn't present in the watch list.
469  * - -2 an invalid state was passed.
470  */
472 {
473  if (state >= FR_CONNECTION_STATE_MAX) return -2;
474 
475  return connection_del_watch(conn, conn->watch_post, state, watch);
476 }
477 
478 /** Add a watch entry to the pre/post[state] list
479  *
480  */
482  fr_connection_watch_t watch, bool oneshot, void const *uctx)
483 {
485 
486  MEM(entry = talloc_zero(conn, fr_connection_watch_entry_t));
487 
488  entry->func = watch;
489  entry->oneshot = oneshot;
490  entry->enabled = true;
491  memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
492 
493  fr_dlist_insert_tail(list, entry);
494 
495  return entry;
496 }
497 
498 /** Add a callback to be executed before a state function has been called
499  *
500  * @param[in] conn to add watcher to.
501  * @param[in] state to call watcher on entering.
502  * @param[in] watch function to call.
503  * @param[in] oneshot If true, remove the function after calling.
504  * @param[in] uctx to pass to callbacks.
505  * @return
506  * - NULL if state value is invalid.
507  * - A new watch entry handle.
508  */
510  fr_connection_watch_t watch, bool oneshot, void const *uctx)
511 {
512  if (state >= FR_CONNECTION_STATE_MAX) return NULL;
513 
514  return connection_add_watch(conn, &conn->watch_pre[state], watch, oneshot, uctx);
515 }
516 
517 /** Add a callback to be executed after a state function has been called
518  *
519  * Where a user callback is executed on state change, the post function
520  * is only called if the callback succeeds.
521  *
522  * @param[in] conn to add watcher to.
523  * @param[in] state to call watcher on entering.
524  * @param[in] watch function to call.
525  * @param[in] oneshot If true, remove the function after calling.
526  * @param[in] uctx to pass to callbacks.
527  * @return
528  * - NULL if state value is invalid.
529  * - A new watch entry handle.
530  */
532  fr_connection_watch_t watch, bool oneshot, void const *uctx)
533 {
534  if (state >= FR_CONNECTION_STATE_MAX) return NULL;
535 
536  return connection_add_watch(conn, &conn->watch_post[state], watch, oneshot, uctx);
537 }
538 
539 /** Enable a watcher
540  *
541  * @param[in] entry to enabled.
542  */
544 {
545  (void)talloc_get_type_abort(entry, fr_connection_watch_entry_t);
546  entry->enabled = true;
547 }
548 
549 /** Disable a watcher
550  *
551  * @param[in] entry to disable.
552  */
554 {
555  (void)talloc_get_type_abort(entry, fr_connection_watch_entry_t);
556  entry->enabled = false;
557 }
558 
559 /** Enable a watcher and replace the uctx
560  *
561  * @param[in] entry to enabled.
562  * @param[in] uctx Opaque data to pass to the callback.
563  */
565 {
566  (void)talloc_get_type_abort(entry, fr_connection_watch_entry_t);
567  entry->enabled = true;
568  memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
569 }
570 
571 /** Change the uctx of an entry
572  *
573  * @param[in] entry to enabled.
574  * @param[in] uctx Opaque data to pass to the callback.
575  */
577 {
578  (void)talloc_get_type_abort(entry, fr_connection_watch_entry_t);
579  memcpy(&entry->uctx, &uctx, sizeof(entry->uctx));
580 }
581 
582 /** Return the state of a watch entry
583  *
584  * @param[in] entry to return state of.
585  * @return
586  * - true if enabled.
587  * - false if disabled.
588  */
590 {
591  (void)talloc_get_type_abort(entry, fr_connection_watch_entry_t);
592  return entry->enabled;
593 }
594 
595 /** Return the number of times we've attempted to establish or re-establish this connection
596  *
597  * @param[in] conn to get count from.
598  * @return the number of times the connection has reconnected.
599  */
601 {
602  if (conn->pub.reconnected == 0) return 0; /* Has never been initialised */
603 
604  return conn->pub.reconnected - 1; /* We don't count the first connection attempt */
605 }
606 
607 /** Return the number of times this connection has timed out whilst connecting
608  *
609  * @param[in] conn to get count from.
610  * @return the number of times the connection has timed out whilst connecting.
611  */
613 {
614  return conn->pub.timed_out;
615 }
616 
617 /** The requisite period of time has passed, try and re-open the connection
618  *
619  * @param[in] el the time event occurred on.
620  * @param[in] now The current time.
621  * @param[in] uctx The #fr_connection_t the fd is associated with.
622  */
624 {
625  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_connection_t);
626 
627  switch (conn->pub.state) {
631  break;
632 
633  default:
635  break;
636  }
637 }
638 
639 /** Close the connection, then wait for another state change
640  *
641  */
643 {
644  switch (conn->pub.state) {
648  break;
649 
650  default:
652  return;
653  }
654 
656 
657  fr_event_timer_delete(&conn->ev);
658 
659  /*
660  * If there's a close callback, call it, so that the
661  * API client can free any resources associated
662  * with the connection handle.
663  */
664  WATCH_PRE(conn);
665 
666  /*
667  * is_closed is for pure paranoia. If everything
668  * is working correctly this state should never
669  * be entered if the connection is closed.
670  */
671  fr_assert(!conn->is_closed);
672  if (conn->close && !conn->is_closed) {
673  HANDLER_BEGIN(conn, conn->close);
674  DEBUG4("Calling close(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
675  conn->close(conn->pub.el, conn->pub.h, conn->uctx);
676  conn->is_closed = true; /* Ensure close doesn't get called twice if the connection is freed */
677  HANDLER_END(conn);
678  } else {
679  conn->is_closed = true;
680  }
681  WATCH_POST(conn);
682 }
683 
684 /** Connection timeout
685  *
686  * Connection wasn't opened within the configured period of time
687  *
688  * @param[in] el the time event occurred on.
689  * @param[in] now The current time.
690  * @param[in] uctx The #fr_connection_t the fd is associated with.
691  */
693 {
694  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_connection_t);
695 
697 }
698 
699 /** Gracefully shutdown the handle
700  *
701  */
703 {
705 
706  switch (conn->pub.state) {
708  break;
709 
710  default:
712  return;
713  }
714 
716 
717  WATCH_PRE(conn);
718  {
719  HANDLER_BEGIN(conn, conn->shutdown);
720  DEBUG4("Calling shutdown(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
721  ret = conn->shutdown(conn->pub.el, conn->pub.h, conn->uctx);
722  HANDLER_END(conn);
723  }
724  switch (ret) {
726  break;
727 
728  default:
730  return;
731  }
732  WATCH_POST(conn);
733 
734  /*
735  * If there's a connection timeout,
736  * set, then add the timer.
737  *
738  * The connection may be bad, in which
739  * case we want to automatically fail
740  * if it doesn't shutdown within the
741  * timeout period.
742  */
744  if (fr_event_timer_in(conn, conn->pub.el, &conn->ev,
745  conn->connection_timeout, _connection_timeout, conn) < 0) {
746  /*
747  * Can happen when the event loop is exiting
748  */
749  PERROR("Failed setting connection_timeout timer, closing connection");
751  }
752  }
753 }
754 
755 /** Connection failed
756  *
757  * Transition to the FR_CONNECTION_STATE_FAILED state.
758  *
759  * If the connection was open, or couldn't be opened wait for reconnection_delay before transitioning
760  * back to init.
761  *
762  * If no reconnection_delay was set, transition to halted.
763  *
764  * @param[in] conn that failed.
765  */
767 {
770 
772 
773  /*
774  * Explicit error occurred, delete the connection timer
775  */
776  fr_event_timer_delete(&conn->ev);
777 
778  /*
779  * Record what state the connection is currently in
780  * so we can figure out what to do next.
781  */
782  prev = conn->pub.state;
783 
784  /*
785  * Now transition to failed
786  */
788 
789  /*
790  * If there's a failed callback, give it the
791  * opportunity to suspend/destroy the
792  * connection.
793  */
794  WATCH_PRE(conn);
795  if (conn->failed) {
796  HANDLER_BEGIN(conn, conn->failed);
797  DEBUG4("Calling failed(h=%p, state=%s, uctx=%p)", conn->pub.h,
798  fr_table_str_by_value(fr_connection_states, prev, "<INVALID>"), conn->uctx);
799  ret = conn->failed(conn->pub.h, prev, conn->uctx);
800  HANDLER_END(conn);
801  }
802  WATCH_POST(conn);
803 
804  /*
805  * Enter the closed state if we failed during
806  * connecting, or when we were connected.
807  */
808  switch (prev) {
811  case FR_CONNECTION_STATE_TIMEOUT: /* Timeout means the connection progress past init */
812  case FR_CONNECTION_STATE_SHUTDOWN: /* Shutdown means the connection failed whilst shutting down */
814  break;
815 
816  default:
817  break;
818  }
819 
820  if (conn->failed) {
821  switch (ret) {
822  /*
823  * The callback signalled it wants the
824  * connection to be reinitialised
825  * after reconnection_delay, or
826  * immediately if the failure was due
827  * to a connection timeout.
828  */
830  break;
831 
832  /*
833  * The callback signalled it wants the
834  * connection to stop.
835  */
837  default:
839  return;
840  }
841  }
842 
843  /*
844  * What previous state we were in
845  * determines if we need to apply the
846  * reconnect timeout.
847  */
848  switch (prev) {
849  case FR_CONNECTION_STATE_INIT: /* Failed during initialisation */
850  case FR_CONNECTION_STATE_CONNECTED: /* Failed after connecting */
851  case FR_CONNECTION_STATE_CONNECTING: /* Failed during connecting */
852  case FR_CONNECTION_STATE_SHUTDOWN: /* Failed during shutdown */
854  DEBUG2("Delaying reconnection by %pVs", fr_box_time_delta(conn->reconnection_delay));
855  if (fr_event_timer_in(conn, conn->pub.el, &conn->ev,
856  conn->reconnection_delay, _reconnect_delay_done, conn) < 0) {
857  /*
858  * Can happen when the event loop is exiting
859  */
860  PERROR("Failed inserting reconnection_delay timer event, halting connection");
862  }
863  return;
864  }
865 
866  /*
867  * If there's no reconnection
868  * delay, then don't automatically
869  * reconnect, and wait to be
870  * signalled.
871  */
873  break;
874 
875  case FR_CONNECTION_STATE_TIMEOUT: /* Failed during connecting due to timeout */
877  break;
878 
879  default:
880  fr_assert(0);
881  }
882 }
883 
884 /** Enter the timeout state
885  *
886  * The connection took took long to open. Timeout the attempt and transition
887  * to the failed state.
888  */
890 {
891  switch (conn->pub.state) {
894  break;
895 
896  default:
898  }
899 
900  ERROR("Connection failed - timed out after %pVs", fr_box_time_delta(conn->connection_timeout));
901 
903 
904  conn->pub.timed_out++;
905 
907 }
908 
909 /** Enter the halted state
910  *
911  * Here we wait, until signalled by fr_connection_signal_reconnect.
912  */
914 {
915  fr_assert(conn->is_closed);
916 
917  switch (conn->pub.state) {
918  case FR_CONNECTION_STATE_FAILED: /* Init failure */
920  break;
921 
922  default:
924  }
925 
926  fr_event_timer_delete(&conn->ev);
927 
929  WATCH_PRE(conn);
930  WATCH_POST(conn);
931 }
932 
933 /** Enter the connected state
934  *
935  * The connection is now fully connected. At this point we call the open callback
936  * so that the API client can install its normal set of I/O callbacks to deal with
937  * sending/receiving actual data.
938  *
939  * After this, the connection will only transition states if an API client
940  * explicitly calls fr_connection_signal_reconnect.
941  *
942  * The connection API cannot monitor the connection for failure conditions.
943  *
944  * @param[in] conn Entering the connecting state.
945  */
947 {
948  int ret;
949 
951 
953 
954  fr_event_timer_delete(&conn->ev);
955  WATCH_PRE(conn);
956  if (conn->open) {
957  HANDLER_BEGIN(conn, conn->open);
958  DEBUG4("Calling open(el=%p, h=%p, uctx=%p)", conn->pub.el, conn->pub.h, conn->uctx);
959  ret = conn->open(conn->pub.el, conn->pub.h, conn->uctx);
960  HANDLER_END(conn);
961  } else {
963  }
964 
965  switch (ret) {
966  /*
967  * Callback agrees everything is connected
968  */
970  DEBUG2("Connection established");
971  WATCH_POST(conn); /* Only call if we successfully connected */
972  return;
973 
974  /*
975  * Open callback failed
976  */
978  default:
979  PERROR("Connection failed");
981  return;
982  }
983 }
984 
985 /** Enter the connecting state
986  *
987  * After this function returns we wait to be signalled with fr_connection_singal_connected
988  * or for the connection timer to expire.
989  *
990  * @param[in] conn Entering the connecting state.
991  */
993 {
994  switch (conn->pub.state) {
996  break;
997 
998  default:
1000  return;
1001  }
1002 
1004 
1005  WATCH_PRE(conn);
1006  WATCH_POST(conn);
1007 
1008  /*
1009  * If there's a connection timeout,
1010  * set, then add the timer.
1011  */
1013  if (fr_event_timer_in(conn, conn->pub.el, &conn->ev,
1014  conn->connection_timeout, _connection_timeout, conn) < 0) {
1015  PERROR("Failed setting connection_timeout event, failing connection");
1016 
1017  /*
1018  * This can happen when the event loop
1019  * is exiting.
1020  *
1021  * Entering fail will close partially
1022  * open connection and then, if we still
1023  * can't insert a timer, then the connection
1024  * will be halted and sit idle until its
1025  * freed.
1026  */
1028  }
1029  }
1030 }
1031 
1032 /** Initial state of the connection
1033  *
1034  * Calls the init function we were passed to allocate a library specific handle or
1035  * file descriptor.
1036  *
1037  * @param[in] conn To initialise.
1038  */
1040 {
1042 
1043  switch (conn->pub.state) {
1047  break;
1048 
1049  default:
1051  return;
1052  }
1053 
1054  /*
1055  * Increment every time we enter
1056  * We have to do this, as we don't know
1057  * whether the connection was halted by
1058  * the failed callback, and is now being
1059  * reconnected, or was automatically
1060  * reconnected.
1061  */
1062  conn->pub.reconnected++;
1063 
1065 
1066  /*
1067  * If we have an init callback, call it.
1068  */
1069  WATCH_PRE(conn);
1070  if (conn->init) {
1071  HANDLER_BEGIN(conn, conn->init);
1072  DEBUG4("Calling init(h_out=%p, conn=%p, uctx=%p)", &conn->pub.h, conn, conn->uctx);
1073  ret = conn->init(&conn->pub.h, conn, conn->uctx);
1074  HANDLER_END(conn);
1075  } else {
1077  }
1078 
1079  switch (ret) {
1081  conn->is_closed = false; /* We now have a handle */
1082  WATCH_POST(conn); /* Only call if we successfully initialised the handle */
1084  return;
1085 
1087  conn->is_closed = false; /* We now have a handle */
1088  WATCH_POST(conn); /* Only call if we successfully initialised the handle */
1090  return;
1091 
1092  /*
1093  * Initialisation callback failed
1094  */
1096  default:
1097  PERROR("Connection initialisation failed");
1099  break;
1100  }
1101 }
1102 
1103 /** Asynchronously signal a halted connection to start
1104  *
1105  */
1107 {
1108  DEBUG2("Signalled to start from %s state",
1109  fr_table_str_by_value(fr_connection_states, conn->pub.state, "<INVALID>"));
1110 
1111  if (DEFER_SIGNALS(conn)) {
1113  return;
1114  }
1115 
1116  switch (conn->pub.state) {
1119  break;
1120 
1121  default:
1122  break;
1123  }
1124 }
1125 
1126 /** Asynchronously signal that the connection is open
1127  *
1128  * Some libraries like libldap are extremely annoying and only return control
1129  * to the caller after a connection is open.
1130  *
1131  * For these libraries, we can't use an I/O handler to determine when the
1132  * connection is open so we rely on callbacks built into the library to
1133  * signal that the transition has occurred.
1134  *
1135  */
1137 {
1138  fr_assert(!conn->open); /* Use one or the other not both! */
1139 
1140  DEBUG2("Signalled connected from %s state",
1141  fr_table_str_by_value(fr_connection_states, conn->pub.state, "<INVALID>"));
1142 
1143  if (DEFER_SIGNALS(conn)) {
1145  return;
1146  }
1147 
1148  switch (conn->pub.state) {
1151  break;
1152 
1153  default:
1154  break;
1155  }
1156 }
1157 
1158 /** Asynchronously signal the connection should be reconnected
1159  *
1160  * Should be called if the caller has knowledge that the connection is bad
1161  * and should be reconnected.
1162  *
1163  * @param[in] conn to reconnect.
1164  * @param[in] reason Why the connection was signalled to reconnect.
1165  */
1167 {
1168  DEBUG2("Signalled to reconnect from %s state",
1169  fr_table_str_by_value(fr_connection_states, conn->pub.state, "<INVALID>"));
1170 
1171  if (DEFER_SIGNALS(conn)) {
1172  if ((reason == FR_CONNECTION_EXPIRED) && conn->shutdown) {
1174  return;
1175  }
1176 
1178  return;
1179  }
1180 
1181  switch (conn->pub.state) {
1182  case FR_CONNECTION_STATE_CLOSED: /* Don't circumvent reconnection_delay */
1183  case FR_CONNECTION_STATE_INIT: /* Already initialising */
1184  break;
1185 
1188  break;
1189 
1191  if (reason == FR_CONNECTION_EXPIRED) break; /* Already shutting down */
1193  break;
1194 
1196  if (reason == FR_CONNECTION_EXPIRED) {
1197  if (conn->shutdown) {
1199  break;
1200  }
1202  break;
1203  }
1204  FALL_THROUGH;
1205 
1210  break;
1211 
1213  fr_assert(0);
1214  return;
1215  }
1216 }
1217 
1218 /** Shuts down a connection gracefully
1219  *
1220  * If a shutdown function has been provided, it is called.
1221  * It's then up to the shutdown function to install I/O handlers to signal
1222  * when the connection has finished shutting down and should be closed
1223  * via #fr_connection_signal_halt.
1224  *
1225  * @param[in] conn to shutdown.
1226  */
1228 {
1229  DEBUG2("Signalled to shutdown from %s state",
1230  fr_table_str_by_value(fr_connection_states, conn->pub.state, "<INVALID>"));
1231 
1232  if (DEFER_SIGNALS(conn)) {
1234  return;
1235  }
1236 
1237  switch (conn->pub.state) {
1240  break;
1241 
1244  break;
1245 
1246  /*
1247  * If the connection is connected it needs to be
1248  * shutdown first.
1249  *
1250  * The shutdown callback or an FD event it inserts then
1251  * to signal that the connection should be closed.
1252  */
1254  if (conn->shutdown) {
1256  break;
1257  }
1258  FALL_THROUGH;
1259 
1260  /*
1261  * If the connection is any of these states it
1262  * must have completed INIT which means it has
1263  * an active handle which needs to be closed before
1264  * the connection is halted.
1265  */
1270  fr_assert(conn->is_closed);
1271 
1272  FALL_THROUGH;
1275  break;
1276 
1278  fr_assert(0);
1279  return;
1280  }
1281 }
1282 
1283 /** Shuts down a connection ungracefully
1284  *
1285  * If a connection is in an open or connection state it will be closed immediately.
1286  * Otherwise the connection will transition directly to the halted state.
1287  *
1288  * @param[in] conn to halt.
1289  */
1291 {
1292  DEBUG2("Signalled to halt from %s state",
1293  fr_table_str_by_value(fr_connection_states, conn->pub.state, "<INVALID>"));
1294 
1295  if (DEFER_SIGNALS(conn)) {
1297  return;
1298  }
1299 
1300  switch (conn->pub.state) {
1302  break;
1303 
1307  break;
1308 
1309  /*
1310  * If the connection is any of these states it
1311  * must have completed INIT which means it has
1312  * an active handle which needs to be closed before
1313  * the connection is halted.
1314  */
1321  fr_assert(conn->is_closed);
1323  break;
1324 
1326  fr_assert(0);
1327  return;
1328  }
1329 }
1330 /** Receive an error notification when we're connecting a socket
1331  *
1332  * @param[in] el event list the I/O event occurred on.
1333  * @param[in] fd the I/O even occurred for.
1334  * @param[in] flags from_kevent.
1335  * @param[in] fd_errno from kevent.
1336  * @param[in] uctx The #fr_connection_t this fd is associated with.
1337  */
1338 static void _connection_error(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, int fd_errno, void *uctx)
1339 {
1340  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_connection_t);
1341 
1342  ERROR("Connection failed for fd (%u): %s", fd, fr_syserror(fd_errno));
1344 }
1345 
1346 /** Receive a write notification after a socket is connected
1347  *
1348  * @param[in] el event list the I/O event occurred on.
1349  * @param[in] fd the I/O even occurred for.
1350  * @param[in] flags from kevent.
1351  * @param[in] uctx The #fr_connection_t this fd is associated with.
1352  */
1353 static void _connection_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1354 {
1355  fr_connection_t *conn = talloc_get_type_abort(uctx, fr_connection_t);
1356 
1359 }
1360 
1361 /** Remove the FD we were watching for connection open/fail from the event loop
1362  *
1363  */
1365  UNUSED fr_connection_state_t prev, fr_connection_state_t state, void *uctx)
1366 {
1367  int fd = *(talloc_get_type_abort(uctx, int));
1368 
1369  /*
1370  * Two states can trigger a cleanup
1371  * Remove the watch on the one that didn't
1372  */
1373  switch (state) {
1376  break;
1377 
1380  break;
1381 
1382  default:
1383  fr_assert(0);
1384  break;
1385  }
1386 
1388  talloc_free(uctx);
1389 }
1390 
1391 /** Setup the connection to change states to connected or failed based on I/O events
1392  *
1393  * Will automatically cleanup after itself, in preparation for
1394  * new I/O handlers to be installed in the open() callback.
1395  *
1396  * @return
1397  * - 0 on success.
1398  * - -1 on failure.
1399  */
1401 {
1402  int *fd_s;
1403 
1404  /*
1405  * If connection becomes writable we
1406  * assume it's open.
1407  */
1408  if (fr_event_fd_insert(conn, NULL, conn->pub.el, fd,
1409  NULL,
1412  conn) < 0) {
1413  PERROR("Failed inserting fd (%u) into event loop %p",
1414  fd, conn->pub.el);
1416  return -1;
1417  }
1418 
1419  /*
1420  * Stop the static analysis tools
1421  * complaining about assigning ints
1422  * to pointers.
1423  */
1424  MEM(fd_s = talloc_zero(conn, int));
1425  *fd_s = fd;
1426 
1427  /*
1428  * Add a oneshot watcher to remove
1429  * the I/O handlers if the connection
1430  * fails, or is connected.
1431  */
1433  _connection_signal_on_fd_cleanup, true, fd_s);
1435  _connection_signal_on_fd_cleanup, true, fd_s);
1436  return 0;
1437 }
1438 
1439 /** Close a connection if it's freed
1440  *
1441  * @param[in] conn to free.
1442  * @return
1443  * - 0 connection was freed immediately.
1444  * - 1 connection free was deferred.
1445  */
1447 {
1448  /*
1449  * Explicitly cancel any pending events
1450  */
1451  fr_event_timer_delete(&conn->ev);
1452 
1453  /*
1454  * Don't allow the connection to be
1455  * arbitrarily freed by a callback.
1456  *
1457  * Add a deferred signal to free the
1458  * connection later.
1459  */
1460  if (DEFER_SIGNALS(conn)) {
1462  return -1;
1463  }
1464 
1465  switch (conn->pub.state) {
1467  break;
1468 
1469  /*
1470  * Need to close the connection first
1471  */
1475  FALL_THROUGH;
1476 
1477  default:
1479  break;
1480  }
1481  return 0;
1482 }
1483 
1484 /** Allocate a new connection
1485  *
1486  * After the connection has been allocated, it should be started with a call to #fr_connection_signal_init.
1487  *
1488  * The connection state machine can detect when the connection is open in one of two ways.
1489  * - You can install a generic socket open/fail callback, using fr_connection_signal_on_fd.
1490  * - You can call either #fr_connection_signal_connected or fr_connection_signal_recommend.
1491  * This allows the connection state machine to work with more difficult library APIs,
1492  * which may not return control to the caller as connections are opened.
1493  *
1494  * @param[in] ctx to allocate connection handle in. If the connection
1495  * handle is freed, and the #fr_connection_state_t is
1496  * #FR_CONNECTION_STATE_CONNECTING or #FR_CONNECTION_STATE_CONNECTED the
1497  * close callback will be called.
1498  * @param[in] el to use for timer events, and to pass to the #fr_connection_open_t callback.
1499  * @param[in] funcs callback functions.
1500  * @param[in] conf our configuration.
1501  * @param[in] log_prefix To prepend to log messages.
1502  * @param[in] uctx User context to pass to callbacks.
1503  * @return
1504  * - A new #fr_connection_t on success.
1505  * - NULL on failure.
1506  */
1508  fr_connection_funcs_t const *funcs,
1509  fr_connection_conf_t const *conf,
1510  char const *log_prefix,
1511  void const *uctx)
1512 {
1513  size_t i;
1514  fr_connection_t *conn;
1515  uint64_t id;
1516 
1517  fr_assert(el);
1518 
1519  conn = talloc(ctx, fr_connection_t);
1520  if (!conn) return NULL;
1521  talloc_set_destructor(conn, _connection_free);
1522 
1524 
1525  *conn = (fr_connection_t){
1526  .pub = {
1527  .id = id,
1528  .state = FR_CONNECTION_STATE_HALTED,
1529  .el = el
1530  },
1531  .reconnection_delay = conf->reconnection_delay,
1532  .connection_timeout = conf->connection_timeout,
1533  .init = funcs->init,
1534  .open = funcs->open,
1535  .close = funcs->close,
1536  .failed = funcs->failed,
1537  .shutdown = funcs->shutdown,
1538  .is_closed = true, /* Starts closed */
1539  .pub.name = talloc_asprintf(conn, "%s - [%" PRIu64 "]", log_prefix, id)
1540  };
1541  memcpy(&conn->uctx, &uctx, sizeof(conn->uctx));
1542 
1543  for (i = 0; i < NUM_ELEMENTS(conn->watch_pre); i++) {
1545  }
1546  for (i = 0; i < NUM_ELEMENTS(conn->watch_post); i++) {
1548  }
1550 
1551  /*
1552  * Pre-allocate a on_halt watcher for deferred signal processing
1553  */
1556  fr_connection_watch_disable(conn->on_halted); /* Start disabled */
1557 
1558  return conn;
1559 }
#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
fr_connection_shutdown_t shutdown
Definition: connection.h:189
uint64_t _CONST id
Unique identifier for the connection.
Definition: connection.h:72
uint64_t _CONST timed_out
How many times has this connection timed out when connecting.
Definition: connection.h:78
fr_connection_state_t(* fr_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
fr_connection_state_t
Definition: connection.h:45
@ FR_CONNECTION_STATE_CLOSED
Connection has been closed.
Definition: connection.h:55
@ FR_CONNECTION_STATE_HALTED
The connection is in a halted stat.
Definition: connection.h:46
@ FR_CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ FR_CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ FR_CONNECTION_STATE_TIMEOUT
Timeout during FR_CONNECTION_STATE_CONNECTING.
Definition: connection.h:51
@ FR_CONNECTION_STATE_INIT
Init state, sets up connection.
Definition: connection.h:49
@ FR_CONNECTION_STATE_SHUTDOWN
Connection is shutting down.
Definition: connection.h:53
@ FR_CONNECTION_STATE_MAX
Definition: connection.h:56
@ FR_CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
void(* fr_connection_watch_t)(fr_connection_t *conn, fr_connection_state_t prev, fr_connection_state_t state, void *uctx)
Receive a notification when a connection enters a particular state.
Definition: connection.h:204
void(* fr_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_connection_state_t _CONST state
Current connection state.
Definition: connection.h:70
fr_connection_init_t init
Definition: connection.h:187
fr_connection_state_t(* fr_connection_init_t)(void **h_out, fr_connection_t *conn, void *uctx)
Callback for the initialise state.
Definition: connection.h:116
fr_connection_state_t(* fr_connection_failed_t)(void *h, fr_connection_state_t state, void *uctx)
Notification that a connection attempt has failed.
Definition: connection.h:167
fr_connection_state_t(* fr_connection_open_t)(fr_event_list_t *el, void *h, void *uctx)
Notification that the connection is now open.
Definition: connection.h:130
fr_connection_failed_t failed
Definition: connection.h:190
fr_connection_close_t close
Definition: connection.h:191
fr_connection_open_t open
Definition: connection.h:188
void *_CONST h
Connection handle.
Definition: connection.h:73
fr_event_list_t *_CONST el
Event list for timers and I/O events.
Definition: connection.h:74
fr_connection_reason_t
Definition: connection.h:83
@ FR_CONNECTION_EXPIRED
Connection is being reconnected because it's at the end of its life.
Definition: connection.h:85
@ FR_CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
uint64_t _CONST reconnected
How many times we've attempted to establish or re-establish this connection.
Definition: connection.h:76
fr_connection_state_t _CONST prev
The previous state the connection was in.
Definition: connection.h:71
Holds a complete set of functions for a connection.
Definition: connection.h:186
Public fields for the connection.
Definition: connection.h:67
#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
#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:1604
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:1253
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
static void connection_state_enter_init(fr_connection_t *conn)
Initial state of the connection.
Definition: connection.c:1039
bool processing_signals
Processing deferred signals, don't let the deferred signal processor be called multiple times.
Definition: connection.c:94
fr_dlist_head_t watch_pre[FR_CONNECTION_STATE_MAX]
Function called before state callback.
Definition: connection.c:97
void fr_connection_signals_pause(fr_connection_t *conn)
Pause processing of deferred signals.
Definition: connection.c:310
int fr_connection_del_watch_pre(fr_connection_t *conn, fr_connection_state_t state, fr_connection_watch_t watch)
Remove a watch function from a pre list.
Definition: connection.c:454
void fr_connection_watch_enable(fr_connection_watch_entry_t *entry)
Enable a watcher.
Definition: connection.c:543
static void connection_state_enter_timeout(fr_connection_t *conn)
Enter the timeout state.
Definition: connection.c:889
fr_connection_close_t close
Callback to close a connection.
Definition: connection.c:103
fr_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
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:692
fr_connection_failed_t failed
Callback for 'failed' notification.
Definition: connection.c:105
static void connection_state_enter_closed(fr_connection_t *conn)
Close the connection, then wait for another state change.
Definition: connection.c:642
#define DEFER_SIGNALS(_conn)
Definition: connection.c:151
static fr_table_num_ordered_t const connection_dsignals[]
Definition: connection.c:166
uint64_t fr_connection_get_num_timed_out(fr_connection_t const *conn)
Return the number of times this connection has timed out whilst connecting.
Definition: connection.c:612
bool is_closed
The close callback has previously been called.
Definition: connection.c:93
static fr_connection_watch_entry_t * connection_add_watch(fr_connection_t *conn, fr_dlist_head_t *list, fr_connection_watch_t watch, bool oneshot, void const *uctx)
Add a watch entry to the pre/post[state] list.
Definition: connection.c:481
fr_connection_watch_entry_t * fr_connection_add_watch_post(fr_connection_t *conn, fr_connection_state_t state, fr_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:531
static void connection_state_enter_failed(fr_connection_t *conn)
Connection failed.
Definition: connection.c:766
struct fr_connection_s fr_connection_t
Definition: connection.c:27
fr_dlist_head_t deferred_signals
A list of signals we received whilst we were in a handler.
Definition: connection.c:114
static atomic_uint_fast64_t connection_counter
Definition: connection.c:73
void fr_connection_signal_connected(fr_connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1136
static size_t connection_dsignals_len
Definition: connection.c:175
void fr_connection_signal_halt(fr_connection_t *conn)
Shuts down a connection ungracefully.
Definition: connection.c:1290
#define WATCH_POST(_conn)
Call the post handler watch functions.
Definition: connection.c:405
void fr_connection_signals_resume(fr_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
fr_event_timer_t const * ev
State transition timer.
Definition: connection.c:107
static void connection_state_enter_halted(fr_connection_t *conn)
Enter the halted state.
Definition: connection.c:913
void fr_connection_signal_init(fr_connection_t *conn)
Asynchronously signal a halted connection to start.
Definition: connection.c:1106
fr_connection_watch_entry_t * next_watcher
Hack to insulate watcher iterator from deletions.
Definition: connection.c:99
fr_connection_watch_t func
Function to call when a connection enters the state this list belongs to.
Definition: connection.c:80
size_t fr_connection_states_len
Definition: connection.c:56
static void connection_deferred_signal_add(fr_connection_t *conn, connection_dsignal_t signal)
Add a deferred signal to the signal list.
Definition: connection.c:209
#define WATCH_PRE(_conn)
Call the pre handler watch functions.
Definition: connection.c:392
static void connection_watch_call(fr_connection_t *conn, fr_dlist_head_t *list)
Call a list of watch functions associated with a state.
Definition: connection.c:356
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:1338
fr_connection_open_t open
Callback for 'open' notification.
Definition: connection.c:102
fr_connection_shutdown_t shutdown
Signal the connection handle to start shutting down.
Definition: connection.c:104
void * uctx
User data.
Definition: connection.c:90
struct fr_connection_pub_s pub
Public fields.
Definition: connection.c:88
fr_connection_watch_entry_t * fr_connection_add_watch_pre(fr_connection_t *conn, fr_connection_state_t state, fr_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:509
void fr_connection_watch_enable_set_uctx(fr_connection_watch_entry_t *entry, void const *uctx)
Enable a watcher and replace the uctx.
Definition: connection.c:564
bool oneshot
Remove the function after it's called once.
Definition: connection.c:82
static void _deferred_signal_connection_on_halted(UNUSED fr_connection_t *conn, UNUSED fr_connection_state_t prev, UNUSED fr_connection_state_t state, void *uctx)
Notification function to tell connection_deferred_signal_process that the connection has been freed.
Definition: connection.c:226
unsigned int signals_pause
Temporarily stop processing of signals.
Definition: connection.c:123
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
static void connection_state_enter_connected(fr_connection_t *conn)
Enter the connected state.
Definition: connection.c:946
static void connection_state_enter_connecting(fr_connection_t *conn)
Enter the connecting state.
Definition: connection.c:992
void fr_connection_signal_shutdown(fr_connection_t *conn)
Shuts down a connection gracefully.
Definition: connection.c:1227
static void connection_state_enter_shutdown(fr_connection_t *conn)
Gracefully shutdown the handle.
Definition: connection.c:702
#define HANDLER_END(_conn)
Called when we exit a handler.
Definition: connection.c:346
#define STATE_TRANSITION(_new)
Definition: connection.c:133
static size_t fr_connection_trigger_names_len
Definition: connection.c:71
fr_dlist_t entry
Entry in the signals list.
Definition: connection.c:181
fr_time_delta_t connection_timeout
How long to wait in the FR_CONNECTION_STATE_CONNECTING state.
Definition: connection.c:109
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:1353
uint64_t fr_connection_get_num_reconnected(fr_connection_t const *conn)
Return the number of times we've attempted to establish or re-establish this connection.
Definition: connection.c:600
fr_time_delta_t reconnection_delay
How long to wait in the FR_CONNECTION_STATE_FAILED state.
Definition: connection.c:111
static void connection_deferred_signal_process(fr_connection_t *conn)
Process any deferred signals.
Definition: connection.c:237
int fr_connection_del_watch_post(fr_connection_t *conn, fr_connection_state_t state, fr_connection_watch_t watch)
Remove a watch function from a post list.
Definition: connection.c:471
static void _connection_signal_on_fd_cleanup(fr_connection_t *conn, UNUSED fr_connection_state_t prev, fr_connection_state_t state, void *uctx)
Remove the FD we were watching for connection open/fail from the event loop.
Definition: connection.c:1364
fr_connection_init_t init
Callback for initialising a connection.
Definition: connection.c:101
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:623
#define BAD_STATE_TRANSITION(_new)
Definition: connection.c:143
bool fr_connection_watch_is_enabled(fr_connection_watch_entry_t *entry)
Return the state of a watch entry.
Definition: connection.c:589
struct fr_connection_watch_entry_s fr_connection_watch_entry_t
An entry in a watch function list.
void * uctx
User data to pass to the function.
Definition: connection.c:84
int fr_connection_signal_on_fd(fr_connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
Definition: connection.c:1400
fr_connection_t * fr_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_connection_funcs_t const *funcs, fr_connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
Definition: connection.c:1507
fr_dlist_t entry
List entry.
Definition: connection.c:79
static int _connection_free(fr_connection_t *conn)
Close a connection if it's freed.
Definition: connection.c:1446
static fr_table_num_indexed_t const fr_connection_trigger_names[]
Map connection states to trigger names.
Definition: connection.c:61
void fr_connection_watch_disable(fr_connection_watch_entry_t *entry)
Disable a watcher.
Definition: connection.c:553
fr_table_num_ordered_t const fr_connection_states[]
Definition: connection.c:46
fr_dlist_head_t watch_post[FR_CONNECTION_STATE_MAX]
Function called after state callback.
Definition: connection.c:98
void fr_connection_watch_set_uctx(fr_connection_watch_entry_t *entry, void const *uctx)
Change the uctx of an entry.
Definition: connection.c:576
static int connection_del_watch(fr_connection_t *conn, fr_dlist_head_t *state_lists, fr_connection_state_t state, fr_connection_watch_t watch)
Remove a watch function from a pre/post[state] list.
Definition: connection.c:418
void fr_connection_signal_reconnect(fr_connection_t *conn, fr_connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1166
bool enabled
Whether the watch entry is enabled.
Definition: connection.c:83
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:253
An element in a table indexed by numeric value.
Definition: table.h:88
An element in an arbitrarily ordered array of name to num mappings.
Definition: table.h:53
#define fr_time_delta_ispos(_a)
Definition: time.h:288
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:336