The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
base.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at 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: 3364fe9a7bfcc6fcac732dcee16bb50397a9a64e $
19 * @file src/process/redis/base.c
20 * @brief State machine for Redis cluster coordinator thread
21 *
22 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
23 */
24#include <freeradius-devel/redis/attrs.h>
25#include <freeradius-devel/redis/base.h>
26#include <freeradius-devel/redis/cluster_async.h>
27#include <freeradius-devel/io/coord_pair.h>
28#include <freeradius-devel/server/main_config.h>
29#include <freeradius-devel/unlang/function.h>
30#include <freeradius-devel/unlang/interpret.h>
31#include <freeradius-devel/util/debug.h>
32
33/* Unique number for each cluster. Starts at 1, so 0 missing data */
35
37
40 { .out = &dict_freeradius, .proto = "freeradius" },
42};
43
46
53
55
60
61/** Individual cluster node
62 */
63typedef struct {
64 fr_dlist_t entry; //!< Entry in the list of cluster nodes.
65 fr_redis_io_conf_t io_conf; //!< Connection config for this node.
66 fr_redis_trunk_t *trunk; //!< Trunk connection for this node.
67 bool in_cluster; //!< Has the node been found in the latest cluster map.
68 uint32_t version; //!< Redis version on this node.
69 uint64_t current_epoch; //!< Redis cluster epoch as reported by this node.
70 fr_pair_list_t trigger_args; //!< Pair list to pass to trigger.
72
73/** Coordinator representation of a Redis cluster
74 */
75typedef struct {
76 fr_redis_conf_t *conf; //!< Redis config for this cluster.
77 uint16_t cluster_id; //!< Numeric ID assigned by the coordinator
78 fr_ipaddr_t addr; //!< IP address of the first bootstrap server
79 uint16_t port; //!< Port of the first bootstrap server
80 fr_dlist_head_t nodes; //!< List of current nodes in the cluster
81 fr_rb_node_t cluster_by_server; //!< Entry in the tree of clusters by bootstrap server.
82 fr_rb_node_t cluster_by_id; //!< Entry in the tree of clusters by ID.
83 fr_redis_ct_t *rtcluster; //!< Cluster used to allocate redis trunk connections
84 fr_pair_list_t cluster_pairs; //!< Pairs built from the last fetch.
85 bool fetching; //!< The map is being fetched.
86 fr_time_t last_update; //!< When was the map last updated.
87 fr_rb_tree_t pending; //!< Requests waiting for custer map update.
88 fr_coord_pair_t *coord_pair; //!< The coord_pair which requested this cluster map.
89 bool failed; //!< Has the cluster failed.
90 fr_timer_t *ev; //!< Timer event for retry / refresh.
91 fr_pair_list_t trigger_args; //!< Pair list to pass to cluster triggers.
92 CONF_PAIR *trigger_cp[FR_REDIS_CODE_MAX]; //!< Cached trigger conf pairs;
93 bool no_trigger[FR_REDIS_CODE_MAX]; //!< Cached trigger conf status;
95
100
101static fr_cmp_ret_t cluster_server_cmp(void const *a, void const *b)
102{
103 process_redis_cluster_t const *cluster_a = (process_redis_cluster_t const *)a;
104 process_redis_cluster_t const *cluster_b = (process_redis_cluster_t const *)b;
105 fr_cmp_ret_t ret;
106
107 ret = fr_ipaddr_cmp(&cluster_a->addr, &cluster_b->addr);
108 if (ret != 0) return ret;
109
110 return CMP(cluster_a->port, cluster_b->port);
111}
112
113static fr_cmp_ret_t cluster_id_cmp(void const*a, void const *b)
114{
115 process_redis_cluster_t const *cluster_a = (process_redis_cluster_t const *)a;
116 process_redis_cluster_t const *cluster_b = (process_redis_cluster_t const *)b;
117
118 return CMP(cluster_a->cluster_id, cluster_b->cluster_id);
119}
120
121static fr_cmp_ret_t process_redis_pending_cmp(void const *a, void const *b)
122{
123 process_redis_pending_t const *pending_a = (process_redis_pending_t const *)a;
124 process_redis_pending_t const *pending_b = (process_redis_pending_t const *)b;
125
126 return CMP(pending_a->request, pending_b->request);
127}
128
139
140typedef struct {
142 fr_rb_tree_t cluster_by_server; //!< Tree of clusters by primary bootstrap server.
143 fr_rb_tree_t cluster_by_id; //!< Tree of clusters by ID.
145
146static const conf_parser_t config[] = {
147 { FR_CONF_OFFSET_SUBSECTION("pool", 0, process_redis_t, trunk_conf, trunk_config) },
148 { FR_CONF_OFFSET("timeout", process_redis_t, timeout), .dflt = "5s" },
149 { FR_CONF_OFFSET("retry_interval", process_redis_t, retry_interval), .dflt = "30s" },
150 { FR_CONF_OFFSET("refresh_interval", process_redis_t, refresh_interval) },
152};
153
154/** State of cluster map fetching from each node.
155 */
164
165/** Resume context for node specific calls
166 */
167typedef struct {
168 process_redis_node_t *node; //!< Node being queried.
169 fr_dlist_t entry; //!< In list of resume contexts.
170 map_get_status_t status; //!< Status of the node calls.
171 bool cluster_ok; //!< Does CLUSTER INFO say the cluster is OK.
172 fr_pair_list_t list; //!< To populate with parsed reply data.
173 fr_redis_command_set_t *cmds; //!< Command set for fetching cluster map.
174 fr_timer_t *ev; //!< Timeout event for this node.
176
177/** Resume context for Redis requests */
178typedef struct {
179 process_redis_t const *inst; //!< Module instance.
180 process_redis_thread_t *thread; //!< Thread instance.
181 unlang_result_t result; //!< Where results are written to
182 int32_t worker_id; //!< The worker which sent the data leading to this request.
183 process_redis_cluster_t *cluster; //!< Cluster which is being updated.
184 process_redis_node_t *current_node; //!< Node currently being queried.
185 fr_dlist_head_t rctx_list; //!< List of per-node resume contexts.
186 uint64_t cluster_epoch; //!< Largest epoch value returned by any node.
188
189#define FR_REDIS_PACKET_CODE_VALID(_code) (((_code) > 0) && ((_code) < FR_REDIS_CODE_MAX))
190#define FR_REDIS_PROCESS_CODE_VALID(_code) (FR_REDIS_PACKET_CODE_VALID(_code) || (_code == FR_REDIS_DO_NOT_RESPOND))
191
192#define PROCESS_PACKET_TYPE fr_redis_packet_code_t
193#define PROCESS_CODE_MAX FR_REDIS_CODE_MAX
194#define PROCESS_CODE_DO_NOT_RESPOND FR_REDIS_DO_NOT_RESPOND
195#define PROCESS_PACKET_CODE_VALID FR_REDIS_PROCESS_CODE_VALID
196#define PROCESS_INST process_redis_t
197#define PROCESS_RCTX process_redis_rctx_t
198
199#include <freeradius-devel/server/process.h>
200
201/** Convert the "slots" array in CLUSTER SHARDS replies into pairs
202 *
203 * @param reply Redis reply containing the "slots" array.
204 * @param shard_vp Pair representing shard to build slots pairs under.
205 * @param slots_covered Array recording which slots have been covered.
206 * @return
207 * - number of slot ranges found
208 * - -1 on error
209 */
210static int fr_redis_cluster_shards_slots_to_pairs(redisReply *reply, fr_pair_t *shard_vp, bool slots_covered[KEY_SLOTS])
211{
212 fr_pair_t *slot_vp, *vp;
213 size_t i;
214 uint16_t s;
215
216 /*
217 * The "slots" value must be an array with an even number
218 * of entries and all integers.
219 */
220 if (reply->type != REDIS_REPLY_ARRAY) return -1;
221 if (reply->elements == 0) return 0;
222 if ((reply->elements % 2) != 0) return -1;
223 for (i = 0; i < reply->elements; i++) if (reply->element[i]->type != REDIS_REPLY_INTEGER) return -1;
224
225 for (i = 0; i < (reply->elements - 1); i += 2) {
226 MEM(slot_vp = fr_pair_afrom_da(shard_vp, attr_redis_slot));
227 fr_pair_append(&shard_vp->vp_group, slot_vp);
228
230 if (reply->element[i]->type != REDIS_REPLY_INTEGER) return -1;
231 if (reply->element[i]->integer >= KEY_SLOTS) return -1;
232 vp->vp_uint16 = (uint16_t) reply->element[i]->integer;
233 fr_pair_append(&slot_vp->vp_group, vp);
234
236 if (reply->element[i + 1]->type != REDIS_REPLY_INTEGER) return -1;
237 vp->vp_uint16 = (uint16_t) reply->element[i + 1]->integer;
238 if (reply->element[i + 1]->integer >= KEY_SLOTS) return -1;
239 fr_pair_append(&slot_vp->vp_group, vp);
240
241 for(s = reply->element[i]->integer; s <= reply->element[i + 1]->integer; s++) slots_covered[s] = true;
242 }
243
244 return i / 2;
245}
246
247/** Convert the "nodes" array in CLUSTER SHARDS replies into pairs
248 *
249 * @param reply Redis reply containing the "nodes" array.
250 * @param shard_vp Pair representing shard to build ndoes pairs under.
251 * @return
252 * - number of nodes found
253 * - -1 on error
254 */
255static int fr_redis_cluster_shards_nodes_to_pairs(redisReply *reply, fr_pair_t *shard_vp)
256{
257 fr_pair_t *node_vp, *vp;
258 size_t i, j;
259 redisReply *node, *field, *value;
260
261 /*
262 * The "nodes" value must be an array of arrays.
263 */
264 if (reply->type != REDIS_REPLY_ARRAY) return -1;
265 for (i = 0; i < reply->elements; i++) if (reply->element[i]->type != REDIS_REPLY_ARRAY) return -1;
266
267 for (i = 0; i < reply->elements; i++) {
268 node = reply->element[i];
269
270 /*
271 * Every other entry must be a string - the field name.
272 */
273 for (j = 0; j < node->elements; j += 2) if (node->element[j]->type != REDIS_REPLY_STRING) return -1;
274
275 MEM(node_vp = fr_pair_afrom_da(shard_vp, attr_redis_node));
276
277 for (j = 0; j < (node->elements - 1); j +=2) {
278 field = node->element[j];
279 value = node->element[j + 1];
280 if (strcmp(field->str, "endpoint") == 0) {
281 if (value->type != REDIS_REPLY_STRING) return -1;
283 fr_pair_value_bstrndup(vp, value->str, value->len, true);
284
285 } else if (strcmp(field->str, "port") == 0) {
286 if (value->type != REDIS_REPLY_INTEGER) return -1;
288 vp->vp_uint16 = (uint16_t) value->integer;
289
290 } else if (strcmp(field->str, "role") == 0) {
291 if (value->type != REDIS_REPLY_STRING) return -1;
293 vp->vp_uint8 = (strcmp(value->str, "master") == 0) ? 1 : 2;
294
295 } else if (strcmp(field->str, "health") == 0) {
296 if (value->type != REDIS_REPLY_STRING) return -1;
297 if (strcmp(value->str, "failed") == 0) {
298 TALLOC_FREE(node_vp);
299 break;
300 }
301 continue;
302
303 } else {
304 continue;
305 }
306
307 fr_pair_append(&node_vp->vp_group, vp);
308 }
309
310 if (!node_vp) continue;
311
312 fr_pair_append(&shard_vp->vp_group, node_vp);
313 }
314
315 return i;
316}
317
318/** Convert the reply to CLUSTER SHARDS into pairs
319 *
320 * The CLUSTER SHARDS reply is designed as an extensible
321 * structure using arrays containing named fields.
322 * i.e. an element which is the field name, followed by
323 * the value in the next element.
324 *
325 * The fields for node entries are specifically described as
326 * being extensible.
327 *
328 * The CLUSTER SHARDS reply structure
329 @verbatim
330 [0] -> Shard 0
331 [0] -> "slots"
332 [1] -> Array of slot entries in pairs of start / end values.
333 [0] -> key_slot0_start
334 [1] -> key_slot0_end
335 [2] -> key_slot1_start
336 [3] -> key_slot1_end
337 [4 .. n] -> key_slot2_start .. key_slotm_end
338 [2] -> "nodes"
339 [3] -> Array of nodes which cover the slots in the "slots" array.
340 [0] -> Node 0
341 [0] -> "id"
342 [1] -> Node ID
343 [2] -> "port"
344 [3] -> (integer) port number
345 [4] -> "ip"
346 [5] -> IP address of node
347 [6] -> "endpoint"
348 [7] -> Preferred endpoint to connect to node
349 [8] -> "role"
350 [9] -> ("master"|"replica")
351 [10] -> "replication-offset"
352 [11] -> (integer) replication offset
353 [12] -> "health"
354 [13] -> ("online"|"failed"|"loading")
355 [1] -> Node 1
356 [0 .. n] -> Entries for Node 1
357 [1] -> Shard 1
358 [...]
359 @endverbatim
360 */
361 static int fr_redis_cluster_shards_to_pairs(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *list, redisReply *reply)
362{
363 size_t i;
364 fr_pair_t *shard_vp;
365 int ret;
366 bool slots_covered[KEY_SLOTS];
367 uint16_t s;
368
369 if(reply->type != REDIS_REPLY_ARRAY) return -1;
370
372
373 memset(slots_covered, 0, sizeof(slots_covered));
374
375 for (i = 0; i < reply->elements; i++) {
376 size_t j;
377 redisReply *shard = reply->element[i];
378
379 if (shard->type != REDIS_REPLY_ARRAY) {
380 error:
381 fr_pair_list_free(list);
382 return -1;
383 }
384 if (shard->elements < 4 || (shard->elements % 2 != 0)) goto error;
385
386 MEM(shard_vp = fr_pair_afrom_da(ctx, attr_redis_shard));
387 fr_pair_append(list, shard_vp);
388
389 for (j = 0; j < (shard->elements - 1); j += 2) {
390 redisReply *field = shard->element[j];
391 if (strcmp(field->str, "slots") == 0) {
392 ret = fr_redis_cluster_shards_slots_to_pairs(shard->element[j + 1], shard_vp,
393 slots_covered);
394 if (ret < 0) goto error;
395
396 /*
397 * Failed nodes can be reported with zero slots entries.
398 * Remove this shard from the list.
399 */
400 if (ret == 0) {
401 clean_up:
402 fr_pair_remove(list, shard_vp);
403 talloc_free(shard_vp);
404 break;
405 }
406
407 } else if (strcmp(field->str, "nodes") == 0) {
408 ret = fr_redis_cluster_shards_nodes_to_pairs(shard->element[j + 1], shard_vp);
409 if (ret < 0) goto error;
410 if (ret == 0) goto clean_up;
411
412 } else {
413 continue;
414 }
415 }
416 }
417
418 for (s = 0; s < KEY_SLOTS; s++) if (!slots_covered[s]) goto error;
419
420 return 0;
421}
422
423/** Convert the reply to CLUSER SLOTS into pairs
424 *
425 * The CLUSTER SLOTS reply structure
426 @verbatim
427 [0] -> key slot range 0
428 [0] -> key_slot_start
429 [1] -> key_slot_end
430 [2] -> master_node
431 [0] -> master 0 ip (string)
432 [1] -> master 0 port (number)
433 [3..n] -> replica_node(s)
434 [1] -> key slot range 1)
435 [0] -> key_slot_start
436 [1] -> key_slot_end
437 [2] -> master_node
438 [0] -> master 1 ip (string)
439 [1] -> master 1 port (number)
440 [3..n] -> replica_node(s)
441 [n] -> key slot range n
442 [0] -> key_slot_start
443 [1] -> key_slot_end
444 [2] -> master_node
445 [0] -> master n ip (string)
446 [1] -> master n port (number)
447 [3..n] -> replica_node(s)
448 @endverbatim
449 *
450 * @param[in] ctx to allocate pairs in.
451 * @param[in,out] list to populate with pairs.
452 * @param[in] reply from CLUSTER SLOTS
453 */
454static int fr_redis_cluster_slots_to_pairs(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *list, redisReply *reply)
455{
456 size_t i;
457 fr_pair_t *shard_vp, *slot_vp, *node_vp, *vp;
458 bool slots_covered[KEY_SLOTS];
459 uint16_t s;
460
461 if(reply->type != REDIS_REPLY_ARRAY) return -1;
462
464
465 memset(slots_covered, 0, sizeof(slots_covered));
466
467 /*
468 * A map consists of an array with the following indexes:
469 * [0] -> key_slot_start
470 * [1] -> key_slot_end
471 * [2] -> master_node
472 * [3..n] -> replica_node(s)
473 */
474 for (i = 0; i < reply->elements; i++) {
475 size_t j;
476 redisReply *map = reply->element[i];
477 redisReply *node;
478
479 MEM(shard_vp = fr_pair_afrom_da(ctx, attr_redis_shard));
480
481 MEM(slot_vp = fr_pair_afrom_da(shard_vp, attr_redis_slot));
482 fr_pair_append(&shard_vp->vp_group, slot_vp);
483
485 if (map->element[0]->type != REDIS_REPLY_INTEGER) {
486 error:
487 talloc_free(shard_vp);
488 list_free:
489 fr_pair_list_free(list);
490 return -1;
491 }
492 if (map->element[0]->integer >= KEY_SLOTS) goto error;
493 vp->vp_uint16 = (uint16_t) map->element[0]->integer;
494 fr_pair_append(&slot_vp->vp_group, vp);
495
497 if (map->element[1]->type != REDIS_REPLY_INTEGER) goto error;
498 if (map->element[0]->integer >= KEY_SLOTS) goto error;
499 vp->vp_uint16 = (uint16_t) map->element[1]->integer;
500 fr_pair_append(&slot_vp->vp_group, vp);
501
502 for (j = 2; j < map->elements; j++) {
503 node = map->element[j];
504 MEM(node_vp = fr_pair_afrom_da(shard_vp, attr_redis_node));
505 fr_pair_append(&shard_vp->vp_group, node_vp);
506
508 if (node->element[0]->type != REDIS_REPLY_STRING) goto error;
509 fr_pair_value_bstrndup(vp, node->element[0]->str, node->element[0]->len, true);
510 fr_pair_append(&node_vp->vp_group, vp);
511
513 if (node->element[1]->type != REDIS_REPLY_INTEGER) goto error;
514 vp->vp_uint16 = (uint16_t) node->element[1]->integer;
515 fr_pair_append(&node_vp->vp_group, vp);
516
518 vp->vp_uint8 = (j == 2) ? 1 : 2;
519 fr_pair_append(&node_vp->vp_group, vp);
520 }
521
522 for (s = map->element[0]->integer; s <= map->element[1]->integer; s++) slots_covered[s] = true;
523
524 fr_pair_append(list, shard_vp);
525 }
526
527 for (s = 0; s < KEY_SLOTS; s++) if (!slots_covered[s]) goto list_free;
528
529 return 0;
530}
531
533 fr_redis_conf_t *conf, fr_pair_t *host_vp, fr_pair_t *port_vp)
534{
536 fr_ipaddr_t ipaddr;
538
539 MEM(node = talloc_zero(ctx, process_redis_node_t));
540 node->io_conf = (fr_redis_io_conf_t) {
541 .password = conf->password,
542 .username = conf->username,
543 .use_tls = conf->use_tls,
544 };
545 if (fr_inet_pton_port(&ipaddr, &node->io_conf.port, host_vp->vp_strvalue,
546 host_vp->vp_length, AF_UNSPEC, true, true) < 0){
547 error:
548 talloc_free(node);
549 return -1;
550 }
551 node->io_conf.hostname = talloc_strdup(node, fr_inet_ntop(buff, sizeof(buff), &ipaddr));
552 if (node->io_conf.port == 0) {
553 if (!port_vp) goto error;
554 node->io_conf.port = port_vp->vp_uint16;
555 }
556 node->io_conf.log_prefix = talloc_asprintf(node, "Coord %s %s:%d", conf->log_prefix,
557 fr_inet_ntop(buff, sizeof(buff), &ipaddr),
558 node->io_conf.port);
559 node->in_cluster = true;
561 if (conf->trunk_conf.conn_triggers) {
562 module_trigger_args_build(node, &node->trigger_args, NULL,
564 .module = "process_redis",
565 .name = inst->inst_name,
566 .server = buff,
567 .port = node->io_conf.port
568 });
569 }
570 fr_dlist_insert_tail(&cluster->nodes, node);
571 return 0;
572}
573
575 redisReply *reply, void *rctx)
576{
577 process_redis_node_t *node = talloc_get_type_abort(rctx, process_redis_node_t);
578 char buffer[20];
579
581 if (fr_redis_parse_version(buffer, sizeof(buffer), reply) < 0) return;
583 RDEBUG3("Cluster node %s:%d is running Redis version %s", node->io_conf.hostname, node->io_conf.port, buffer);
584}
585
586static void redis_cluster_info_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
587{
588 process_redis_node_rctx_t *nrctx = talloc_get_type_abort(rctx, process_redis_node_rctx_t);
589 fr_sbuff_t sbuff;
590
592
593 if (reply->type != REDIS_REPLY_STRING) {
594 RERROR("Bad value type, expected string, got %s",
595 fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
596 error:
598 return;
599 }
600
601 fr_sbuff_init_in(&sbuff, reply->str, reply->len);
602 if (!fr_sbuff_adv_to_str_literal(&sbuff, SIZE_MAX, "cluster_state:")) {
603 RERROR("Response did not contain cluster_state");
604 goto error;
605 }
606 fr_sbuff_advance(&sbuff, sizeof("cluster_state:") - 1);
607
608 if (fr_sbuff_adv_past_str_literal(&sbuff, "ok\r\n")) {
609 nrctx->cluster_ok = true;
610 RDEBUG2("Node %s:%d reports Cluster OK", nrctx->node->io_conf.hostname, nrctx->node->io_conf.port);
611 } else {
612 RERROR("Node %s:%d reports Cluster Failed", nrctx->node->io_conf.hostname, nrctx->node->io_conf.port);
613 }
614
615 /*
616 * The sequence of entries in the CLUSTER INFO results is not guaranteed,
617 * so we start the search from the beginning again.
618 */
619 fr_sbuff_set_to_start(&sbuff);
620
621 if (!fr_sbuff_adv_to_str_literal(&sbuff, SIZE_MAX, "cluster_current_epoch:")) {
622 RERROR("Response did not contain cluster_current_epoch");
623 goto error;
624 }
625 fr_sbuff_advance(&sbuff, sizeof("cluster_current_epoch:") -1);
626
627 if (fr_sbuff_out_uint64(&nrctx->node->current_epoch, &sbuff, false) < 0) {
628 RERROR("Failed parsing current_cluster_epoch");
629 goto error;
630 }
631
632 RDEBUG3("Node %s:%d reported epoch %"PRIu64, nrctx->node->io_conf.hostname,
633 nrctx->node->io_conf.port, nrctx->node->current_epoch);
635 return;
636}
637
638static void redis_cluster_slots_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
639{
640 process_redis_node_rctx_t *nrctx = talloc_get_type_abort(rctx, process_redis_node_rctx_t);
641 int ret;
642
643 if (nrctx->node->version > redis_shards_version) {
644 ret = fr_redis_cluster_shards_to_pairs(nrctx, request, &nrctx->list, reply);
645 } else {
646 ret = fr_redis_cluster_slots_to_pairs(nrctx, request, &nrctx->list, reply);
647 }
648 if (RDEBUG_ENABLED2 && (ret == 0)){
649 RDEBUG2("Cluster map fetched:");
650 RINDENT();
651 fr_pair_list_foreach(&nrctx->list, vp) {
652 RDEBUG2("%pP", vp);
653 }
654 REXDENT();
655 }
657}
658
660{
661 process_redis_node_rctx_t *nrctx = talloc_get_type_abort(uctx, process_redis_node_rctx_t);
662
663 ERROR("Fetching map from %s:%d failed", nrctx->node->io_conf.hostname, nrctx->node->io_conf.port);
666}
667
669{
670 process_redis_cluster_t *cluster = talloc_get_type_abort(uctx, process_redis_cluster_t);
671 fr_pair_list_t list;
672 fr_pair_t *vp;
673 TALLOC_CTX *local = talloc_new(NULL);
674
675 if (cluster->failed) {
676 DEBUG2("Retrying fetch of cluster map");
677 } else {
678 DEBUG2("Refreshing cluster map");
679 }
680
681 fr_pair_list_init(&list);
683 if (!vp) goto free;
684
685 fr_pair_list_append_by_da(local, vp, &list, attr_redis_cluster_id, cluster->cluster_id, false);
686 if (!vp) goto free;
687
688 fr_coord_pair_coord_request_start(cluster->coord_pair, &list, now);
689
690free:
691 talloc_free(local);
692}
693
694/** Send a Cluster-Failed message to a worker
695 */
698{
699 fr_pair_t *vp;
700
701 MEM(fr_pair_prepend_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_redis_cluster_id) >= 0);
702 vp->vp_uint16 = cluster->cluster_id;
703
704 MEM(fr_pair_prepend_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_redis_packet_type) >= 0);
705 vp->vp_uint32 = FR_REDIS_CLUSTER_MAP_FAIL;
706
709}
710
712{
713 process_redis_rctx_t *rctx = talloc_get_type_abort(uctx, process_redis_rctx_t);
714 process_redis_cluster_t *cluster = rctx->cluster;
716
717 cluster->fetching = true;
719
721 MEM(nrctx = talloc_zero(rctx, process_redis_node_rctx_t));
722 nrctx->node = node;
723 fr_pair_list_init(&nrctx->list);
724
725 MEM(nrctx->cmds = fr_redis_command_set_alloc(rctx, request, NULL, NULL, nrctx, false));
726
727 RDEBUG2("Fetching cluster map %d from %s:%d", cluster->cluster_id, node->io_conf.hostname,
728 node->io_conf.port);
729 if (!node->trunk) {
730 node->trunk = fr_redis_trunk_alloc(cluster->rtcluster, &node->io_conf, &node->trigger_args,
731 NULL, NULL, false);
733 node) != FR_REDIS_PIPELINE_OK) {
734 fail:
735 fr_fatal_assert_fail("Failed adding command to Redis command set");
736 }
737 }
738
740 nrctx) != FR_REDIS_PIPELINE_OK) goto fail;
741
742 if (redis_command_set_enqueue(node->trunk, nrctx->cmds) != FR_REDIS_PIPELINE_OK) {
743 RERROR("Unable to enqueue request on node %s:%d", node->io_conf.hostname,
744 node->io_conf.port);
745 talloc_free(nrctx);
746 continue;
747 }
748
749 fr_timer_in(nrctx, rctx->thread->el->tl, &nrctx->ev, rctx->inst->timeout, true,
751 fr_dlist_insert_tail(&rctx->rctx_list, nrctx);
752 }
753
755
756 RERROR("Unable to query any cluster node");
757
758 cluster->failed = true;
759 if (fr_timer_in(cluster, rctx->thread->el->tl, &cluster->ev, rctx->inst->retry_interval,
760 false, redis_cluster_map_get_refetch, cluster) < 0) {
761 RERROR("Failed setting up retry event");
762 };
763 return process_redis_return_failed(request, cluster, rctx->worker_id);
764}
765
767{
768 process_redis_rctx_t *rctx = talloc_get_type_abort(uctx, process_redis_rctx_t);
769 process_redis_cluster_t *cluster = rctx->cluster;
770 fr_pair_t *vp;
773 size_t completed = 0;
774 fr_pair_list_t *list = NULL;
775
776 /*
777 * The request processing will resume when one or more nodes has
778 * replied.
779 * Check the current state of the rctx for each node.
780 */
782 switch (nrctx->status) {
785 break;
786
789 clean_up:
790 fr_dlist_remove(&rctx->rctx_list, nrctx);
791 talloc_free(nrctx);
792 break;
793
795 /*
796 * Check epoch returned by node. Anything lower than
797 * the highest value seen so far can be disregarded.
798 */
799 if (nrctx->node->current_epoch < rctx->cluster_epoch) {
800 RWARN("Node %s:%d returned lower epoch than other nodes - ignoring",
801 nrctx->node->io_conf.hostname, nrctx->node->io_conf.port);
802 goto clean_up;
803 }
804
805 rctx->cluster_epoch = nrctx->node->current_epoch;
806
807 fr_redis_command_set_clear(nrctx->cmds);
808 if (nrctx->node->version > redis_shards_version) {
809 if (fr_redis_command_literal_add(nrctx->cmds, "CLUSTER SHARDS",
811 nrctx) != FR_REDIS_PIPELINE_OK) goto clean_up;
812 } else {
813 if (fr_redis_command_literal_add(nrctx->cmds, "CLUSTER SLOTS",
815 nrctx) != FR_REDIS_PIPELINE_OK) goto clean_up;
816 }
817 nrctx->status = CLUSTER_MAP_GET_MAP;
818 if (redis_command_set_enqueue(nrctx->node->trunk, nrctx->cmds) != FR_REDIS_PIPELINE_OK) {
819 RERROR("Unable to enqueue request on node %s:%d",
820 nrctx->node->io_conf.hostname, nrctx->node->io_conf.port);
821 goto clean_up;
822 }
823 break;
824
826 (void) fr_timer_delete(&nrctx->ev);
827 TALLOC_FREE(nrctx->cmds);
828 if (fr_pair_list_num_elements(&nrctx->list) == 0) {
829 RWARN("Node %s:%d didn't return a cluster", nrctx->node->io_conf.hostname,
830 nrctx->node->io_conf.port);
831 goto clean_up;
832 }
833 completed++;
834 break;
835 }
836 }
837
838 /*
839 * If there are still nodes with outstanding rctx then yield.
840 */
841 if (completed < fr_dlist_num_elements(&rctx->rctx_list)) {
842 if (unlang_function_repeat_set(request, redis_cluster_map_get_resume) < 0) goto fail;
843 return UNLANG_ACTION_YIELD;
844 }
845
846 if (fr_dlist_num_elements(&rctx->rctx_list) < 1) {
847 RERROR("No node returned a valid cluster map");
848 fail:
849 cluster->failed = true;
850 if (fr_timer_in(cluster, rctx->thread->el->tl, &cluster->ev, rctx->inst->retry_interval,
851 false, redis_cluster_map_get_refetch, cluster) < 0) {
852 RERROR("Failed setting up retry event");
853 };
854 if (!cluster->no_trigger[FR_REDIS_CLUSTER_MAP_FAIL]) {
856 &cluster->trigger_cp[FR_REDIS_CLUSTER_MAP_FAIL], "modules.redis.cluster_fail", true,
857 &cluster->trigger_args, cluster);
859 cluster->no_trigger[FR_REDIS_CLUSTER_MAP_FAIL] = true;
860 }
861 return process_redis_return_failed(request, cluster, rctx->worker_id);
862 }
863
864 /*
865 * Find the first node's rctx where the node epoch matches the
866 * highest seen value.
867 */
869 if (nrctx->node->current_epoch == rctx->cluster_epoch) {
870 list = &nrctx->list;
871 break;
872 }
873 }
874 if (unlikely(!list)) goto fail;
875
876 /*
877 * Verify the list of nodes, checking the cluster map matches
878 */
880 node->in_cluster = false;
881 }
882 fr_pair_list_foreach(list, shard) {
883 vp = NULL;
884 while ((vp = fr_pair_find_by_da(&shard->vp_group, vp, attr_redis_node))) {
885 fr_pair_t *endpoint, *port;
886 bool found = false;
887
888 endpoint = fr_pair_find_by_da(&vp->vp_group, NULL, attr_redis_node_endpoint);
889 port = fr_pair_find_by_da(&vp->vp_group, NULL, attr_redis_node_port);
890
891 if (unlikely(!endpoint || !port)) {
892 RERROR("Invalid cluster shard");
893 continue;
894 }
895
897 if ((strcmp(node->io_conf.hostname, endpoint->vp_strvalue) == 0) &&
898 (node->io_conf.port == port->vp_uint16)) {
899 node->in_cluster = true;
900 found = true;
901 break;
902 }
903 }
904
905 if (found) continue;
906
907 if (process_redis_cluster_node_add(cluster, cluster, rctx->inst, cluster->conf, endpoint, port) < 0) {
908 RERROR("Failed adding new node to cluster");
909 }
910 }
911 }
912
913 /*
914 * Remove any nodes not in the returned cluster map.
915 */
917 if (node->in_cluster) continue;
918 fr_dlist_remove(&cluster->nodes, node);
919 if (node->trunk) talloc_free(node->trunk);
920 talloc_free(node);
921 }
922
923 /*
924 * Update the stored cluster definition
925 */
927 MEM(fr_pair_list_copy(cluster, &cluster->cluster_pairs, list) >= 0);
928
929 MEM(fr_pair_prepend_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_redis_cluster_id) >= 0);
930 vp->vp_uint16 = cluster->cluster_id;
931 cluster->fetching = false;
932 cluster->failed = false;
933 cluster->last_update = fr_time();
934
935 MEM(fr_pair_list_copy(request->reply_ctx, &request->reply_pairs, list) >= 0);
936
937 MEM(fr_pair_prepend_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_redis_packet_type) >= 0);
938 vp->vp_uint32 = FR_REDIS_CLUSTER_MAP_UPDATE;
939
941 fr_pair_list_t trigger_args;
942 fr_pair_list_init(&trigger_args);
943 (void) fr_pair_list_copy(NULL, &trigger_args, &cluster->trigger_args);
944 (void) fr_pair_list_copy(NULL, &trigger_args, list);
946 &cluster->trigger_cp[FR_REDIS_CLUSTER_MAP_UPDATE], "modules.redis.cluster_update", true,
947 &trigger_args, cluster);
950 fr_pair_list_free(&trigger_args);
951 }
952
954
956 (fr_timer_in(cluster, rctx->thread->el->tl, &cluster->ev, rctx->inst->refresh_interval,
957 false, redis_cluster_map_get_refetch, cluster) < 0)) {
958 RERROR("Failed setting up refresh event");
959 }
960
962
963 for (pending = fr_rb_iter_init_inorder(&cluster->pending, &iter);
964 pending;
965 pending = fr_rb_iter_next_inorder(&cluster->pending, &iter)) {
966 fr_rb_iter_delete_inorder(&cluster->pending, &iter);
968 talloc_free(pending);
969 }
970
972}
973
974static void redis_cluster_map_get_cancel(request_t *request, UNUSED fr_signal_t action, void *uctx)
975{
976 process_redis_rctx_t *rctx = talloc_get_type_abort(uctx, process_redis_rctx_t);
977
979 if (!nrctx->cmds) continue;
980 RWARN("Forcibly cancelling cluster map request on %s:%d",
981 nrctx->node->io_conf.hostname, nrctx->node->io_conf.port);
982 fr_redis_command_set_cancel(nrctx->cmds);
983 }
984}
985
987{
988 process_redis_node_t *node = NULL;
989
990 while ((node = fr_dlist_next(&cluster->nodes, node))) {
991 if (!node->trunk) continue;
992 talloc_free(node->trunk);
993 }
994 return 0;
995}
996
997static void process_redis_pending_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
998{
999 process_redis_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_redis_rctx_t);
1000 process_redis_pending_t find, *pending;
1001
1002 find.request = request;
1003 fr_rb_find((void **)&pending, &rctx->cluster->pending, &find);
1004 if (!pending) return;
1005
1006 fr_rb_remove(NULL, &rctx->cluster->pending, pending);
1007 talloc_free(pending);
1008}
1009
1012{
1013 fr_pair_t *vp;
1014
1015 MEM(fr_pair_prepend_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_redis_cluster_id) >= 0);
1016 vp->vp_uint16 = cluster->cluster_id;
1017
1018 MEM(fr_pair_list_copy(request->reply_ctx, &request->reply_pairs, &cluster->cluster_pairs) >= 0);
1019
1020 MEM(fr_pair_prepend_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_redis_packet_type) >= 0);
1021 vp->vp_uint32 = FR_REDIS_CLUSTER_MAP_UPDATE;
1022
1025}
1026
1027RECV(cluster_map_bootstrap)
1028{
1029 process_redis_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_redis_rctx_t);
1030 process_redis_t const *inst = rctx->inst;
1031 process_redis_thread_t *thread = rctx->thread;
1032 fr_pair_t *vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_worker_id);
1033 fr_pair_t *port_vp;
1034 process_redis_cluster_t find, *cluster;
1036 CONF_SECTION *tls_conf = NULL;
1037 char bootstrap_ip[FR_IPADDR_STRLEN];
1038
1039 rctx->worker_id = vp ? vp->vp_int32 : 0;
1040
1041 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_bootstrap_node);
1042 fr_fatal_assert_msg(vp, "Missing %s", attr_redis_bootstrap_node->name);
1043
1044 port_vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_bootstrap_port);
1045
1046 if (fr_inet_pton_port(&find.addr, &find.port, vp->vp_strvalue, vp->vp_length,
1047 AF_UNSPEC, true, true) < 0) {
1048 fr_fatal_assert_fail("Unable to parse bootstrap node");
1049 }
1050
1051 if (find.port == 0) {
1052 fr_fatal_assert_msg(port_vp, "Missing %s", attr_redis_bootstrap_port->name);
1053 find.port = port_vp->vp_uint16;
1054 }
1055
1056 fr_rb_find((void **)&cluster, &thread->cluster_by_server, &find);
1057
1058 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_max_nodes);
1059 fr_fatal_assert_msg(vp, "Missing %s", attr_redis_max_nodes->name);
1060
1061 if (cluster) {
1062 /*
1063 * If this is a bootstrap call using nodes matching an existing
1064 * cluster, check the max_nodes match or array sizes will get messy.
1065 */
1066 fr_fatal_assert_msg(vp->vp_uint8 == cluster->conf->max_nodes,
1067 "Max nodes (%d) mis-match with existing cluster configured with %d",
1068 vp->vp_uint8, cluster->conf->max_nodes);
1069
1070 /*
1071 * We already have data for this cluster, just return it.
1072 */
1073 if (fr_time_gt(cluster->last_update, fr_time_wrap(0))) {
1074 return process_redis_return_existing(request, cluster, rctx->worker_id);
1075 }
1076
1077 /*
1078 * If the cluster map is already being fetched, yield until
1079 * the result is in.
1080 * More than one module instance may be using the same
1081 * so we need to process the request rather than relying
1082 * on the broadcast to workers, as that will only update
1083 * a single module instance.
1084 */
1085 if (cluster->fetching) {
1086 process_redis_pending_t *pending;
1087
1088 RDEBUG2("Cluster map already being fetched");
1089 rctx->cluster = cluster;
1090 if (unlang_module_yield(request, recv_cluster_map_bootstrap, process_redis_pending_cancel,
1092
1093 MEM(pending = talloc(cluster, process_redis_pending_t));
1094 *pending = (process_redis_pending_t) {.request = request};
1095 fr_rb_insert(&cluster->pending, pending);
1096
1097 return UNLANG_ACTION_YIELD;
1098 }
1099
1100 /*
1101 * Cluster map fetching failed, and the retry timer is armed.
1102 * Tell the caller that the map has failed.
1103 */
1104 if (cluster->failed) {
1105 return process_redis_return_failed(request, cluster, rctx->worker_id);
1106 }
1107 }
1108
1109 MEM(cluster = talloc_zero(thread, process_redis_cluster_t));
1110 MEM(conf = talloc_zero(cluster, fr_redis_conf_t));
1111 conf->trunk_conf = inst->trunk_conf;
1112
1113 cluster->cluster_id = cluster_id++;
1114 cluster->conf = conf;
1115 cluster->addr = find.addr;
1116 cluster->port = find.port;
1117 cluster->coord_pair = fr_coord_pair_request_coord_pair(request);
1120
1121 module_trigger_args_build(cluster, &cluster->trigger_args, NULL,
1123 .module = "process_redis",
1124 .name = inst->inst_name,
1125 .server = fr_inet_ntop(bootstrap_ip, sizeof(bootstrap_ip), &cluster->addr),
1126 .port = cluster->port
1127 });
1128
1131
1132 conf->max_nodes = vp->vp_uint8;
1133 conf->use_cluster_map = true;
1134
1135 fr_pair_list_foreach(&request->request_pairs, conf_vp) {
1136 if (conf_vp->da == attr_redis_username) {
1137 conf->username = talloc_strdup(conf, conf_vp->vp_strvalue);
1138 } else if (conf_vp->da == attr_redis_password) {
1139 conf->password = talloc_strdup(conf, conf_vp->vp_strvalue);
1140 } else if (conf_vp->da == attr_redis_log_prefix) {
1141 conf->log_prefix = talloc_strdup(conf, conf_vp->vp_strvalue);
1142 }
1143 }
1144
1145 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_use_tls);
1146 if (vp) {
1147 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_tls_conf);
1148 fr_fatal_assert_msg(vp, "Missing %s when TLS is enabled", attr_redis_tls_conf->name);
1149 conf->use_tls = true;
1150 tls_conf = (CONF_SECTION *)(uintptr_t)vp->vp_uint64;
1151 }
1152
1153 MEM(cluster->rtcluster = fr_redis_ct_alloc(cluster, tls_conf, thread->el, conf, NULL, NULL, false));
1154
1155 /*
1156 * Add all the bootstrap nodes to the cluster.
1157 */
1158 vp = NULL;
1159 while ((vp = fr_pair_find_by_da(&request->request_pairs, vp, attr_redis_bootstrap_node))) {
1160 if (process_redis_cluster_node_add(cluster, cluster, inst, conf, vp, port_vp) < 0) {
1161 talloc_free(cluster);
1162 fr_fatal_assert_fail("Failed adding cluster node to list");
1163 }
1164 }
1165 fr_rb_insert(&thread->cluster_by_server, cluster);
1166 fr_rb_insert(&thread->cluster_by_id, cluster);
1167 talloc_set_destructor(cluster, _process_redis_cluster_free);
1168
1171 "modules.redis.cluster_bootstrap", true, &cluster->trigger_args, cluster);
1172
1173 rctx->cluster = cluster;
1176}
1177
1178RECV(cluster_map_get)
1179{
1180 process_redis_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_redis_rctx_t);
1181 fr_pair_t *vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_worker_id);
1183
1184 rctx->worker_id = vp ? vp->vp_int32 : 0;
1185
1186 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_cluster_id);
1187 fr_fatal_assert_msg(vp, "Missing %s", attr_redis_cluster_id->name);
1188
1189 find.cluster_id = vp->vp_uint16;
1190 fr_rb_find((void **)&rctx->cluster, &rctx->thread->cluster_by_id, &find);
1191 fr_fatal_assert_msg(rctx->cluster, "Update requested for cluster %d which has not been bootstrapped",
1192 vp->vp_uint16);
1193
1194 /*
1195 * Cluster map fetching failed, and the retry timer is armed.
1196 * Tell the caller that the map has failed.
1197 */
1198 if (fr_timer_armed(rctx->cluster->ev)) {
1199 return process_redis_return_failed(request, rctx->cluster, rctx->worker_id);
1200 }
1201
1202 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_redis_force_update);
1203 if ((fr_time_to_sec(fr_time()) == fr_time_to_sec(rctx->cluster->last_update)) && (!vp || !vp->vp_bool)) {
1204 RWARN("Cluster was updated less than a second ago, returning last response");
1205 return process_redis_return_existing(request, rctx->cluster, rctx->worker_id);
1206 }
1207
1210 &rctx->cluster->trigger_cp[FR_REDIS_CLUSTER_MAP_GET], "modules.redis.cluster_get", true,
1211 &rctx->cluster->trigger_args, rctx->cluster);
1214 }
1215
1217 NULL, 0, UNLANG_SUB_FRAME, rctx);
1218}
1219
1220static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1221{
1222 fr_process_state_t const *state;
1223 process_redis_t *inst = talloc_get_type_abort(mctx->mi->data, process_redis_t);
1224 process_redis_thread_t *thread = talloc_get_type_abort(mctx->thread, process_redis_thread_t);
1225 process_redis_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_redis_rctx_t);
1226
1228
1230 fr_assert(FR_REDIS_PACKET_CODE_VALID(request->packet->code));
1231
1232 request->component = "redis";
1233 request->module = NULL;
1234 fr_assert(request->proto_dict == dict_redis);
1235
1236 UPDATE_STATE(packet);
1237
1238 if (!state->recv) {
1239 REDEBUG("Invalid packet type (%u)", request->packet->code);
1241 }
1242
1243 rctx->inst = inst;
1244 rctx->thread = thread;
1245
1246 return state->recv(p_result, mctx, request);
1247}
1248
1250{
1251 process_redis_thread_t *t = talloc_get_type_abort(mctx->thread, process_redis_thread_t);
1252
1253 t->el = mctx->el;
1256 return 0;
1257}
1258
1259static int mod_instantiate(module_inst_ctx_t const *mctx)
1260{
1261 process_redis_t *inst = talloc_get_type_abort(mctx->mi->data, process_redis_t);
1262
1263 inst->inst_name = mctx->mi->name;
1264 inst->conf = mctx->mi->conf;
1265 return 0;
1266}
1267
1268static int mod_load(void)
1269{
1270 if (redis_dict_init() < 0) return -1;
1271
1273 return 0;
1274}
1275
1276static fr_process_state_t const process_state[] = {
1278 .default_reply = FR_REDIS_CLUSTER_MAP_UPDATE,
1279 .default_rcode = RLM_MODULE_NOOP,
1280 .recv = recv_cluster_map_bootstrap,
1281 },
1283 .default_reply = FR_REDIS_CLUSTER_MAP_UPDATE,
1284 .default_rcode = RLM_MODULE_NOOP,
1285 .recv = recv_cluster_map_get,
1286 }
1287};
1288
1291 .common = {
1292 .magic = MODULE_MAGIC_INIT,
1293 .name = "redis",
1294 .config = config,
1295 .onload = mod_load,
1296 .instantiate = mod_instantiate,
1300 .thread_instantiate = mod_thread_instantiate,
1301 },
1302 .process = mod_process,
1303 .dict = &dict_redis,
1304 .packet_type = &attr_redis_packet_type
1305};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ 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
static int const char char buffer[256]
Definition acutest.h:576
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:673
#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
Definition cf_parse.h:280
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:309
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:610
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
fr_redis_ct_t * fr_redis_ct_alloc(TALLOC_CTX *ctx, CONF_SECTION *tls_cs, fr_event_list_t *el, fr_redis_conf_t *conf, fr_redis_trunk_active_t active, void *active_uctx, bool active_oneshot)
Allocate per-thread, per-cluster instance.
Thread local state for a cluster.
#define KEY_SLOTS
Maximum number of keyslots (should not change).
int fr_coord_to_worker_reply_broadcast(request_t *request)
Send a reply list from a coordinator to all workers.
Definition coord_pair.c:792
int fr_coord_to_worker_reply_send(request_t *request, uint32_t worker_id)
Send a reply list from a coordinator to a worker.
Definition coord_pair.c:764
fr_coord_pair_t * fr_coord_pair_request_coord_pair(request_t *request)
Return the coord_pair associated with a coord_pair internal request.
Definition coord_pair.c:883
int fr_coord_pair_coord_request_start(fr_coord_pair_t *coord_pair, fr_pair_list_t *list, fr_time_t now)
Start a coordinator request to run through a coord_pair process module.
Definition coord_pair.c:898
#define fr_fatal_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:229
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:222
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:316
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:329
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:335
Specifies an attribute which must be present for the module to function.
Definition dict.h:315
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:328
Test enumeration values.
Definition dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
#define 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
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:108
free(array)
talloc_free(hp)
int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port)
Definition inet.c:944
char * fr_inet_ntop(char out[static FR_IPADDR_STRLEN], size_t outlen, fr_ipaddr_t const *addr)
Print the address portion of a fr_ipaddr_t.
Definition inet.c:1025
fr_cmp_ret_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition inet.c:1353
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition inet.h:89
IPv4/6 prefix.
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:2008
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2561
#define UNLANG_SUB_FRAME
Definition interpret.h:37
static fr_dict_t const * dict_freeradius
Definition base.c:37
fr_dict_attr_t const * attr_packet_type
Definition base.c:91
fr_dict_attr_t const * attr_redis_node_role
Definition redis.c:74
fr_dict_attr_t const * attr_redis_bootstrap_node
Definition redis.c:62
fr_dict_attr_t const * attr_redis_use_tls
Definition redis.c:76
fr_dict_attr_t const * attr_redis_slot_end
Definition redis.c:70
fr_dict_attr_t const * attr_redis_force_update
Definition redis.c:75
fr_dict_attr_t const * attr_redis_packet_type
Definition redis.c:59
fr_dict_attr_t const * attr_redis_log_prefix
Definition redis.c:60
fr_dict_attr_t const * attr_redis_node
Definition redis.c:71
fr_dict_attr_t const * attr_redis_node_port
Definition redis.c:73
fr_dict_attr_t const * attr_redis_slot
Definition redis.c:68
fr_dict_t const * dict_redis
Definition redis.c:51
fr_dict_attr_t const * attr_redis_slot_start
Definition redis.c:69
fr_dict_attr_t const * attr_redis_max_nodes
Definition redis.c:61
fr_dict_attr_t const * attr_redis_password
Definition redis.c:65
fr_dict_attr_t const * attr_redis_bootstrap_port
Definition redis.c:63
fr_dict_attr_t const * attr_redis_cluster_id
Definition redis.c:66
fr_dict_attr_t const * attr_redis_tls_conf
Definition redis.c:77
fr_dict_attr_t const * attr_redis_username
Definition redis.c:64
fr_dict_attr_t const * attr_redis_node_endpoint
Definition redis.c:72
fr_dict_attr_t const * attr_redis_shard
Definition redis.c:67
char const * password
to authenticate to Redis.
Definition io.h:56
char const * hostname
Definition io.h:51
char const * log_prefix
Definition io.h:57
uint16_t port
Definition io.h:52
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define RERROR(fmt,...)
Definition log.h:315
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
#define fr_time()
Definition event.c:60
Stores all information relating to an event list.
Definition event.c:377
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition log.h:69
unsigned short uint16_t
@ FR_TYPE_INT32
32 Bit signed integer.
unsigned int uint32_t
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2300
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:708
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1298
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:291
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:47
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2744
int fr_pair_prepend_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and prepend)
Definition pair.c:1444
fr_redis_pipeline_status_t redis_command_set_enqueue(fr_redis_trunk_t *rtrunk, fr_redis_command_set_t *cmds)
Enqueue a command set on a specific trunk.
Definition pipeline.c:535
fr_redis_command_set_t * fr_redis_command_set_alloc(TALLOC_CTX *ctx, request_t *request, fr_redis_command_set_complete_t complete, fr_redis_command_set_fail_t fail, void *rctx, bool autofree)
Allocate a new command set.
Definition pipeline.c:252
void fr_redis_command_set_cancel(fr_redis_command_set_t *cmds)
Cancel a command set.
Definition pipeline.c:566
fr_redis_pipeline_status_t fr_redis_command_literal_add(fr_redis_command_set_t *cmds, char const *cmd_str, fr_redis_command_complete_t complete, void *rctx)
Add a literal command to the command set.
Definition pipeline.c:404
fr_redis_trunk_t * fr_redis_trunk_alloc(fr_redis_ct_t *rtcluster, fr_redis_io_conf_t const *io_conf, fr_pair_list_t *trigger_args, fr_redis_trunk_active_t active, void *active_uctx, bool active_oneshot)
Allocate a new trunk.
Definition pipeline.c:991
int fr_redis_command_set_clear(fr_redis_command_set_t *cmds)
Reinitialise a command set so that it can be used again.
Definition pipeline.c:1106
Represents a single command.
Definition pipeline.c:62
Represents a collection of pipelined commands.
Definition pipeline.c:94
@ FR_REDIS_PIPELINE_OK
No failure.
Definition pipeline.h:44
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition base.c:168
static fr_process_state_t const process_state[]
Definition base.c:68
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition base.c:348
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition base.c:213
static int mod_load(void)
Definition base.c:229
static const conf_parser_t config[]
Definition base.c:162
int32_t worker_id
The worker which sent the data leading to this request.
Definition base.c:182
bool in_cluster
Has the node been found in the latest cluster map.
Definition base.c:67
static unlang_action_t process_redis_return_existing(request_t *request, process_redis_cluster_t *cluster, uint32_t worker_id)
Definition base.c:1010
fr_rb_node_t node
Definition base.c:98
uint64_t current_epoch
Redis cluster epoch as reported by this node.
Definition base.c:69
static void redis_cluster_map_get_timeout(UNUSED fr_timer_list_t *el, UNUSED fr_time_t now, void *uctx)
Definition base.c:659
bool cluster_ok
Does CLUSTER INFO say the cluster is OK.
Definition base.c:171
static void redis_cluster_info_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Definition base.c:586
uint16_t port
Port of the first bootstrap server.
Definition base.c:79
bool fetching
The map is being fetched.
Definition base.c:85
static const uint32_t redis_shards_version
Definition base.c:54
static uint16_t cluster_id
Definition base.c:34
static int _process_redis_cluster_free(process_redis_cluster_t *cluster)
Definition base.c:986
map_get_status_t status
Status of the node calls.
Definition base.c:170
fr_rb_node_t cluster_by_id
Entry in the tree of clusters by ID.
Definition base.c:82
uint64_t cluster_epoch
Largest epoch value returned by any node.
Definition base.c:186
process_redis_node_t * current_node
Node currently being queried.
Definition base.c:184
process_redis_t const * inst
Module instance.
Definition base.c:179
static int fr_redis_cluster_shards_nodes_to_pairs(redisReply *reply, fr_pair_t *shard_vp)
Convert the "nodes" array in CLUSTER SHARDS replies into pairs.
Definition base.c:255
fr_coord_pair_t * coord_pair
The coord_pair which requested this cluster map.
Definition base.c:88
static void redis_cluster_slots_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Definition base.c:638
fr_dlist_head_t nodes
List of current nodes in the cluster.
Definition base.c:80
request_t * request
Definition base.c:97
fr_redis_trunk_t * trunk
Trunk connection for this node.
Definition base.c:66
static unlang_action_t process_redis_return_failed(request_t *request, process_redis_cluster_t *cluster, uint32_t worker_id)
Send a Cluster-Failed message to a worker.
Definition base.c:696
static void process_redis_pending_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition base.c:997
#define FR_REDIS_PACKET_CODE_VALID(_code)
Definition base.c:189
uint32_t version
Redis version on this node.
Definition base.c:68
fr_dlist_t entry
In list of resume contexts.
Definition base.c:169
fr_timer_t * ev
Timeout event for this node.
Definition base.c:174
fr_redis_ct_t * rtcluster
Cluster used to allocate redis trunk connections.
Definition base.c:83
fr_dlist_t entry
Entry in the list of cluster nodes.
Definition base.c:64
static fr_cmp_ret_t process_redis_pending_cmp(void const *a, void const *b)
Definition base.c:121
fr_event_list_t * el
Definition base.c:141
char const * inst_name
Definition base.c:136
CONF_SECTION * conf
Definition base.c:137
fr_rb_tree_t pending
Requests waiting for custer map update.
Definition base.c:87
fr_redis_command_set_t * cmds
Command set for fetching cluster map.
Definition base.c:173
static fr_dict_attr_t const * attr_worker_id
Definition base.c:45
static fr_cmp_ret_t cluster_server_cmp(void const *a, void const *b)
Definition base.c:101
fr_process_module_t process_redis
Definition base.c:1290
process_redis_cluster_t * cluster
Cluster which is being updated.
Definition base.c:183
bool failed
Has the cluster failed.
Definition base.c:89
static void redis_cluster_map_get_cancel(request_t *request, UNUSED fr_signal_t action, void *uctx)
Definition base.c:974
fr_time_t last_update
When was the map last updated.
Definition base.c:86
fr_time_delta_t retry_interval
Definition base.c:134
fr_redis_io_conf_t io_conf
Connection config for this node.
Definition base.c:65
static unlang_action_t redis_cluster_map_get(UNUSED unlang_result_t *p_result, request_t *request, void *uctx)
Definition base.c:711
uint16_t cluster_id
Numeric ID assigned by the coordinator.
Definition base.c:77
fr_time_delta_t refresh_interval
Definition base.c:135
fr_redis_conf_t * conf
Redis config for this cluster.
Definition base.c:76
static fr_cmp_ret_t cluster_id_cmp(void const *a, void const *b)
Definition base.c:113
static int process_redis_cluster_node_add(TALLOC_CTX *ctx, process_redis_cluster_t *cluster, process_redis_t const *inst, fr_redis_conf_t *conf, fr_pair_t *host_vp, fr_pair_t *port_vp)
Definition base.c:532
static void redis_cluster_info_server_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Definition base.c:574
fr_rb_node_t cluster_by_server
Entry in the tree of clusters by bootstrap server.
Definition base.c:81
fr_dict_attr_autoload_t process_redis_dict_attr[]
Definition base.c:48
fr_time_delta_t timeout
Definition base.c:133
fr_pair_list_t cluster_pairs
Pairs built from the last fetch.
Definition base.c:84
process_redis_sections_t sections
Definition base.c:130
module_method_t const * method
Definition base.c:131
process_redis_node_t * node
Node being queried.
Definition base.c:168
fr_timer_t * ev
Timer event for retry / refresh.
Definition base.c:90
fr_pair_list_t trigger_args
Pair list to pass to cluster triggers.
Definition base.c:91
static unlang_action_t redis_cluster_map_get_resume(UNUSED unlang_result_t *p_result, request_t *request, void *uctx)
Definition base.c:766
static int fr_redis_cluster_shards_to_pairs(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *list, redisReply *reply)
Convert the reply to CLUSTER SHARDS into pairs.
Definition base.c:361
map_get_status_t
State of cluster map fetching from each node.
Definition base.c:156
@ CLUSTER_MAP_GET_MAP
Definition base.c:159
@ CLUSTER_MAP_GET_INFO
Definition base.c:157
@ CLUSTER_MAP_GET_FAILED
Definition base.c:161
@ CLUSTER_MAP_GOT_MAP
Definition base.c:160
@ CLUSTER_MAP_GET_TIMEOUT
Definition base.c:162
@ CLUSTER_MAP_GOT_INFO
Definition base.c:158
fr_pair_list_t list
To populate with parsed reply data.
Definition base.c:172
CONF_PAIR * trigger_cp[FR_REDIS_CODE_MAX]
Cached trigger conf pairs;.
Definition base.c:92
unlang_result_t result
Where results are written to.
Definition base.c:181
process_redis_thread_t * thread
Thread instance.
Definition base.c:180
fr_rb_tree_t cluster_by_id
Tree of clusters by ID.
Definition base.c:143
fr_ipaddr_t addr
IP address of the first bootstrap server.
Definition base.c:78
fr_pair_list_t trigger_args
Pair list to pass to trigger.
Definition base.c:70
trunk_conf_t trunk_conf
Definition base.c:132
fr_dict_autoload_t process_redis_dict[]
Definition base.c:39
fr_dlist_head_t rctx_list
List of per-node resume contexts.
Definition base.c:185
CONF_SECTION * cluster_map_get
Definition base.c:58
fr_rb_tree_t cluster_by_server
Tree of clusters by primary bootstrap server.
Definition base.c:142
bool no_trigger[FR_REDIS_CODE_MAX]
Cached trigger conf status;.
Definition base.c:93
static void redis_cluster_map_get_refetch(UNUSED fr_timer_list_t *tl, fr_time_t now, void *uctx)
Definition base.c:668
static int fr_redis_cluster_slots_to_pairs(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *list, redisReply *reply)
Convert the reply to CLUSER SLOTS into pairs.
Definition base.c:454
static int fr_redis_cluster_shards_slots_to_pairs(redisReply *reply, fr_pair_t *shard_vp, bool slots_covered[KEY_SLOTS])
Convert the "slots" array in CLUSTER SHARDS replies into pairs.
Definition base.c:210
Coordinator representation of a Redis cluster.
Definition base.c:75
Resume context for node specific calls.
Definition base.c:167
Individual cluster node.
Definition base.c:63
Resume context for Redis requests.
Definition base.c:178
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition process.h:55
module_t common
Common fields for all loadable modules.
Common public symbol definition for all process modules.
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG_ENABLED2()
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
static rs_t * conf
Definition radsniff.c:52
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:718
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:850
void fr_rb_iter_delete_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
Definition rb.c:925
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:876
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:178
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
The main red black tree structure.
Definition rb.h:71
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx, fr_redis_rcode_t status)
Print the response data in a useful treelike form.
Definition redis.c:153
#define REDIS_VERSION(_max, _min, _patch)
Definition base.h:53
uint8_t max_nodes
Maximum number of cluster nodes to connect to.
Definition base.h:124
int redis_dict_init(void)
Load the Redis dictionaries.
Definition redis.c:136
uint32_t fr_redis_version_num(char const *version)
Convert version string into a 32bit unsigned integer for comparisons.
Definition redis.c:569
@ FR_REDIS_CLUSTER_MAP_BOOTSTRAP
Definition base.h:102
@ FR_REDIS_CODE_MAX
Definition base.h:106
@ FR_REDIS_CLUSTER_MAP_GET
Definition base.h:103
@ FR_REDIS_CLUSTER_MAP_UPDATE
Definition base.h:104
@ FR_REDIS_CLUSTER_MAP_FAIL
Definition base.h:105
fr_table_num_sorted_t const redis_reply_types[]
Definition redis.c:31
int fr_redis_parse_version(char *out, size_t out_len, redisReply *reply)
Parse the reply from the Redis command INFO SERVER to extract the version.
Definition redis.c:535
@ REDIS_RCODE_SUCCESS
Operation was successful.
Definition base.h:70
Configuration parameters for a redis connection.
Definition base.h:114
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define fr_sbuff_adv_to_str_literal(_sbuff, _len, _needle)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_init_in(_out, _start, _len_or_end)
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition schedule.c:105
#define MODULE_THREAD_INST(_ctype)
Definition module.h:260
char const * name
Instance name e.g. user_database.
Definition module.h:359
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:353
void * data
Module's instance data.
Definition module.h:295
unlang_action_t(* module_method_t)(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Module section callback.
Definition module.h:69
#define MODULE_RCTX(_ctype)
Definition module.h:261
#define MODULE_INST(_ctype)
Definition module.h:259
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_asprintf
Definition talloc.h:151
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition time.h:731
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_gt(_a, _b)
Definition time.h:237
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
int fr_timer_delete(fr_timer_t **ev_p)
Delete a timer event and free its memory.
Definition timer.c:692
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define fr_timer_in(...)
Definition timer.h:87
static bool fr_timer_armed(fr_timer_t *ev)
Definition timer.h:120
int trigger(unlang_interpret_t *intp, CONF_SECTION const *cs, CONF_PAIR **trigger_cp, char const *name, bool rate_limit, fr_pair_list_t *args, void const *uctx)
Execute a trigger - call an executable to process an event.
Definition trigger.c:160
int module_trigger_args_build(TALLOC_CTX *ctx, fr_pair_list_t *list, CONF_SECTION const *cs, module_trigger_args_t *args)
Build trigger args pair list for modules.
Definition trigger.c:504
Common values used by modules when building trigger args.
Definition trigger.h:42
conf_parser_t const trunk_config[]
Config parser definitions to populate a trunk_conf_t.
Definition trunk.c:345
Common configuration parameters for a trunk.
Definition trunk.h:234
static fr_event_list_t * el
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:281
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define fr_pair_list_append_by_da(_ctx, _vp, _list, _attr, _val, _tainted)
Append a pair to a list, assigning its value.
Definition pair.h:306
size_t fr_pair_list_num_elements(fr_pair_list_t const *list)
Get the length of a list of fr_pair_t.