The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
sql.c
Go to the documentation of this file.
1/*
2 * sql.c rlm_sql - FreeRADIUS SQL Module
3 * Main code directly taken from ICRADIUS
4 *
5 * Version: $Id: bc30fc0e35ce32e30a4485b0d590d8916b5d7cd1 $
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 *
21 * @copyright 2001,2006 The FreeRADIUS server project
22 * @copyright 2000 Mike Machado (mike@innercite.com)
23 * @copyright 2000 Alan DeKok (aland@freeradius.org)
24 * @copyright 2001 Chad Miller (cmiller@surfsouth.com)
25 */
26
27RCSID("$Id: bc30fc0e35ce32e30a4485b0d590d8916b5d7cd1 $")
28
29#define LOG_PREFIX inst->name
30
31#include <freeradius-devel/server/base.h>
32#include <freeradius-devel/util/debug.h>
33
34#include <sys/file.h>
35#include <sys/stat.h>
36
37#include <ctype.h>
38
39#include "rlm_sql.h"
40
41/*
42 * Translate rlm_sql rcodes to humanly
43 * readable reason strings.
44 */
46 { L("need alt query"), RLM_SQL_ALT_QUERY },
47 { L("no connection"), RLM_SQL_RECONNECT },
48 { L("no more rows"), RLM_SQL_NO_MORE_ROWS },
49 { L("query invalid"), RLM_SQL_QUERY_INVALID },
50 { L("server error"), RLM_SQL_ERROR },
51 { L("success"), RLM_SQL_OK }
52};
54
56 { L("alternate"), RLM_SQL_ALT_QUERY },
57 { L("empty"), RLM_SQL_NO_MORE_ROWS },
58 { L("error"), RLM_SQL_ERROR },
59 { L("invalid"), RLM_SQL_QUERY_INVALID },
60 { L("ok"), RLM_SQL_OK },
61 { L("reconnect"), RLM_SQL_RECONNECT }
62};
64
65/** Call the driver's sql_fetch_row function
66 *
67 * Calls the driver's sql_fetch_row logging any errors. On success, will
68 * write row data to ``uctx->row``.
69 *
70 * The rcode within the query context is updated to
71 * - #RLM_SQL_OK on success.
72 * - other #sql_rcode_t constants on error.
73 *
74 * @param p_result Result of current module call.
75 * @param request Current request.
76 * @param uctx query context containing query to execute.
77 * @return an unlang_action_t.
78 */
80{
81 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
82 rlm_sql_t const *inst = query_ctx->inst;
83
84 if (!query_ctx->tconn) {
85 ROPTIONAL(RERROR, ERROR, "Invalid connection");
86 query_ctx->rcode = RLM_SQL_ERROR;
88 }
89
90 /*
91 * We can't implement reconnect logic here, because the caller
92 * may require the original connection to free up queries or
93 * result sets associated with that connection.
94 */
95 (inst->driver->sql_fetch_row)(p_result, request, query_ctx);
96 switch (query_ctx->rcode) {
97 case RLM_SQL_OK:
98 fr_assert(query_ctx->row != NULL);
100
102 fr_assert(query_ctx->row == NULL);
104
105 default:
106 ROPTIONAL(RERROR, ERROR, "Error fetching row");
107 rlm_sql_print_error(inst, request, query_ctx, false);
109 }
110}
111
112/** Retrieve any errors from the SQL driver
113 *
114 * Retrieves errors from the driver from the last operation and writes them to
115 * to request/global log, in the ERROR, WARN, INFO and DEBUG categories.
116 *
117 * @param inst Instance of rlm_sql.
118 * @param request Current request, may be NULL.
119 * @param query_ctx Query context to retrieve errors for.
120 * @param force_debug Force all errors to be logged as debug messages.
121 */
122void rlm_sql_print_error(rlm_sql_t const *inst, request_t *request, fr_sql_query_t *query_ctx, bool force_debug)
123{
124 char const *driver = inst->driver_submodule->name;
125 sql_log_entry_t log[20];
126 size_t num, i;
127 TALLOC_CTX *log_ctx;
128
129 /*
130 * Every driver reads the errors off the connection handle, which the
131 * trunk frees when it closes the connection. A query that failed
132 * because its connection died is resumed after that has happened,
133 * so there is nothing left to ask the driver for.
134 */
135 if (!query_ctx->tconn || query_ctx->tconn->conn->is_closed) {
136 ROPTIONAL(RERROR, ERROR, "%s: Connection closed", driver);
137 return;
138 }
139
140 log_ctx = talloc_new(NULL);
141
142 num = (inst->driver->sql_error)(log_ctx, log, (NUM_ELEMENTS(log)), query_ctx);
143 if (num == 0) {
144 ROPTIONAL(RERROR, ERROR, "Unknown error");
145 talloc_free(log_ctx);
146 return;
147 }
148
149 for (i = 0; i < num; i++) {
150 if (force_debug) goto debug;
151
152 switch (log[i].type) {
153 case L_ERR:
154 ROPTIONAL(RERROR, ERROR, "%s: %s", driver, log[i].msg);
155 break;
156
157 case L_WARN:
158 ROPTIONAL(RWARN, WARN, "%s: %s", driver, log[i].msg);
159 break;
160
161 case L_INFO:
162 ROPTIONAL(RINFO, INFO, "%s: %s", driver, log[i].msg);
163 break;
164
165 case L_DBG:
166 default:
167 debug:
168 ROPTIONAL(RDEBUG2, DEBUG2, "%s: %s", driver, log[i].msg);
169 break;
170 }
171 }
172
173 talloc_free(log_ctx);
174}
175
176/** Automatically run the correct `finish` function when freeing an SQL query
177 *
178 * And mark any associated trunk request as complete.
179 */
181{
182 if (to_free->status > 0) {
183 if (to_free->type == SQL_QUERY_SELECT) {
184 (to_free->inst->driver->sql_finish_select_query)(to_free, &to_free->inst->config);
185 } else {
186 (to_free->inst->driver->sql_finish_query)(to_free, &to_free->inst->config);
187 }
188 }
189 if (to_free->treq) trunk_request_signal_complete(to_free->treq);
190 return 0;
191}
192
193/** Allocate an sql query structure
194 *
195 */
196fr_sql_query_t *fr_sql_query_alloc(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request,
197 trunk_t *trunk, char const *query_str, fr_sql_query_type_t type)
198{
199 fr_sql_query_t *query;
200 MEM(query = talloc(ctx, fr_sql_query_t));
201 *query = (fr_sql_query_t) {
202 .inst = inst,
203 .request = request,
204 .trunk = trunk,
205 .query_str = query_str,
206 .type = type
207 };
208 talloc_set_destructor(query, fr_sql_query_free);
209 return query;
210}
211
212/** Yield processing after submitting a trunk request.
213 */
215 UNUSED request_t *request, UNUSED void *uctx)
216{
217 return UNLANG_ACTION_YIELD;
218}
219
220/** Cancel an SQL query submitted on a trunk
221 */
222static void sql_trunk_query_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
223{
224 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
225 trunk_request_t *treq;
226
227 if (!query_ctx->treq) return;
228 treq = query_ctx->treq;
229
230 /*
231 * A reapable trunk request has already completed.
232 */
233 if (unlikely(treq->state == TRUNK_REQUEST_STATE_REAPABLE)) {
235 query_ctx->treq = NULL;
236 return;
237 }
238
239 /*
240 * The query_ctx needs to be parented by the treq so that it still exists
241 * when the cancel_mux callback is run.
242 */
243 if (query_ctx->inst->driver->trunk_io_funcs.request_cancel_mux) talloc_steal(treq, query_ctx);
244 query_ctx->treq = NULL;
245
247}
248
249/** Submit an SQL query using a trunk connection.
250 *
251 * @param p_result Result of current module call.
252 * @param request Current request.
253 * @param uctx query context containing query to execute.
254 * @return an unlang_action_t.
255 */
257{
258 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
259 trunk_enqueue_t status;
260
261 fr_assert(query_ctx->trunk);
262
263 /* There's no query to run, return an error */
264 if (query_ctx->query_str[0] == '\0') {
265 if (request) REDEBUG("Zero length query");
267 }
268
269 /*
270 * If the query already has a treq, and that is not in the "init" state
271 * then this is part of an ongoing transaction and needs requeueing
272 * to submit on the same connection.
273 */
274 if (query_ctx->treq && query_ctx->treq->state != TRUNK_REQUEST_STATE_INIT) {
275 status = trunk_request_requeue(query_ctx->treq);
276 } else {
277 status = trunk_request_enqueue(&query_ctx->treq, query_ctx->trunk, request, query_ctx, NULL);
278 }
279 switch (status) {
280 case TRUNK_ENQUEUE_OK:
282 /*
283 * Drivers such as SQLite which are synchronous run the query immediately
284 * on queueing. If the query fails then the trunk request will be failed
285 * in which case the query_ctx will no longer have a trunk request.
286 */
287 if (!query_ctx->treq) RETURN_UNLANG_FAIL;
288
289 /*
290 * Synchronous drivers will have processed the query and set the
291 * state of the trunk request to reapable - so in that case don't
292 * yield (in sql_trunk_query_start)
293 */
294 if (unlang_function_push_with_result(/* allow the caller of rlm_sql_trunk_query to get at the rcode */p_result,
295 request,
296 query_ctx->treq->state == TRUNK_REQUEST_STATE_REAPABLE ?
298 query_ctx->type == SQL_QUERY_SELECT ?
299 query_ctx->inst->driver->sql_select_query_resume :
300 query_ctx->inst->driver->sql_query_resume,
301 sql_trunk_query_cancel, ~FR_SIGNAL_CANCEL,
302 UNLANG_SUB_FRAME, query_ctx) < 0) RETURN_UNLANG_FAIL;
303 p_result->rcode = RLM_MODULE_OK;
305
306 default:
307 REDEBUG("Unable to enqueue SQL query");
308 query_ctx->status = SQL_QUERY_FAILED;
309 query_ctx->rcode = RLM_SQL_ERROR;
311 }
312}
313
314/** Process the results of an SQL query to produce a map list.
315 *
316 */
318{
319 fr_sql_map_ctx_t *map_ctx = talloc_get_type_abort(uctx, fr_sql_map_ctx_t);
320 tmpl_rules_t lhs_rules = (tmpl_rules_t) {
321 .attr = {
322 .dict_def = request->local_dict,
323 .list_def = map_ctx->list,
324 .list_presence = TMPL_ATTR_LIST_ALLOW
325 }
326 };
327 tmpl_rules_t rhs_rules = lhs_rules;
328 fr_sql_query_t *query_ctx = map_ctx->query_ctx;
329 rlm_sql_row_t row;
330 map_t *parent = NULL;
331 rlm_sql_t const *inst = map_ctx->inst;
332
334
335 if (query_ctx->rcode != RLM_SQL_OK) {
336 rlm_sql_print_error(inst, request, query_ctx, false);
338 }
339
340 while ((inst->fetch_row(p_result, request, query_ctx) == UNLANG_ACTION_CALCULATE_RESULT) &&
341 (query_ctx->rcode == RLM_SQL_OK)) {
342 map_t *map;
343
344 row = query_ctx->row;
345 if (!row[2] || !row[3] || !row[4]) {
346 RPERROR("SQL query returned NULL values");
348 }
349 if (map_afrom_fields(map_ctx->ctx, &map, &parent, request, row[2], row[4], row[3],
350 &lhs_rules, &rhs_rules,
351 !(inst->config.expand_rhs || map_ctx->expand_rhs)) < 0) {
352 RPEDEBUG("Data read from SQL cannot be parsed.");
353 REDEBUG(" %s", row[2]);
354 REDEBUG(" %s", row[4]);
355 REDEBUG(" %s", row[3]);
357 }
358 if (!map->parent) map_list_insert_tail(map_ctx->out, map);
359
360 map_ctx->rows++;
361 }
362 talloc_free(query_ctx);
363
365}
366
367/** Submit the query to get any user / group check or reply pairs
368 *
369 */
371{
372 rlm_sql_t const *inst = map_ctx->inst;
373
374 fr_assert(request);
375 fr_assert(map_ctx->query);
376
377 MEM(map_ctx->query_ctx = fr_sql_query_alloc(map_ctx->ctx, inst, request, trunk,
378 map_ctx->query->vb_strvalue, SQL_QUERY_SELECT));
379
381 request,
382 NULL,
384 NULL, 0,
386 map_ctx) < 0) return UNLANG_ACTION_FAIL;
387
388 return unlang_function_push_with_result(/* discard, sql_get_map_list_resume uses query_ctx->rcode */ NULL,
389 request,
390 inst->select,
391 NULL,
392 NULL, 0,
394 map_ctx->query_ctx);
395}
396
397/*
398 * Log the query to a file.
399 */
400void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
401{
402 int fd;
403 size_t len;
404 bool failed = false; /* Write the log message outside of the critical region */
405
406 fd = exfile_open(inst->ef, filename, 0640, 0, NULL);
407 if (fd < 0) {
408 ERROR("Couldn't open logfile '%s': %s", filename, fr_syserror(errno));
409
410 /* coverity[missing_unlock] */
411 return;
412 }
413
414 len = strlen(query);
415 if ((write(fd, query, len) < 0) || (write(fd, ";\n", 2) < 0)) failed = true;
416
417 if (failed) ERROR("Failed writing to logfile '%s': %s", filename, fr_syserror(errno));
418
419 exfile_close(inst->ef, fd);
420}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:41
log_entry msg
Definition acutest.h:794
#define RCSID(id)
Definition build.h:560
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
int exfile_open(exfile_t *ef, char const *filename, mode_t permissions, int flags, off_t *offset)
Open a new log file, or maybe an existing one.
Definition exfile.c:528
int exfile_close(exfile_t *ef, int fd)
Close the log file.
Definition exfile.c:595
#define unlang_function_push_with_result(_result_p, _request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack that produces a result.
Definition function.h:144
talloc_free(hp)
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:140
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:545
#define RWARN(fmt,...)
Definition log.h:314
#define RERROR(fmt,...)
Definition log.h:315
#define RPERROR(fmt,...)
Definition log.h:319
#define RINFO(fmt,...)
Definition log.h:313
#define RPEDEBUG(fmt,...)
Definition log.h:393
int map_afrom_fields(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, request_t *request, char const *lhs, char const *op_str, char const *rhs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, bool bare_word_only)
Convert a fr_pair_t into a map.
Definition map.c:2529
@ L_WARN
Warning.
Definition log.h:54
@ L_ERR
Error message.
Definition log.h:53
@ L_INFO
Informational message.
Definition log.h:52
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:56
static TALLOC_CTX * map_ctx
Definition map_builtin.c:32
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
#define INFO(fmt,...)
Definition radict.c:63
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_OK
Definition rcode.h:64
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
fr_dict_attr_t const * request_attr_request
Definition request.c:43
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
Definition rlm_sql.h:144
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition rlm_sql.h:140
fr_sql_query_type_t type
Type of query.
Definition rlm_sql.h:143
trunk_t * trunk
Trunk this query is being run on.
Definition rlm_sql.h:139
rlm_sql_t const * inst
Module instance for this query.
Definition rlm_sql.h:137
char const * query_str
Query string to run.
Definition rlm_sql.h:142
@ RLM_SQL_QUERY_INVALID
Query syntax error.
Definition rlm_sql.h:45
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition rlm_sql.h:50
fr_sql_query_type_t
Definition rlm_sql.h:119
@ SQL_QUERY_SELECT
Definition rlm_sql.h:120
char ** rlm_sql_row_t
Definition rlm_sql.h:59
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:146
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:145
trunk_request_t * treq
Trunk request for this query.
Definition rlm_sql.h:141
@ SQL_QUERY_FAILED
Failed to submit.
Definition rlm_sql.h:127
Context used when fetching attribute value pairs as a map list.
Definition rlm_sql.h:152
Definition rlm_sql.h:61
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
@ TMPL_ATTR_LIST_ALLOW
Attribute refs are allowed to have a list.
Definition tmpl.h:262
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
fr_sql_query_t * fr_sql_query_alloc(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request, trunk_t *trunk, char const *query_str, fr_sql_query_type_t type)
Allocate an sql query structure.
Definition sql.c:196
size_t sql_rcode_table_len
Definition sql.c:63
static int fr_sql_query_free(fr_sql_query_t *to_free)
Automatically run the correct finish function when freeing an SQL query.
Definition sql.c:180
unlang_action_t sql_get_map_list(unlang_result_t *p_result, request_t *request, fr_sql_map_ctx_t *map_ctx, trunk_t *trunk)
Submit the query to get any user / group check or reply pairs.
Definition sql.c:370
unlang_action_t rlm_sql_fetch_row(unlang_result_t *p_result, request_t *request, void *uctx)
Call the driver's sql_fetch_row function.
Definition sql.c:79
void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
Definition sql.c:400
static unlang_action_t sql_get_map_list_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of an SQL query to produce a map list.
Definition sql.c:317
fr_table_num_sorted_t const sql_rcode_table[]
Definition sql.c:55
static unlang_action_t sql_trunk_query_start(UNUSED unlang_result_t *p_result, UNUSED request_t *request, UNUSED void *uctx)
Yield processing after submitting a trunk request.
Definition sql.c:214
static void sql_trunk_query_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an SQL query submitted on a trunk.
Definition sql.c:222
void rlm_sql_print_error(rlm_sql_t const *inst, request_t *request, fr_sql_query_t *query_ctx, bool force_debug)
Retrieve any errors from the SQL driver.
Definition sql.c:122
size_t sql_rcode_description_table_len
Definition sql.c:53
fr_table_num_sorted_t const sql_rcode_description_table[]
Definition sql.c:45
unlang_action_t rlm_sql_trunk_query(unlang_result_t *p_result, request_t *request, void *uctx)
Submit an SQL query using a trunk connection.
Definition sql.c:256
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
Value pair map.
Definition map.h:77
map_t * parent
Definition map.h:88
unlang_function_with_result_t sql_select_query_resume
Callback run after an SQL select trunk query is run.
Definition rlm_sql.h:206
trunk_io_funcs_t trunk_io_funcs
Trunk callback functions for this driver.
Definition rlm_sql.h:224
sql_rcode_t(* sql_finish_query)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition rlm_sql.h:217
sql_rcode_t(* sql_finish_select_query)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition rlm_sql.h:218
unlang_function_with_result_t sql_query_resume
Callback run after an SQL trunk query is run.
Definition rlm_sql.h:205
rlm_sql_config_t config
Definition rlm_sql.h:228
rlm_sql_driver_t const * driver
Driver's exported interface.
Definition rlm_sql.h:235
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:295
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition trunk.c:2637
trunk_enqueue_t trunk_request_requeue(trunk_request_t *treq)
Re-enqueue a request on the same connection.
Definition trunk.c:2726
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2196
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2138
Wraps a normal request.
Definition trunk.c:99
Main trunk management handle.
Definition trunk.c:215
trunk_enqueue_t
Definition trunk.h:158
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition trunk.h:160
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition trunk.h:159
trunk_request_cancel_mux_t request_cancel_mux
!< Read one or more requests from a connection.
Definition trunk.h:760
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
Definition trunk.h:183
@ TRUNK_REQUEST_STATE_INIT
Initial state.
Definition trunk.h:172
static fr_slen_t parent
Definition pair.h:858