The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rlm_redis.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 (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 1945c0eb6b9ff24e7ddec69d0f23ed365bb7dc9f $
19 * @file rlm_redis.c
20 * @brief Driver for the Redis noSQL key value store.
21 *
22 * @author Gabriel Blanchard
23 *
24 * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 * @copyright 2011 TekSavvy Solutions (gabe@teksavvy.com)
26 * @copyright 2000,2006,2015 The FreeRADIUS server project
27 */
28
29RCSID("$Id: 1945c0eb6b9ff24e7ddec69d0f23ed365bb7dc9f $")
30
31#include <assert.h>
32#include <stdint.h>
33
34#include <freeradius-devel/redis/base.h>
35#include <freeradius-devel/redis/cluster_async.h>
36
37#include <freeradius-devel/server/modpriv.h>
38#include <freeradius-devel/server/module_rlm.h>
39#include <freeradius-devel/io/coord_pair.h>
40
41#include <freeradius-devel/unlang/xlat_func.h>
42
43#include <freeradius-devel/util/base16.h>
44#include <freeradius-devel/util/debug.h>
45#include <freeradius-devel/util/types.h>
46
47static fr_dict_t const *dict_redis;
48
51 { .out = &dict_redis, .proto = "redis" },
53};
54
55/** A lua function or stored procedure we make available as an xlat
56 *
57 */
58typedef struct {
59 char const *name; //!< Friendly name for the function. Used to register the equivalent xlat.
60 char digest[(SHA1_DIGEST_LENGTH * 2) + 1]; //!< pre-computed hash of lua code.
61 char const *body; //!< the actual lua code.
62 bool read_only; //!< Function has no side effects
64
65/** Instance of a redis lua func xlat
66 *
67 */
68typedef struct {
69 redis_lua_func_t *func; //!< Function configuration.
71
72
73typedef struct {
74 redis_lua_func_t **funcs; //!< Array of functions to register.
75
77
78/** rlm_redis module instance
79 *
80 */
81typedef struct {
82 fr_redis_conf_t conf; //!< Connection parameters for the Redis server.
83 //!< Must be first field in this struct.
84
85 CONF_SECTION *tls_conf; //!< TLS CONF_SECTION
86
87 rlm_redis_lua_t lua; //!< Array of functions to register.
88
89 fr_coord_reg_t *coord_reg; //!< Coordinator registration.
90 fr_coord_pair_reg_t *coord_pair_reg; //!< Coord pair registration.
92
93typedef struct {
94 rlm_redis_t const *inst; //!< Module instance.
95 fr_redis_ct_t *rtcluster; //!< Per thread Redis cluster.
96 fr_coord_worker_t *cw; //!< Coord-worker for fetching cluster map.
98
99/** Resume context for redis lua xlat
100 */
101typedef struct {
102 redis_lua_func_t const *func; //!< Lua function
103 TALLOC_CTX *ctx; //!< Context to allocate boxes.
104 fr_value_box_list_t out; //!< List to store boxes in callback.
105 xlat_action_t action; //!< Xlat action set in callback.
106 fr_redis_command_set_t *cmds; //!< Command set for this xlat.
107 fr_redis_async_cmd_t *cmd; //!< Async command context.
109
110/** Resume context for redis xlat
111 */
112typedef struct {
113 bool read_only; //!< Should the xlat be run read only.
114 bool forced_node; //!< Was the xlat called with a specific node.
115 TALLOC_CTX *ctx; //!< Context to allocate boxes.
116 fr_value_box_list_t out; //!< List to store boxes in callback.
117 xlat_action_t action; //!< Xlat action set in callback.
118 fr_redis_command_set_t *cmds; //!< Command set for this xlat.
119 fr_redis_async_cmd_t *cmd; //!< Async command context.
121
122#define REDIS_XLAT_CMD_SETUP(_cmds, _argc, _argv, _arg_len, _rctx, _read_only, _func) \
123if (_read_only && \
124 (fr_redis_command_literal_add(_cmds, "READONLY", redis_xlat_status_check, _rctx) != FR_REDIS_PIPELINE_OK)) \
125 return XLAT_ACTION_FAIL; \
126if (fr_redis_command_argv_add(_cmds, _argc, _argv, _arg_len, _func, _rctx) != FR_REDIS_PIPELINE_OK) \
127 return XLAT_ACTION_FAIL; \
128if (_read_only && \
129 (fr_redis_command_literal_add(_cmds, "READWRITE", redis_xlat_status_check, _rctx) != FR_REDIS_PIPELINE_OK)) \
130 return XLAT_ACTION_FAIL
131
132static int lua_func_body_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
133
136 { FR_CONF_OFFSET("read_only", redis_lua_func_t, read_only) },
138};
139
143 .subcs_type = "redis_lua_func_t", .name2 = CF_IDENT_ANY },
145};
146
152
153/** Do basic processing for a lua function body and compute its sha1 hash
154 *
155 */
156static int lua_func_body_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
157{
158 int ret;
159 redis_lua_func_t *func = talloc_get_type_abort(parent, redis_lua_func_t);
160 char const *body;
161 fr_sha1_ctx sha1_ctx;
163
164 /*
165 * Get the function name from name2
166 * of the enclosing function section.
167 */
169 if (unlikely(!func->name)) {
170 cf_log_err(cf_parent(ci), "functions must be declared as \"function <name> {\"");
171 return -1;
172 }
173
174 /*
175 * Perform normal string parsing first
176 */
177 if ((ret = cf_pair_parse_value(ctx, out, parent, ci, rule)) < 0) return ret;
178 body = *((char **)out);
179
180 fr_sha1_init(&sha1_ctx);
181 fr_sha1_update(&sha1_ctx, (uint8_t const *)body, talloc_strlen(body));
182 fr_sha1_final(digest, &sha1_ctx);
183 fr_base16_encode(&FR_SBUFF_OUT(func->digest, sizeof(func->digest)), &FR_DBUFF_TMP(digest, sizeof(digest)));
184
185 if (DEBUG_ENABLED3) cf_log_debug(ci, "sha1 hash of function is %pV", fr_box_strvalue_len(func->digest, sizeof(func->digest) - 1));
186
187 return 0;
188}
189
190/** Callback to check redis replied with "OK" when expected.
191 */
192static void redis_xlat_status_check(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
193{
194 rlm_redis_xlat_rctx_t *xlat_rctx = talloc_get_type_abort(rctx, rlm_redis_xlat_rctx_t);
195
196 if (reply->type != REDIS_REPLY_STATUS) {
197 RWARN("Did not receive expected redis status reply");
198 xlat_rctx->action = XLAT_ACTION_FAIL;
199 return;
200 }
201
202 if (strcmp(reply->str, "OK") != 0) {
203 RERROR("Running \"%s\" returned %s", fr_redis_command_get_cmd(cmd), reply->str);
204 xlat_rctx->action = XLAT_ACTION_FAIL;
205 }
206}
207
208/** Callback to check redis replied with "PONG" when expected.
209 */
210static void redis_xlat_ping_check(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
211{
212 rlm_redis_xlat_rctx_t *xlat_rctx = talloc_get_type_abort(rctx, rlm_redis_xlat_rctx_t);
213
214 if (reply->type != REDIS_REPLY_STATUS) {
215 RWARN("Did not receive expected redis status reply");
216 return;
217 }
218
219 if (strcmp(reply->str, "PONG") != 0) {
220 RERROR("Running \"%s\" returned %s", fr_redis_command_get_cmd(cmd), reply->str);
221 return;
222 }
223 xlat_rctx->action = XLAT_ACTION_DONE;
224}
225
226/** Common cancellation for redis xlats
227 *
228 */
229static void redis_xlat_cancel(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
230{
231 rlm_redis_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_redis_xlat_rctx_t);
232
233 RDEBUG2("Forcibly cancelling pending redis command");
235}
236
238 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
239{
240 rlm_redis_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_redis_xlat_rctx_t);
241 fr_value_box_t *vb = NULL;
242
243 if (rctx->action != XLAT_ACTION_DONE) {
244 RPERROR("PING after cluter remap failed");
245 return rctx->action;
246 }
247
248 MEM(vb = fr_value_box_alloc_null(ctx));
249 switch (fr_redis_command_set_rcode(rctx->cmds)) {
251 fr_value_box_strdup(vb, vb, NULL, "success", false);
252 break;
253 default:
254 fr_value_box_strdup(vb, vb, NULL, "fail", false);
255 }
256
258
259 return XLAT_ACTION_DONE;
260}
261
262/** Force a redis cluster remap
263 *
264@verbatim
265%redis.remap()
266@endverbatim
267 *
268 * @ingroup xlat_functions
269 */
271 xlat_ctx_t const *xctx,
272 request_t *request, UNUSED fr_value_box_list_t *in)
273{
275 rlm_redis_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_redis_thread_t);
276
280
281 if (!inst->conf.use_cluster_map) {
282 RWARN("Cluster map not in use");
283 return XLAT_ACTION_DONE;
284 }
285
286 if (fr_redis_ct_map_get(thread->rtcluster, thread->cw,
287 inst->coord_pair_reg, true) == REDIS_ASYNC_RCODE_ERROR) {
288 RPEDEBUG("Failed to initiate cluster remap");
289 return XLAT_ACTION_FAIL;
290 }
291
292 /*
293 * Since cluster remap is out of band, using the coordinator thread, queue up
294 * a "PING" command which will run after the remap.
295 * In addition, if the cluster map has not been previously fetched, this will
296 * bootstrap the cluster map fetching.
297 * The xlat will return after the remap has completed.
298 */
299 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), rlm_redis_xlat_rctx_t));
300 rctx->ctx = ctx;
301 rctx->action = XLAT_ACTION_FAIL;
302
303 MEM(cmds = fr_redis_command_set_alloc(rctx, request, NULL, NULL, NULL, false));
304 rctx->cmds = cmds;
306 talloc_free(rctx);
307 return XLAT_ACTION_FAIL;
308 }
309
310 rctx->cmd = fr_redis_async_cmd_start(unlang_interpret_frame_talloc_ctx(request), request, &ret,
311 thread->rtcluster, NULL, 0, cmds, rctx->read_only, NULL);
312
313 REDIS_ASYNC_START_RCODE_PROCESS(ret, thread->rtcluster, thread->cw, inst->coord_pair_reg,
314 "Failed to enqueue redis PING", XLAT_ACTION_FAIL)
315
317}
318
320 { .required = true, .single = true, .type = FR_TYPE_STRING },
321 { .single = true, .type = FR_TYPE_UINT32 },
323};
324
325/** Return the node that is currently servicing a particular key
326 *
327@verbatim
328%redis.node(<key>[, <index>])
329@endverbatim
330 *
331 * @ingroup xlat_functions
332 */
334 xlat_ctx_t const *xctx,
335 request_t *request, fr_value_box_list_t *in)
336{
337 rlm_redis_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_redis_thread_t);
338
339 fr_redis_ct_key_slot_t const *key_slot;
340 fr_redis_ct_node_t const *node;
341 char const *ipaddr;
342 uint16_t port;
343
344 unsigned long idx = 0;
345 fr_value_box_t *vb, *key, *idx_vb;
346 XLAT_ARGS(in, &key, &idx_vb);
347
348 if (idx_vb) idx = idx_vb->vb_uint32;
349
350 key_slot = fr_redis_ct_slot_by_key(thread->rtcluster, request, (uint8_t const *)key->vb_strvalue,
351 key->vb_length);
352 if (idx == 0) {
353 node = fr_redis_ct_master(thread->rtcluster, key_slot);
354 } else {
355 node = fr_redis_ct_replica(thread->rtcluster, key_slot, idx - 1);
356 }
357
358 if (!node) {
359 RDEBUG2("No node available for this key slot");
360 return XLAT_ACTION_DONE;
361 }
362
363 ipaddr = fr_redis_ct_ipaddr(node);
364 if (!ipaddr || (fr_redis_ct_port(&port, node) < 0)) {
365 REDEBUG("Failed retrieving node information");
366 return XLAT_ACTION_FAIL;
367 }
368
369 MEM(vb = fr_value_box_alloc_null(ctx));
370 fr_value_box_asprintf(vb, vb, NULL, false, "%s:%u", ipaddr, port);
372
373 return XLAT_ACTION_DONE;
374}
375
377 { .required = true, .single = true, .type = FR_TYPE_UINT64 }, /* key count */
378 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .concat = true, .type = FR_TYPE_STRING }, /* keys and args */
380};
381
382/** Cancellation of Redis Lua load / exec
383 *
384 */
385static void redis_lua_cancel(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
386{
387 rlm_redis_lua_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_redis_lua_xlat_rctx_t);
388
389 RDEBUG2("Forcibly cancelling pending redis lua command");
391}
392
393static xlat_action_t redis_lua_func_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
394 UNUSED request_t *request, UNUSED fr_value_box_list_t *in);
395
396/** Process the results of loading a lua script to a redis server
397 *
398 * If the load succeeds, re-enqueue the original EVALSHA command.
399 */
401 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
402{
403 rlm_redis_lua_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_redis_lua_xlat_rctx_t);
404
405 if (rctx->action != XLAT_ACTION_DONE) {
406 RPERROR("Failed loading lua script");
407 return rctx->action;
408 }
409
410 /*
411 * Now the script has loaded, re-run the EVALSHA command on the same trunk
412 */
415 return XLAT_ACTION_FAIL;
416 }
417
419}
420
421/** Callback to verify reply to SCRIPT LOAD
422 */
423static void redis_lua_load_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
424{
425 rlm_redis_lua_xlat_rctx_t *xlat_rctx = talloc_get_type_abort(rctx, rlm_redis_lua_xlat_rctx_t);
426
427 if (reply->type != REDIS_REPLY_STRING) {
428 REDEBUG("Unexpected reply type after loading function");
429 return;
430 }
431
432 if (strcmp(reply->str, xlat_rctx->func->digest) == 0) {
433 RDEBUG3("Script \"%s\" loaded", xlat_rctx->func->name);
434 xlat_rctx->action = XLAT_ACTION_DONE;
435 return;
436 }
437
438 REDEBUG("Function digest %s, does not match calculated digest %s", reply->str, xlat_rctx->func->digest);
439}
440
441/** Callback to convert redis reply to value boxes
442 */
443static void redis_lua_xlat_results(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
444{
445 rlm_redis_lua_xlat_rctx_t *xlat_rctx = talloc_get_type_abort(rctx, rlm_redis_lua_xlat_rctx_t);
446 fr_value_box_t *vb;
447
448 MEM(vb = fr_value_box_alloc_null(xlat_rctx->ctx));
449 if (fr_redis_reply_to_value_box(xlat_rctx->ctx, vb, reply, FR_TYPE_VOID, NULL, false, false) < 0) {
450 RPERROR("Failed processing reply to %s", fr_redis_command_get_cmd(cmd));
451 return;
452 }
453
454 if (vb->type == FR_TYPE_GROUP) {
455 fr_value_box_t *child_vb = NULL;
456 while ((child_vb = fr_value_box_list_pop_head(&vb->vb_group))) fr_value_box_list_insert_tail(&xlat_rctx->out, child_vb);
457 talloc_free(vb);
458 } else {
459 fr_value_box_list_insert_tail(&xlat_rctx->out, vb);
460 }
461 xlat_rctx->action = XLAT_ACTION_DONE;
462}
463
464/** Process the results from calling a lua script
465 *
466 * Enqueuing SCRIPT LOAD ... if the server reports that the script is missing
467 */
469 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
470{
471 rlm_redis_lua_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_redis_lua_xlat_rctx_t);
472 fr_value_box_t *vb = NULL;
473
474 switch (fr_redis_command_set_rcode(rctx->cmds)) {
476 {
478 rlm_redis_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_redis_thread_t);
479
480 if (inst->conf.use_cluster_map) fr_redis_ct_map_get(thread->rtcluster, thread->cw,
481 inst->coord_pair_reg, false);
482 }
484
488
492
494 PERROR("Server returned error");
495 return XLAT_ACTION_FAIL;
496
498 {
500 char const **argv;
501 size_t *argv_len;
502
503 RWARN("Script \"%s\" not on the server", rctx->func->name);
504
505 MEM(cmds = fr_redis_command_set_alloc(rctx, request, NULL, NULL, NULL, false));
506 MEM(argv = talloc_array(cmds, char const *, 3));
507 MEM(argv_len = talloc_array(cmds, size_t, 3));
508
509 argv[0] = "SCRIPT";
510 argv_len[0] = sizeof("SCRIPT") - 1;
511 argv[1] = "LOAD";
512 argv_len[1] = sizeof("LOAD") - 1;
513 argv[2] = rctx->func->body;
514 argv_len[2] = talloc_strlen(rctx->func->body);
515
516 REDIS_XLAT_CMD_SETUP(cmds, 3, argv, argv_len, rctx, false, redis_lua_load_results);
517
519 return XLAT_ACTION_FAIL;
520 }
521
523 }
524 default:
525 break;
526 }
527 if (rctx->action != XLAT_ACTION_DONE) {
528 RPERROR("Failed executing lua script");
529 return rctx->action;
530 }
531 while ((vb = fr_value_box_list_pop_head(&rctx->out))) fr_dcursor_append(out, vb);
532
533 return XLAT_ACTION_DONE;
534}
535
536/** Call a lua function on the redis server
537 *
538 * Lua functions either get uploaded when the trunk connection becomes active or the first
539 * time they get executed.
540 */
542 xlat_ctx_t const *xctx,
543 request_t *request, fr_value_box_list_t *in)
544{
545 rlm_redis_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_redis_t);
546 rlm_redis_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_redis_thread_t);
548 redis_lua_func_t *func = xlat_inst->func;
549
553
554 char const **argv;
555 size_t *arg_len;
556 size_t argc;
557 fr_value_box_t *key_vb;
558 char *key_count;
559 uint8_t const *key = NULL;
560 size_t key_len = 0;
561
562 argc = fr_value_box_list_num_elements(in);
563 if (argc > MAX_REDIS_ARGS) {
564 REDEBUG("Too many arguments (%ld)", argc);
565 return XLAT_ACTION_FAIL;
566 }
567 argc += 2;
568
570 rctx->ctx = ctx;
571 rctx->action = XLAT_ACTION_FAIL;
572 rctx->func = func;
573 fr_value_box_list_init(&rctx->out);
574
575 MEM(cmds = fr_redis_command_set_alloc(rctx, request, NULL, NULL, NULL, false));
576 rctx->cmds = cmds;
577
578 MEM(argv = talloc_array(cmds, char const *, argc));
579 MEM(arg_len = talloc_array(cmds, size_t, argc));
580
581 /*
582 * Try EVALSHA first, and if that fails fall back to SCRIPT LOAD
583 */
584 argv[0] = talloc_strdup(argv, "EVALSHA");
585 arg_len[0] = sizeof("EVALSHA") - 1;
586 argv[1] = func->digest;
587 arg_len[1] = sizeof(func->digest) - 1;
588
589 /*
590 * First argument is always the key count
591 */
592 key_vb = fr_value_box_list_pop_head(in);
593 fr_assert(key_vb);
594 arg_len[2] = fr_value_box_aprint(argv, &key_count, key_vb, NULL);
595 if (unlikely(!key_count)) {
596 RPERROR("Failed converting key count to string");
597 return XLAT_ACTION_FAIL;
598 }
599 argv[2] = key_count;
600
601 argc = 3;
603 /*
604 * Fixup null or empty arguments to be
605 * zero length strings so that the position
606 * of subsequent arguments are maintained.
607 */
608 if (!fr_type_is_string(vb->type)) {
609 argv[argc] = "";
610 arg_len[argc++] = 0;
611 continue;
612 }
613
614 argv[argc] = talloc_strdup(argv, vb->vb_strvalue);
615 arg_len[argc++] = vb->vb_length;
616 }
617
618 /*
619 * For eval commands all keys should hash to the same redis instance
620 * so we just use the first key (the arg after the key count).
621 */
622 if (argc > 3) {
623 key = (uint8_t const *)argv[3];
624 key_len = arg_len[3];
625 }
626
627 REDIS_XLAT_CMD_SETUP(cmds, argc, argv, arg_len, rctx, func->read_only, redis_lua_xlat_results);
628
629 rctx->cmd = fr_redis_async_cmd_start(rctx, request, &ret, thread->rtcluster, key, key_len,
630 cmds, func->read_only, NULL);
631
632 REDIS_ASYNC_START_RCODE_PROCESS(ret, thread->rtcluster, thread->cw, inst->coord_pair_reg,
633 "Failed enqueing lua command", XLAT_ACTION_FAIL)
634
636}
637
638/** Copies the function configuration into xlat function instance data
639 *
640 */
642{
643 redis_lua_func_inst_t *inst = talloc_get_type_abort(xctx->inst, redis_lua_func_inst_t);
644
645 inst->func = talloc_get_type_abort(xctx->uctx, redis_lua_func_t);
646
647 return 0;
648}
649
651 { .required = true, .concat = true, .type = FR_TYPE_STRING },
652 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .concat = true, .type = FR_TYPE_STRING },
654};
655
656/** Callback to convert redis reply to value boxes
657 */
658static void redis_xlat_results(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
659{
660 rlm_redis_xlat_rctx_t *xlat_rctx = talloc_get_type_abort(rctx, rlm_redis_xlat_rctx_t);
661 fr_value_box_t *vb;
662
663 MEM(vb = fr_value_box_alloc_null(xlat_rctx->ctx));
664 if (fr_redis_reply_to_value_box(xlat_rctx->ctx, vb, reply, FR_TYPE_VOID, NULL, false, false) < 0) {
665 RPERROR("Failed processing reply to %s", fr_redis_command_get_cmd(cmd));
666 return;
667 }
668
669 if (vb->type == FR_TYPE_GROUP) {
670 fr_value_box_t *child_vb = NULL;
671 while ((child_vb = fr_value_box_list_pop_head(&vb->vb_group))) fr_value_box_list_insert_tail(&xlat_rctx->out, child_vb);
672 talloc_free(vb);
673 } else {
674 fr_value_box_list_insert_tail(&xlat_rctx->out, vb);
675 }
676 xlat_rctx->action = XLAT_ACTION_DONE;
677}
678
679static xlat_action_t redis_xlat_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
680 request_t *request, UNUSED fr_value_box_list_t *in)
681{
682 rlm_redis_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_redis_xlat_rctx_t);
683 fr_value_box_t *vb = NULL;
684
685 switch (fr_redis_command_set_rcode(rctx->cmds)) {
687 {
689 rlm_redis_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_redis_thread_t);
690
691 if (inst->conf.use_cluster_map) fr_redis_ct_map_get(thread->rtcluster, thread->cw,
692 inst->coord_pair_reg, false);
693 }
695
697 if (rctx->forced_node) goto error;
700
704
706 error:
707 RPERROR("Server returned error");
708 return XLAT_ACTION_FAIL;
709
710 default:
711 break;
712 }
713
714 if (rctx->action != XLAT_ACTION_DONE) {
715 RPERROR("Failed executing Redis command");
716 return rctx->action;
717 }
718
719 while ((vb = fr_value_box_list_pop_head(&rctx->out))) fr_dcursor_append(out, vb);
720
721 return XLAT_ACTION_DONE;
722}
723
724/** Xlat to make calls to redis
725 *
726@verbatim
727%redis(<redis command>)
728@endverbatim
729 *
730 * @ingroup xlat_functions
731 */
733 xlat_ctx_t const *xctx,
734 request_t *request, fr_value_box_list_t *in)
735{
737 rlm_redis_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_redis_thread_t);
738 uint8_t const *key = NULL;
739 size_t key_len = 0;
740
741 fr_value_box_t *first = fr_value_box_list_head(in);
742 fr_sbuff_t sbuff = FR_SBUFF_IN(first->vb_strvalue, first->vb_length);
743
744 int argc = 0;
745 char const **argv;
746 size_t *arg_len;
750 fr_redis_ct_node_t *node = NULL;
751
752 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), rlm_redis_xlat_rctx_t));
753 rctx->ctx = ctx;
754 rctx->action = XLAT_ACTION_FAIL;
755 fr_value_box_list_init(&rctx->out);
756
757 if (fr_sbuff_next_if_char(&sbuff, '-')) rctx->read_only = true;
758
759 /*
760 * Hack to allow querying against a specific node for testing
761 */
762 if (fr_sbuff_next_if_char(&sbuff, '@')) {
763 fr_ipaddr_t node_addr;
764 uint16_t port;
766
767 RDEBUG3("Overriding node selection");
768
769 if (fr_inet_pton_port(&node_addr, &port,
770 fr_sbuff_current(&sbuff), fr_sbuff_remaining(&sbuff),
771 AF_UNSPEC, true, true) < 0) {
772 RPEDEBUG("Failed parsing node address");
773 return XLAT_ACTION_FAIL;
774 }
775
776 fr_inet_ntop(buff, sizeof(buff), &node_addr);
778 .hostname = buff,
779 .port = port
780 });
781 if (!node) {
782 RPEDEBUG("Failed locating cluster node");
783 return XLAT_ACTION_FAIL;
784 }
785
786 fr_value_box_list_talloc_free_head(in); /* Remove and free server arg */
787 rctx->forced_node = true;
788 }
789
790 MEM(cmds = fr_redis_command_set_alloc(rctx, request, NULL, NULL, NULL, false));
791 rctx->cmds = cmds;
792
793 argc = fr_value_box_list_num_elements(in);
794 MEM(argv = talloc_array(cmds, char const *, argc));
795 MEM(arg_len = talloc_array(cmds, size_t, argc));
796
797 argc = 0;
799 if (!fr_type_is_string(vb->type)) {
800 argv[argc] = talloc_strdup(argv, "");
801 arg_len[argc++] = 0;
802 continue;
803 }
804
805 if ((argc == 0) && rctx->read_only && !rctx->forced_node) {
806 argv[argc] = talloc_strndup(argv, vb->vb_strvalue + 1, vb->vb_length - 1);
807 arg_len[argc] = vb->vb_length - 1;
808 } else {
809 argv[argc] = talloc_strndup(argv, vb->vb_strvalue, vb->vb_length);
810 arg_len[argc] = vb->vb_length;
811 }
812 argc++;
813 }
814
815 /*
816 * If we've got multiple arguments, the second one is usually the key.
817 * The Redis docs say commands should be analysed first to get key
818 * positions, but this involves sending them to the server, which is
819 * just as expensive as sending them to the wrong server and receiving
820 * a redirect.
821 */
822 if (argc > 1) {
823 key = (uint8_t const *)argv[1];
824 key_len = arg_len[1];
825 }
826
827 REDIS_XLAT_CMD_SETUP(cmds, argc, argv, arg_len, rctx, rctx->read_only, redis_xlat_results);
828
829 rctx->cmd = fr_redis_async_cmd_start(unlang_interpret_frame_talloc_ctx(request), request, &ret,
830 thread->rtcluster, key, key_len, cmds, rctx->read_only, node);
831
832 REDIS_ASYNC_START_RCODE_PROCESS(ret, thread->rtcluster, thread->cw, inst->coord_pair_reg,
833 "Failed enqueueing Redis command", XLAT_ACTION_FAIL)
834
836}
837
839 UNUSED redisReply *reply, void *rctx)
840{
841 redis_lua_func_t *func = talloc_get_type_abort(rctx, redis_lua_func_t);
842 DEBUG2("Loaded lua function \"%s\" onto node", func->name);
843}
844
845static void lua_script_load(fr_redis_trunk_t *rtrunk, void *uctx)
846{
847 rlm_redis_thread_t *thread = talloc_get_type_abort(uctx, rlm_redis_thread_t);
849
850 MEM(cmds = fr_redis_command_set_alloc(rtrunk, NULL, NULL, NULL, NULL, true));
851
852 talloc_foreach(thread->inst->lua.funcs, func) {
853 char const **argv;
854 size_t *argv_len;
855
856 MEM(argv = talloc_array(cmds, char const *, 3));
857 MEM(argv_len = talloc_array(cmds, size_t, 3));
858
859 argv[0] = "SCRIPT";
860 argv_len[0] = sizeof("SCRIPT") - 1;
861 argv[1] = "LOAD";
862 argv_len[1] = sizeof("LOAD") - 1;
863 argv[2] = func->body;
864 argv_len[2] = talloc_strlen(func->body);
865
866 if (fr_redis_command_argv_add(cmds, 3, argv, argv_len,
868 talloc_free(cmds);
869 return;
870 };
871 }
872
873 if (redis_command_set_enqueue(rtrunk, cmds) != FR_REDIS_PIPELINE_OK) {
874 ERROR("Failed to enqueue lua function loading");
875 talloc_free(cmds);
876 }
877}
878
879
881{
882 rlm_redis_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_redis_thread_t);
883 rlm_redis_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
884
885 if (talloc_array_length(inst->lua.funcs) == 0) {
886 t->rtcluster = fr_redis_ct_alloc(t, inst->tls_conf, mctx->el, &inst->conf, NULL, NULL, false);
887 } else {
888 t->rtcluster = fr_redis_ct_alloc(t, inst->tls_conf, mctx->el, &inst->conf, lua_script_load, t, true);
889 }
890 if (!t->rtcluster) return -1;
891 t->inst = inst;
892
893 return 0;
894}
895
897{
898 rlm_redis_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_redis_thread_t);
899 rlm_redis_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
900
901 if (!inst->conf.use_cluster_map) return 0;
902
903 t->cw = fr_coord_attach(t, mctx->el, inst->coord_reg);
904
905 if (!t->cw) {
906 ERROR("Failed to attach to coordinator");
907 return -1;
908 }
909
910 if ((inst->conf.trunk_conf.start == 0) || (fr_schedule_worker_id() != 0)) return 0;
911
912 return fr_redis_ct_map_bootstrap(t->rtcluster, t->cw, inst->coord_pair_reg);
913}
914
916
917static int mod_instantiate(module_inst_ctx_t const *mctx)
918{
919 rlm_redis_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
920
921 inst->conf.log_prefix = mctx->mi->name;
922 inst->conf.module_name = mctx->mi->module->name;
923 inst->conf.inst_name = mctx->mi->name;
924
925 if (inst->conf.use_tls) {
926 inst->tls_conf = cf_section_find(mctx->mi->conf, "tls", CF_IDENT_ANY);
927
928 if (!inst->tls_conf) {
929 cf_log_err(mctx->mi->conf, "Missing tls section");
930 return -1;
931 }
932 }
933
934 if (!inst->conf.use_cluster_map) return 0;
935
936 if (inst->conf.database) {
937 cf_log_err(mctx->mi->conf, "Cannot set Redis database number when cluster in use");
938 return -1;
939 }
940
942 .name = mctx->mi->name,
943 .worker_cb = worker_pair_callbacks,
944 .cb_id = REDIS_COORD_PAIR_CALLBACK_ID,
945 .root = fr_dict_root(dict_redis),
946 .cs = mctx->mi->conf,
947 }
948 );
949 if (!inst->coord_pair_reg) return -1;
950
951 FR_COORD_PAIR_CB_CTX_SET(coord_callbacks, worker_callbacks, inst->coord_pair_reg);
952
954 .name = mctx->mi->name,
955 .coord_cb = coord_callbacks,
956 .worker_cb = worker_callbacks,
957 .mi = mctx->mi
958 });
959
960 if (!inst->coord_reg) return -1;
961
962 return 0;
963}
964
966{
967 rlm_redis_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_redis_thread_t);
968
969 if (!t->cw) return 0;
970
971 fr_coord_detach(t->cw, true);
972 t->cw = NULL;
973 return 0;
974}
975
976static int mod_detach(module_detach_ctx_t const *mctx)
977{
978 rlm_redis_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
979
980 if (!inst->conf.use_cluster_map) return 0;
981
982 fr_coord_deregister(inst->coord_reg);
983 talloc_free(inst->coord_pair_reg);
984 return 0;
985}
986
987static int mod_bootstrap(module_inst_ctx_t const *mctx)
988{
989 rlm_redis_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
990 xlat_t *xlat;
991
992 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, redis_xlat, FR_TYPE_VOID);
995
996 /*
997 * %redis.node(<key>[, idx])
998 */
999 if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "node", redis_node_xlat, FR_TYPE_STRING)) == NULL)) return -1;
1001
1002 if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "remap", redis_remap_xlat, FR_TYPE_STRING)) == NULL)) return -1;
1003
1004 /*
1005 * Loop over the lua functions, registering an xlat
1006 * that'll call that function specifically.
1007 */
1008 talloc_foreach(inst->lua.funcs, func) {
1009 if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, func->name, redis_lua_func_xlat, FR_TYPE_VOID)) == NULL)) return -1;
1013 }
1014
1015 return 0;
1016}
1017
1018static int mod_load(void)
1019{
1021
1022 return redis_dict_init();
1023}
1024
1025extern module_rlm_t rlm_redis;
1027 .common = {
1028 .magic = MODULE_MAGIC_INIT,
1029 .name = "redis",
1030 .inst_size = sizeof(rlm_redis_t),
1032 .onload = mod_load,
1033 .bootstrap = mod_bootstrap,
1034 .instantiate = mod_instantiate,
1035 .coord_attach = mod_coord_attach,
1036 .detach = mod_detach,
1038 .thread_instantiate = mod_thread_instantiate,
1039 .thread_detach = mod_thread_detach,
1040 }
1041};
#define fr_base16_encode(_out, _in)
Definition base16.h:54
#define RCSID(id)
Definition build.h:560
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM *ci, conf_parser_t const *rule)
Parses a CONF_PAIR into a C data type.
Definition cf_parse.c:213
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:623
#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_SUBSECTION_ALLOC(_name, _type, _flags, _struct, _field, _subcs)
A conf_parser_t multi-subsection.
Definition cf_parse.h:369
char const * name2
Second identifier for CONF_SECTION.
Definition cf_parse.h:608
#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
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:446
@ CONF_FLAG_OK_MISSING
OK if it's missing.
Definition cf_parse.h:454
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:423
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
Common header for all CONF_* types.
Definition cf_priv.h:54
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1362
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1204
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:695
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:345
#define cf_parent(_cf)
Definition cf_util.h:118
#define cf_log_debug(_cf, _fmt,...)
Definition cf_util.h:348
#define CF_IDENT_ANY
Definition cf_util.h:80
int fr_redis_ct_map_bootstrap(fr_redis_ct_t *rtcluster, fr_coord_worker_t *cw, fr_coord_pair_reg_t *coord_pair_reg)
Initiate bootstrapping of the cluster map.
fr_redis_async_cmd_t * fr_redis_async_cmd_start(TALLOC_CTX *ctx, request_t *request, fr_redis_async_rcode_t *rcode, fr_redis_ct_t *rtcluster, uint8_t const *key, size_t key_len, fr_redis_command_set_t *cmds, bool read_only, fr_redis_ct_node_t *node)
Start running a command set on an async redis cluster.
fr_redis_async_rcode_t fr_redis_ct_map_get(fr_redis_ct_t *rtcluster, fr_coord_worker_t *cw, fr_coord_pair_reg_t *coord_pair_reg, bool force)
Initiate updating of the cluster map.
void fr_redis_async_cmd_cancel(fr_redis_async_cmd_t *cmd)
Cancel a Redis async command.
fr_redis_ct_node_t * fr_redis_ct_node_by_addr(fr_redis_ct_t *rtcluster, fr_redis_io_conf_t *ioconf)
fr_redis_async_rcode_t fr_redis_async_cmd_resend(fr_redis_async_cmd_t *cmd)
Re-submit a redis async command set.
fr_redis_ct_node_t const * fr_redis_ct_replica(fr_redis_ct_t *rtcluster, fr_redis_ct_key_slot_t const *key_slot, uint8_t replica_num)
Return the replica node that would be used for a particular key slot.
fr_redis_trunk_t * fr_redis_async_cmd_trunk(fr_redis_async_cmd_t *cmd)
Fetch the redis trunk a command is associated with.
fr_redis_ct_key_slot_t const * fr_redis_ct_slot_by_key(fr_redis_ct_t *rtcluster, request_t *request, uint8_t const *key, size_t key_len)
Resolve key to key slot.
fr_redis_async_rcode_t fr_redis_async_cmd_redirect(fr_redis_async_cmd_t *cmd)
Re-submit a redis async command set on a different node.
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.
int fr_redis_ct_port(uint16_t *out, fr_redis_ct_node_t const *node)
Return the port of a particular node.
fr_redis_ct_node_t const * fr_redis_ct_master(fr_redis_ct_t *rtcluster, fr_redis_ct_key_slot_t const *key_slot)
Return the master node that would be used for a particular key slot.
char const * fr_redis_ct_ipaddr(fr_redis_ct_node_t const *node)
Return the ipaddr of a particular node.
Structure for holding the state of an async redis command set.
Thread local state for a cluster.
#define REDIS_ASYNC_COORD_CALLBACKS(_thread_type)
#define REDIS_ASYNC_START_RCODE_PROCESS(_rcode, _cluster, _cw, _coord_pair_reg, _error_msg, _error_ret)
Convenience macro to reduce boilerplate.
fr_coord_reg_t * fr_coord_register(fr_coord_reg_ctx_t *reg_ctx)
Register a coordinator.
Definition coord.c:137
fr_coord_worker_t * fr_coord_attach(TALLOC_CTX *ctx, fr_event_list_t *el, fr_coord_reg_t *coord_reg)
Attach a worker to a coordinator.
Definition coord.c:662
void fr_coord_deregister(fr_coord_reg_t *coord_reg)
De-register a coordinator.
Definition coord.c:172
int fr_coord_detach(fr_coord_worker_t *cw, bool exiting)
Signal a coordinator that a worker wants to detach.
Definition coord.c:623
A coordinator registration.
Definition coord.c:85
The worker end of worker <-> coordinator communication.
Definition coord.c:73
fr_coord_pair_reg_t * fr_coord_pair_register(fr_coord_pair_reg_ctx_t *reg_ctx)
Register a set of callbacks for pair list based coordinator messages.
Definition coord_pair.c:113
struct fr_coord_pair_reg_s fr_coord_pair_reg_t
Definition coord_pair.h:32
#define FR_COORD_PAIR_CB_CTX_SET(_in_cb, _out_cb, _reg)
Set up ctx on pair list callbacks.
Definition coord_pair.h:93
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:522
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2637
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static xlat_action_t redis_remap_xlat(TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Force a redis cluster remap.
Definition rlm_redis.c:270
static xlat_action_t redis_node_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Return the node that is currently servicing a particular key.
Definition rlm_redis.c:333
static xlat_action_t redis_xlat(TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Xlat to make calls to redis.
Definition rlm_redis.c:732
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
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition inet.h:89
IPv4/6 prefix.
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2053
#define PERROR(_fmt,...)
Definition log.h:233
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define RERROR(fmt,...)
Definition log.h:315
#define RPERROR(fmt,...)
Definition log.h:319
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:264
unsigned short uint16_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned char uint8_t
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
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
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
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:234
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
char const * fr_redis_command_get_cmd(fr_redis_command_t *cmd)
Definition pipeline.c:979
fr_redis_async_rcode_t fr_redis_command_set_rcode(fr_redis_command_set_t *cmds)
Extract the rcode from a command set.
Definition pipeline.c:994
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:532
int fr_redis_command_set_reset(fr_redis_command_set_t *cmds)
Reset a command set to it's state before enqueuing.
Definition pipeline.c:1012
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:249
fr_redis_pipeline_status_t fr_redis_command_argv_add(fr_redis_command_set_t *cmds, size_t argc, char const **argv, size_t *argv_len, fr_redis_command_complete_t complete, void *rctx)
Add a command with arguments to the command set.
Definition pipeline.c:436
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:401
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 const conf_parser_t config[]
Definition base.c:162
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
int fr_redis_reply_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, redisReply *reply, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, bool box_error, bool shallow))
Convert a string or integer type to fr_value_box_t of specified type.
Definition redis.c:222
int redis_dict_init(void)
Load the Redis dictionaries.
Definition redis.c:136
#define REDIS_COMMON_CONFIG
Definition base.h:143
fr_redis_async_rcode_t
Definition base.h:80
@ REDIS_ASYNC_RCODE_MOVE
Attempt operation on an alternative node with remap.
Definition base.h:88
@ REDIS_ASYNC_RCODE_ERROR
Unrecoverable error.
Definition base.h:82
@ REDIS_ASYNC_RCODE_ASK
Attempt operation on an alternative node.
Definition base.h:87
@ REDIS_ASYNC_RCODE_TRY_AGAIN
Try the operation again.
Definition base.h:86
@ REDIS_ASYNC_RCODE_NO_SCRIPT
Script doesn't exist.
Definition base.h:89
@ REDIS_ASYNC_RCODE_SUCCESS
Operation was successful.
Definition base.h:81
#define MAX_REDIS_ARGS
Definition base.h:45
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition redis.c:107
Configuration parameters for a redis connection.
Definition base.h:114
static void redis_lua_xlat_results(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Callback to convert redis reply to value boxes.
Definition rlm_redis.c:443
fr_dict_autoload_t rlm_redis_dict[]
Definition rlm_redis.c:50
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_redis.c:976
static int mod_load(void)
Definition rlm_redis.c:1018
fr_redis_async_cmd_t * cmd
Async command context.
Definition rlm_redis.c:119
rlm_redis_lua_t lua
Array of functions to register.
Definition rlm_redis.c:87
static void lua_script_load(fr_redis_trunk_t *rtrunk, void *uctx)
Definition rlm_redis.c:845
static int mod_coord_attach(module_thread_inst_ctx_t const *mctx)
Definition rlm_redis.c:896
static conf_parser_t module_lua[]
Definition rlm_redis.c:140
TALLOC_CTX * ctx
Context to allocate boxes.
Definition rlm_redis.c:115
redis_lua_func_t const * func
Lua function.
Definition rlm_redis.c:102
static void redis_xlat_ping_check(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Callback to check redis replied with "PONG" when expected.
Definition rlm_redis.c:210
TALLOC_CTX * ctx
Context to allocate boxes.
Definition rlm_redis.c:103
xlat_action_t action
Xlat action set in callback.
Definition rlm_redis.c:117
module_rlm_t rlm_redis
Definition rlm_redis.c:1026
static xlat_action_t redis_xlat_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_redis.c:679
bool read_only
Should the xlat be run read only.
Definition rlm_redis.c:113
fr_redis_conf_t conf
Connection parameters for the Redis server.
Definition rlm_redis.c:82
static xlat_action_t redis_remap_xlat_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_redis.c:237
static int redis_lua_func_instantiate(xlat_inst_ctx_t const *xctx)
Copies the function configuration into xlat function instance data.
Definition rlm_redis.c:641
fr_redis_command_set_t * cmds
Command set for this xlat.
Definition rlm_redis.c:106
static xlat_action_t redis_lua_load_resume(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Process the results of loading a lua script to a redis server.
Definition rlm_redis.c:400
fr_value_box_list_t out
List to store boxes in callback.
Definition rlm_redis.c:104
redis_lua_func_t ** funcs
Array of functions to register.
Definition rlm_redis.c:74
fr_coord_reg_t * coord_reg
Coordinator registration.
Definition rlm_redis.c:89
rlm_redis_t const * inst
Module instance.
Definition rlm_redis.c:94
char digest[(SHA1_DIGEST_LENGTH *2)+1]
pre-computed hash of lua code.
Definition rlm_redis.c:60
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_redis.c:987
static void redis_lua_load_results(request_t *request, UNUSED fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Callback to verify reply to SCRIPT LOAD.
Definition rlm_redis.c:423
redis_lua_func_t * func
Function configuration.
Definition rlm_redis.c:69
static xlat_action_t redis_lua_func_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Process the results from calling a lua script.
Definition rlm_redis.c:468
static conf_parser_t module_lua_func[]
Definition rlm_redis.c:134
static void redis_xlat_cancel(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
Common cancellation for redis xlats.
Definition rlm_redis.c:229
char const * body
the actual lua code.
Definition rlm_redis.c:61
fr_redis_command_set_t * cmds
Command set for this xlat.
Definition rlm_redis.c:118
char const * name
Friendly name for the function. Used to register the equivalent xlat.
Definition rlm_redis.c:59
static fr_dict_t const * dict_redis
Definition rlm_redis.c:47
fr_coord_pair_reg_t * coord_pair_reg
Coord pair registration.
Definition rlm_redis.c:90
static void redis_lua_cancel(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
Cancellation of Redis Lua load / exec.
Definition rlm_redis.c:385
#define REDIS_XLAT_CMD_SETUP(_cmds, _argc, _argv, _arg_len, _rctx, _read_only, _func)
Definition rlm_redis.c:122
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Definition rlm_redis.c:880
fr_coord_worker_t * cw
Coord-worker for fetching cluster map.
Definition rlm_redis.c:96
static xlat_arg_parser_t const redis_node_xlat_args[]
Definition rlm_redis.c:319
static void redis_xlat_results(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Callback to convert redis reply to value boxes.
Definition rlm_redis.c:658
fr_value_box_list_t out
List to store boxes in callback.
Definition rlm_redis.c:116
static void lua_script_load_results(UNUSED request_t *request, UNUSED fr_redis_command_t *cmd, UNUSED redisReply *reply, void *rctx)
Definition rlm_redis.c:838
bool forced_node
Was the xlat called with a specific node.
Definition rlm_redis.c:114
static void redis_xlat_status_check(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Callback to check redis replied with "OK" when expected.
Definition rlm_redis.c:192
static xlat_action_t redis_lua_func_xlat(TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Call a lua function on the redis server.
Definition rlm_redis.c:541
static xlat_arg_parser_t const redis_args[]
Definition rlm_redis.c:650
static int lua_func_body_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Do basic processing for a lua function body and compute its sha1 hash.
Definition rlm_redis.c:156
xlat_action_t action
Xlat action set in callback.
Definition rlm_redis.c:105
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition rlm_redis.c:965
CONF_SECTION * tls_conf
TLS CONF_SECTION.
Definition rlm_redis.c:85
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_redis.c:917
fr_redis_async_cmd_t * cmd
Async command context.
Definition rlm_redis.c:107
static conf_parser_t module_config[]
Definition rlm_redis.c:147
fr_redis_ct_t * rtcluster
Per thread Redis cluster.
Definition rlm_redis.c:95
static xlat_arg_parser_t const redis_lua_func_args[]
Definition rlm_redis.c:376
bool read_only
Function has no side effects.
Definition rlm_redis.c:62
Instance of a redis lua func xlat.
Definition rlm_redis.c:68
A lua function or stored procedure we make available as an xlat.
Definition rlm_redis.c:58
Resume context for redis lua xlat.
Definition rlm_redis.c:101
rlm_redis module instance
Definition rlm_redis.c:81
Resume context for redis xlat.
Definition rlm_redis.c:112
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2178
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
int fr_schedule_worker_id(void)
Return the worker id for the current thread.
Definition schedule.c:110
#define MODULE_THREAD_INST(_ctype)
Definition module.h:258
char const * name
Instance name e.g. user_database.
Definition module.h:357
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
void fr_sha1_init(fr_sha1_ctx *context)
Definition sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition sha1.c:105
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
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
eap_aka_sim_process_conf_t * inst
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_strndup(_ctx, _str, _len)
Definition talloc.h:150
#define talloc_foreach(_array, _iter)
Iterate over a talloced array of elements.
Definition talloc.h:79
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
Definition xlat.h:138
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:384
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:147
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:171
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumed by an xlat function.
Definition xlat.h:146
static fr_slen_t parent
Definition pair.h:858
#define fr_type_is_string(_x)
Definition types.h:348
int fr_value_box_asprintf(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, bool tainted, char const *fmt,...)
Print a formatted string using our internal printf wrapper and assign it to a value box.
Definition value.c:4731
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4643
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:655
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1030
void * rctx
Resume context.
Definition xlat_ctx.h:54
void const * inst
xlat instance data.
Definition xlat_ctx.h:50
void * uctx
Passed to the registration function.
Definition xlat_ctx.h:66
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
void * inst
xlat instance data to populate.
Definition xlat_ctx.h:63
An xlat calling ctx.
Definition xlat_ctx.h:49
An xlat instantiation ctx.
Definition xlat_ctx.h:62
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition xlat_func.c:401
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:374
#define xlat_func_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx)
Set a callback for global instantiation of xlat functions.
Definition xlat_func.h:94
@ XLAT_FUNC_FLAG_MODULE_STATUS
Definition xlat_func.h:40