36#define LOG_PREFIX "sql - cassandra"
38#include <freeradius-devel/server/base.h>
39#include <freeradius-devel/util/debug.h>
41#ifdef HAVE_WDOCUMENTATION
47#ifdef HAVE_WDOCUMENTATION
168 {
L(
"all"), CASS_CONSISTENCY_ALL },
169 {
L(
"any"), CASS_CONSISTENCY_ANY },
170 {
L(
"each_quorum"), CASS_CONSISTENCY_EACH_QUORUM },
171 {
L(
"local_one"), CASS_CONSISTENCY_LOCAL_ONE },
172 {
L(
"local_quorum"), CASS_CONSISTENCY_LOCAL_QUORUM },
173 {
L(
"one"), CASS_CONSISTENCY_ONE },
174 {
L(
"quorum"), CASS_CONSISTENCY_QUORUM },
175 {
L(
"three"), CASS_CONSISTENCY_THREE },
176 {
L(
"two"), CASS_CONSISTENCY_TWO }
181 {
L(
"identity"), CASS_SSL_VERIFY_PEER_IDENTITY },
182 {
L(
"no"), CASS_SSL_VERIFY_NONE },
183 {
L(
"yes"), CASS_SSL_VERIFY_PEER_CERT }
251 switch (message->severity) {
252 case CASS_LOG_CRITICAL:
255 ERROR(
"%s[%d] %s: %s",
256 message->file, message->line, message->function, message->message);
258 ERROR(
"%s", message->message);
264 WARN(
"%s[%d] %s: %s",
265 message->file, message->line, message->function, message->message);
267 WARN(
"%s", message->message);
272 case CASS_LOG_DISABLED:
273 case CASS_LOG_LAST_ENTRY:
275 INFO(
"%s[%d] %s: %s",
276 message->file, message->line, message->function, message->message);
278 INFO(
"%s", message->message);
287 message->file, message->line, message->function, message->message);
289 DEBUG2(
"%s", message->message);
318 CC_HINT(format (printf, 3, 4));
325 cass_query_ctx->
error.
msg = talloc_vasprintf(ctx,
fmt, ap);
334 DEBUG2(
"Socket destructor called, closing socket");
353 .log_ctx = talloc_pool(c, 2048),
354 .poll_interval = 1000
364 if (!
inst->mutable->done_connect_keyspace) {
368 pthread_mutex_lock(&
inst->mutable->connect_mutex);
369 if (!
inst->mutable->done_connect_keyspace) {
376 DEBUG2(
"Connecting to Cassandra cluster");
377 future = cass_session_connect_keyspace(
inst->session,
inst->cluster,
config->sql_db);
378 ret = cass_future_error_code(future);
379 if (ret != CASS_OK) {
383 cass_future_error_message(future, &
msg, &msg_len);
384 ERROR(
"Unable to connect: [%x] %s", (
int)ret,
msg);
385 cass_future_free(future);
386 pthread_mutex_unlock(&
inst->mutable->connect_mutex);
390 cass_future_free(future);
391 inst->mutable->done_connect_keyspace =
true;
393 pthread_mutex_unlock(&
inst->mutable->connect_mutex);
412 CassStatement *statement;
422 switch (query_ctx->
status) {
425 statement = cass_statement_new_n(query_ctx->
query_str, talloc_array_length(query_ctx->
query_str) - 1, 0);
426 if (
inst->consistency_str) cass_statement_set_consistency(statement,
inst->consistency);
433 query_ctx->
uctx = cass_query_ctx;
439 cass_query->
future = cass_session_execute(
inst->session, statement);
441 cass_statement_free(statement);
448 query_ctx->
tconn = tconn;
466 bool handled =
false;
473 if (!cass_future_ready(next_query->
future))
goto next;
475 cass_query = next_query;
483 ret = cass_future_error_code(cass_query->
future);
484 if (ret != CASS_OK) {
488 cass_future_error_message(cass_query->
future, &error, &len);
492 case CASS_ERROR_SERVER_SYNTAX_ERROR:
493 case CASS_ERROR_SERVER_INVALID_QUERY:
501 cass_future_free(cass_query->
future);
507 cass_query_ctx->
result = cass_future_get_result(cass_query->
future);
508 cass_future_free(cass_query->
future);
541 ERROR(
"Unable to insert polling event");
577 ERROR(
"Unable to insert polling event");
587 ERROR(
"Unable to insert polling event");
604 if (!query_ctx->
treq)
return;
613 if (cass_query->
query_ctx == query_ctx) {
615 cass_future_free(cass_query->
future);
625 return cass_query_ctx->
result ? cass_result_row_count(cass_query_ctx->
result) : 0;
631 CassResult
const *result = cass_query_ctx->
result;
633 unsigned int fields, i;
636 fields = result ? cass_result_column_count(result) : 0;
639 MEM(
names = talloc_array(query_ctx,
char const *, fields));
641 for (i = 0; i < fields; i++) {
642 const char *col_name;
646 if (cass_result_column_name(result, i, &col_name, &col_name_len) != CASS_OK) {
647 col_name =
"<INVALID>";
661 CassRow
const *cass_row;
663 CassResult
const *result = cass_query_ctx->
result;
667#define RLM_CASS_ERR_DATA_RETRIVE(_t) \
669 char const *_col_name;\
670 size_t _col_name_len;\
672 if ((_ret = cass_result_column_name(result, i, &_col_name, &_col_name_len)) != CASS_OK) {\
673 _col_name = "<INVALID>";\
675 sql_set_query_error_printf(conn->log_ctx, cass_query_ctx, "Failed to retrieve " _t " data at column %s (%d): %s", \
676 _col_name, i, cass_error_desc(_ret));\
677 TALLOC_FREE(query_ctx->row);\
678 query_ctx->rcode = RLM_SQL_ERROR;\
694 TALLOC_FREE(query_ctx->
row);
696 if (!cass_iterator_next(conn->
iterator)) {
701 cass_row = cass_iterator_get_row(conn->
iterator);
702 fields = cass_result_column_count(result);
704 MEM(row = query_ctx->
row = talloc_zero_array(query_ctx,
char *, fields + 1));
706 for (i = 0; i < fields; i++) {
707 CassValue
const *
value;
710 value = cass_row_get_column(cass_row, i);
712 if (cass_value_is_null(
value) == cass_true)
continue;
716 case CASS_VALUE_TYPE_ASCII:
717 case CASS_VALUE_TYPE_TEXT:
718 case CASS_VALUE_TYPE_VARCHAR:
725 MEM(row[i] = talloc_array(row,
char, len + 1));
726 memcpy(row[i], str, len);
731 case CASS_VALUE_TYPE_BOOLEAN:
737 MEM(row[i] = talloc_zero_array(row,
char, 2));
738 row[i][0] = (bv == cass_false) ?
'0' :
'1';
742 case CASS_VALUE_TYPE_INT:
752 case CASS_VALUE_TYPE_TIMESTAMP:
753 case CASS_VALUE_TYPE_BIGINT:
763 case CASS_VALUE_TYPE_UUID:
764 case CASS_VALUE_TYPE_TIMEUUID:
769 MEM(row[i] = talloc_array(row,
char, CASS_UUID_STRING_LENGTH));
770 cass_uuid_string(uuid, row[i]);
776 const char *col_name;
779 if (cass_result_column_name(result, i, &col_name,
780 &col_name_len) != CASS_OK) col_name =
"<INVALID>";
783 "Failed to retrieve data at column %s (%d): Unsupported data type",
799 if (query_ctx->
row) TALLOC_FREE(query_ctx->
row);
806 if (query_ctx->
uctx) {
808 if (cass_query_ctx->
result) cass_result_free(cass_query_ctx->
result);
809 cass_query_ctx->
result = NULL;
820 if (cass_query_ctx->
error.
msg && (outlen >= 1)) {
834 cass_result_free(cass_query_ctx->
result);
856 if (
inst->ssl) cass_ssl_free(
inst->ssl);
857 if (
inst->session) cass_session_free(
inst->session);
858 if (
inst->cluster) cass_cluster_free(
inst->cluster);
860 pthread_mutex_destroy(&
inst->mutable->connect_mutex);
872 bool do_latency_aware_routing =
false;
873 CassCluster *cluster;
876#define DO_CASS_OPTION(_opt, _x) \
879 if ((_ret = (_x)) != CASS_OK) {\
880 ERROR("Error setting " _opt ": %s", cass_error_desc(_ret));\
886 if ((ret = pthread_mutex_init(&
inst->mutable->connect_mutex, NULL)) < 0) {
899 DEBUG4(
"Configuring CassCluster structure");
900 cluster =
inst->cluster = cass_cluster_new();
901 if (!cluster)
return -1;
914 if (
config->sql_login &&
config->sql_password) cass_cluster_set_credentials(cluster,
config->sql_login,
920 if (
inst->consistency_str) {
924 if (consistency < 0) {
925 ERROR(
"Invalid consistency level \"%s\"",
inst->consistency_str);
928 inst->consistency = (CassConsistency)consistency;
931 if (
inst->protocol_version) {
933 cass_cluster_set_protocol_version(
inst->cluster,
inst->protocol_version));
936 if (
inst->connections_per_host) {
938 cass_cluster_set_core_connections_per_host(
inst->cluster,
939 inst->connections_per_host));
942 if (
inst->event_queue_size) {
944 cass_cluster_set_num_threads_io(
inst->cluster,
inst->event_queue_size));
947 if (
inst->io_queue_size) {
949 cass_cluster_set_num_threads_io(
inst->cluster,
inst->io_queue_size));
952 if (
inst->io_threads) {
956 if (
inst->load_balance_round_robin) cass_cluster_set_load_balance_round_robin(
inst->cluster);
958 cass_cluster_set_token_aware_routing(
inst->cluster,
inst->token_aware_routing);
960 if (
inst->lbdc_local_dc) {
962 cass_cluster_set_load_balance_dc_aware(
inst->cluster,
964 inst->lbdc_hosts_per_remote_dc,
965 inst->lbdc_allow_remote_dcs_for_local_cl));
968 if (do_latency_aware_routing) {
970 cass_cluster_set_latency_aware_routing(
inst->cluster,
true);
973 cass_cluster_set_latency_aware_routing_settings(
inst->cluster,
974 (cass_double_t)
inst->lar_exclusion_threshold,
978 inst->lar_min_measured);
981 if (
inst->tcp_keepalive) cass_cluster_set_tcp_keepalive(
inst->cluster,
true,
inst->tcp_keepalive);
982 cass_cluster_set_tcp_nodelay(
inst->cluster,
inst->tcp_nodelay);
987 ssl =
inst->ssl = cass_ssl_new();
990 if (
inst->tls_verify_cert_str) {
994 if (verify_cert < 0) {
995 ERROR(
"Invalid certificate validation type \"%s\", "
996 "must be one of 'yes', 'no', 'identity'",
inst->tls_verify_cert_str);
999 cass_ssl_set_verify_flags(ssl, verify_cert);
1004 if (
inst->tls_ca_file) {
1008 if (
inst->tls_certificate_file) {
1009 DO_CASS_OPTION(
"certificate_file", cass_ssl_set_cert(ssl,
inst->tls_certificate_file));
1012 if (
inst->tls_private_key_file) {
1013 DO_CASS_OPTION(
"private_key", cass_ssl_set_private_key(ssl,
inst->tls_private_key_file,
1014 inst->tls_private_key_password));
1017 cass_cluster_set_ssl(cluster, ssl);
1020 inst->session = cass_session_new();
1021 if (!
inst->session)
return -1;
1028 INFO(
"Built against libcassandra version %d.%d.%d%s",
1029 CASS_VERSION_MAJOR, CASS_VERSION_MINOR, CASS_VERSION_PATCH, CASS_VERSION_SUFFIX);
1034 cass_log_set_level(CASS_LOG_INFO);
1044 .name =
"sql_cassandra",
1053 .sql_query_resume = sql_query_resume,
1054 .sql_select_query_resume = sql_query_resume,
1064 .connection_alloc = sql_trunk_connection_alloc,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
static int const char * fmt
#define L(_str)
Helper for initialising arrays of string literals.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define CC_NO_UBSAN(_sanitize)
#define CONF_PARSER_TERMINATOR
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
conf_parser_flags_t flags
Flags which control parsing behaviour.
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
@ CONF_FLAG_OK_MISSING
OK if it's missing.
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Defines a CONF_PAIR to C data type mapping.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Head of a doubly linked list.
Entry in a doubly linked list.
#define fr_event_timer_in(...)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Stores all information relating to an event list.
module_instance_t * mi
Module instance to detach.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for detach calls.
Temporary structure to hold arguments for instantiation calls.
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
static const conf_parser_t config[]
#define RETURN_MODULE_FAIL
rlm_rcode_t
Return codes indicating the result of the module call.
static int instantiate(module_inst_ctx_t const *mctx)
Prototypes and functions for the SQL module.
char const * msg
Log message.
fr_log_type_t type
Type of log entry L_ERR, L_WARN, L_INFO, L_DBG etc.
fr_sql_query_status_t status
Status of the query.
trunk_connection_t * tconn
Trunk connection this query is being run on.
#define RLM_SQL_MULTI_QUERY_CONN
Can support multiple queries on a single connection.
void * uctx
Driver specific data.
char const * query_str
Query string to run.
request_t * request
Request this query relates to.
sql_rcode_t
Action to take at end of an SQL query.
@ RLM_SQL_QUERY_INVALID
Query syntax error.
@ RLM_SQL_ERROR
General connection/server error.
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
fr_time_delta_t query_timeout
How long to allow queries to run for.
rlm_sql_row_t row
Row data from the last query.
sql_rcode_t rcode
Result code.
trunk_request_t * treq
Trunk request for this query.
@ SQL_QUERY_SUBMITTED
Submitted for execution.
@ SQL_QUERY_PREPARED
Ready to submit.
static int mod_detach(module_detach_ctx_t const *mctx)
static int mod_load(void)
bool done_connect_keyspace
Whether we've connected to a keyspace.
static void sql_set_query_error_printf(TALLOC_CTX *ctx, cassandra_query_ctx_t *cass_query_ctx, char const *fmt,...))
Store the last error associated with a query, using a format string.
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
#define DO_CASS_OPTION(_opt, _x)
fr_event_timer_t const * write_ev
Polling event for sending queries.
char const * lbdc_local_dc
The primary data center to try first.
uint poll_count
How many consecutive polls had no available results.
sql_log_entry_t error
Most recent Cassandra error message for this query.
fr_dlist_head_t queries
Outstanding queries on this connection.
static SQL_TRUNK_CONNECTION_ALLOC void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
uint32_t connections_per_host
Number of connections to each server in each IO thread.
static void sql_trunk_connection_write_poll(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
uint32_t protocol_version
The protocol version.
fr_event_timer_t const * read_ev
Polling event for reading query results.
fr_time_delta_t lar_update_rate
The rate at which the best average latency is recomputed.
pthread_mutex_t connect_mutex
Mutex to prevent multiple connections attempting to connect to a keyspace concurrently.
CassConsistency consistency
Level of consistency converted to a constant.
static conf_parser_t load_balance_dc_aware_config[]
CassSsl * ssl
Connection's SSL context.
static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
static conf_parser_t latency_aware_routing_config[]
CassSession * session
Cluster's connection pool.
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
uint32_t lbdc_hosts_per_remote_dc
The number of host used in each remote DC if no hosts are available in the local dc.
static conf_parser_t tls_config[]
bool lbdc_allow_remote_dcs_for_local_cl
Allows remote hosts to be used if no local.
static int sql_num_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
char const * tls_ca_file
Path to the CA used to validate the server's certificate.
static void sql_set_query_error(TALLOC_CTX *ctx, cassandra_query_ctx_t *cass_query_ctx, char const *message, size_t len)
Store the last error associated with a query.
static int sql_affected_rows(UNUSED fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
CassFuture * future
Future produced when submitting query.
CassCluster * cluster
Configuration of the cassandra cluster connection.
bool tcp_nodelay
Disable TCP naggle algorithm.
char const * tls_verify_cert_str
Whether we validate the cert provided by the server.
static size_t sql_error(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, fr_sql_query_t *query_ctx)
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static fr_table_num_sorted_t const consistency_levels[]
char const * tls_certificate_file
Public certificate we present to the server.
uint32_t event_queue_size
Sets the size of the the fixed size queue that stores events.
double lar_exclusion_threshold
How much worse the latency me be, compared to the average latency of the best performing node before ...
char const * tls_private_key_password
String to decrypt private key.
uint poll_interval
Interval between read polling.
static const conf_parser_t driver_config[]
#define RLM_CASS_ERR_DATA_RETRIVE(_t)
CassResult const * result
Cassandra result handle.
static size_t consistency_levels_len
fr_sql_query_t * query_ctx
SQL query ctx.
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
bool load_balance_round_robin
Enable round robin load balancing.
char const * consistency_str
Level of consistency required.
uint32_t io_queue_size
Size of the the fixed size queue that stores pending requests.
fr_dlist_t entry
Entry in list of outstanding queries.
fr_time_delta_t lar_scale
Weight given to older latencies when calculating the average latency of a node.
static size_t verify_cert_table_len
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static fr_table_num_sorted_t const verify_cert_table[]
connection_t * conn
Generic connection structure for managing this handle.
rlm_sql_config_t const * config
SQL instance config.
bool token_aware_routing
Whether to use token aware routing.
uint32_t tcp_keepalive
How often to send TCP keepalives.
static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
CassIterator * iterator
Row set iterator.
char const * tls_private_key_file
Private key for the certificate we present to the server.
static void sql_trunk_connection_read_poll(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
TALLOC_CTX * log_ctx
Prevent unneeded memory allocation by keeping a permanent pool, to store log entries.
static void _rlm_sql_cassandra_log(CassLogMessage const *message, UNUSED void *data)
Log callback for libcassandra.
uint32_t io_threads
Number of IO threads.
static int mod_instantiate(module_inst_ctx_t const *mctx)
rlm_sql_cassandra_t const * inst
Module instance for this connection.
fr_time_delta_t lar_retry_period
The amount of time a node is penalized by the policy before being given a second chance when the curr...
rlm_sql_driver_t rlm_sql_cassandra
uint64_t lar_min_measured
The minimum number of measurements per-host required to be considered by the policy.
SQL_QUERY_RESUME static SQL_QUERY_FAIL void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
Driver specific data to attach to the query ctx.
Structure for tracking outstanding queries.
Cassandra cluster connection.
Cassandra driver instance.
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
CONF_SECTION * conf
Module's instance configuration.
size_t inst_size
Size of the module's instance data.
void * data
Module's instance data.
module_instance_t const * parent
Parent module's instance (if any).
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
module_t common
Common fields for all loadable modules.
module_instance_t * driver_submodule
Driver's submodule.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
An element in a lexicographically sorted array of name to num mappings.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
#define talloc_get_type_abort_const
static int talloc_const_free(void const *ptr)
Free const'd memory.
static const char * names[8]
#define fr_time_delta_ispos(_a)
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
A time delta, a difference in time measured in nanoseconds.
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Associates request queues with a connection.
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
trunk_cancel_reason_t
Reasons for a request being cancelled.
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
static fr_event_list_t * el
static size_t char ** out