The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_redis.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: a393f98a94b4a32ca24ecc1c59adc2e0c5b668df $
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 
29 RCSID("$Id: a393f98a94b4a32ca24ecc1c59adc2e0c5b668df $")
30 
31 #include <assert.h>
32 #include <stdint.h>
33 
34 #include <freeradius-devel/redis/base.h>
35 #include <freeradius-devel/redis/cluster.h>
36 
37 #include <freeradius-devel/server/base.h>
38 #include <freeradius-devel/server/cf_util.h>
39 #include <freeradius-devel/server/modpriv.h>
40 #include <freeradius-devel/server/module_rlm.h>
41 #include <freeradius-devel/server/pool.h>
42 
43 #include <freeradius-devel/unlang/xlat.h>
44 #include <freeradius-devel/unlang/xlat_func.h>
45 
46 #include <freeradius-devel/util/base16.h>
47 #include <freeradius-devel/util/debug.h>
48 #include <freeradius-devel/util/log.h>
49 #include <freeradius-devel/util/talloc.h>
50 #include <freeradius-devel/util/types.h>
51 #include <freeradius-devel/util/value.h>
52 
53 /** A lua function or stored procedure we make available as an xlat
54  *
55  */
56 typedef struct {
57  char const *name; //!< Friendly name for the function. Used to register the equivalent xlat.
58  char digest[(SHA1_DIGEST_LENGTH * 2) + 1]; //!< pre-computed hash of lua code.
59  char const *body; //!< the actual lua code.
60  bool read_only; //!< Function has no side effects
62 
63 /** Instance of a redis lua func xlat
64  *
65  */
66 typedef struct {
67  redis_lua_func_t *func; //!< Function configuration.
69 
70 
71 typedef struct {
72  redis_lua_func_t **funcs; //!< Array of functions to register.
73 
75 
76 /** rlm_redis module instance
77  *
78  */
79 typedef struct {
80  fr_redis_conf_t conf; //!< Connection parameters for the Redis server.
81  //!< Must be first field in this struct.
82 
83  rlm_redis_lua_t lua; //!< Array of functions to register.
84 
85  fr_redis_cluster_t *cluster; //!< Redis cluster.
86 } rlm_redis_t;
87 
88 static int lua_func_body_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
89 
91  { FR_CONF_OFFSET("body", redis_lua_func_t, body), .func = lua_func_body_parse },
92  { FR_CONF_OFFSET("read_only", redis_lua_func_t, read_only) },
94 };
95 
99  .subcs_type = "redis_lua_func_t", .name2 = CF_IDENT_ANY },
101 };
102 
104  { FR_CONF_OFFSET_SUBSECTION("lua", 0, rlm_redis_t, lua, module_lua) },
107 };
108 
109 /** Do basic processing for a lua function body and compute its sha1 hash
110  *
111  */
112 static int lua_func_body_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
113 {
114  int ret;
115  redis_lua_func_t *func = talloc_get_type_abort(parent, redis_lua_func_t);
116  char const *body;
117  fr_sha1_ctx sha1_ctx;
118  uint8_t digest[SHA1_DIGEST_LENGTH];
119 
120  /*
121  * Get the function name from name2
122  * of the enclosing function section.
123  */
125  if (unlikely(!func->name)) {
126  cf_log_err(cf_parent(ci), "functions must be declared as \"function <name> {\"");
127  return -1;
128  }
129 
130  /*
131  * Perform normal string parsing first
132  */
133  if ((ret = cf_pair_parse_value(ctx, out, parent, ci, rule)) < 0) return ret;
134  body = *((char **)out);
135 
136  fr_sha1_init(&sha1_ctx);
137  fr_sha1_update(&sha1_ctx, (uint8_t const *)body, talloc_array_length(body) - 1);
138  fr_sha1_final(digest, &sha1_ctx);
139  fr_base16_encode(&FR_SBUFF_OUT(func->digest, sizeof(func->digest)), &FR_DBUFF_TMP(digest, sizeof(digest)));
140 
141  if (DEBUG_ENABLED3) cf_log_debug(ci, "sha1 hash of function is %pV", fr_box_strvalue_len(func->digest, sizeof(func->digest) - 1));
142 
143  return 0;
144 }
145 
146 /** Issue a command against redis and get a response
147  *
148  * This is a convenience function for dealing with commands which made need to execute against an
149  * ldap replica. It temporarily places the connection in readonly mode to allow commands to be
150  * run against ldap replicas, then reverts back to readwrite mode.
151  *
152  * @param[out] status_out Where to write the status from the command.
153  * @param[out] reply_out Where to write the reply associated with the highest priority status.
154  * @param[in] request The current request.
155  * @param[in] conn to issue commands with.
156  * @param[in] read_only wrap command in READONLY/READWRITE.
157  * @param[in] argc Redis command argument count.
158  * @param[in] argv Redis command arguments.
159  * @param[in] arg_len Optional array of redis command argument length.
160  * @return
161  * - 0 success.
162  * - -1 normal failure.
163  * - -2 failure that may leave the connection in a READONLY state.
164  */
165 static int redis_command(fr_redis_rcode_t *status_out, redisReply **reply_out,
166  request_t *request, fr_redis_conn_t *conn,
167  bool read_only,
168  int argc, char const **argv, size_t arg_len[])
169 {
170  bool maybe_more = false;
171  redisReply *reply;
172  fr_redis_rcode_t status;
173 
174  *reply_out = NULL;
175 
176  if (read_only) redisAppendCommand(conn->handle, "READONLY");
177  redisAppendCommandArgv(conn->handle, argc, argv, arg_len);
178  if (read_only) {
179  redisAppendCommand(conn->handle, "READWRITE");
180  } else goto parse_reply;
181 
182 
183  /*
184  * Process the response for READONLY
185  */
186  reply = NULL; /* Doesn't set reply to NULL on error *sigh* */
187  if (redisGetReply(conn->handle, (void **)&reply) == REDIS_OK) maybe_more = true;
188  status = fr_redis_command_status(conn, reply);
189  if (status != REDIS_RCODE_SUCCESS) {
190  ROPTIONAL(REDEBUG, ERROR, "Setting READONLY failed");
191 
192  *reply_out = reply;
193  *status_out = status;
194 
195  if (maybe_more) {
196  if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) return -1;
197  fr_redis_reply_free(&reply);
198  if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) return -1;
199  fr_redis_reply_free(&reply);
200  }
201  return -1;
202  }
203 
204  fr_redis_reply_free(&reply);
205 
206 parse_reply:
207  /*
208  * Process the response for the command
209  */
210  reply = NULL; /* Doesn't set reply to NULL on error *sigh* */
211  if ((redisGetReply(conn->handle, (void **)&reply) == REDIS_OK) && read_only) maybe_more = true;
212  status = fr_redis_command_status(conn, reply);
213  if (status != REDIS_RCODE_SUCCESS) {
214  *reply_out = reply;
215  *status_out = status;
216 
217  if (maybe_more) {
218  if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) return -1;
219  fr_redis_reply_free(&reply);
220  }
221  return -1;
222  }
223 
224  *reply_out = reply;
225  reply = NULL;
226  *status_out = status;
227 
228  if (!read_only) return 0; /* No more responses to deal with */
229 
230  /*
231  * Process the response for READWRITE
232  */
233  reply = NULL; /* Doesn't set reply to NULL on error *sigh* */
234  if ((redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) ||
235  (fr_redis_command_status(conn, reply) != REDIS_RCODE_SUCCESS)) {
236  ROPTIONAL(REDEBUG, ERROR, "Setting READWRITE failed");
237 
238  fr_redis_reply_free(&reply); /* There could be a response we need to free */
239  fr_redis_reply_free(reply_out);
240  *reply_out = reply;
241  *status_out = status;
242 
243  return -2;
244  }
245  fr_redis_reply_free(&reply); /* Free READWRITE response */
246 
247  return 0;
248 }
249 
251  { .required = true, .concat = true, .type = FR_TYPE_STRING },
253 };
254 
255 /** Force a redis cluster remap
256  *
257 @verbatim
258 %redis.remap(<redis server ip>:<redis server port>)
259 @endverbatim
260  *
261  * @ingroup xlat_functions
262  */
264  xlat_ctx_t const *xctx,
265  request_t *request, fr_value_box_list_t *in)
266 {
268 
269  fr_socket_t node_addr;
270  fr_pool_t *pool;
271  fr_redis_conn_t *conn;
273  fr_value_box_t *vb;
274  fr_value_box_t *in_head = fr_value_box_list_head(in);
275 
276  if (fr_inet_pton_port(&node_addr.inet.dst_ipaddr, &node_addr.inet.dst_port, in_head->vb_strvalue, in_head->vb_length,
277  AF_UNSPEC, true, true) < 0) {
278  RPEDEBUG("Failed parsing node address");
279  return XLAT_ACTION_FAIL;
280  }
281 
282  if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &node_addr, true) < 0) {
283  RPEDEBUG("Failed locating cluster node");
284  return XLAT_ACTION_FAIL;
285  }
286 
287  conn = fr_pool_connection_get(pool, request);
288  if (!conn) {
289  REDEBUG("No connections available for cluster node");
290  return XLAT_ACTION_FAIL;
291  }
292 
293  rcode = fr_redis_cluster_remap(request, inst->cluster, conn);
294  switch (rcode) {
296  fr_pool_connection_close(pool, request, conn);
297  break;
298 
299  default:
300  fr_pool_connection_release(pool, request, conn);
301  break;
302  }
303 
304  MEM(vb = fr_value_box_alloc_null(ctx));
305  fr_value_box_strdup(vb, vb, NULL, fr_table_str_by_value(fr_redis_cluster_rcodes_table, rcode, "<INVALID>"), false);
306  fr_dcursor_append(out, vb);
307 
308  return XLAT_ACTION_DONE;
309 }
310 
312  { .required = true, .single = true, .type = FR_TYPE_STRING },
313  { .single = true, .type = FR_TYPE_UINT32 },
315 };
316 
317 /** Return the node that is currently servicing a particular key
318  *
319 @verbatim
320 %redis.node(<key>[, <index>])
321 @endverbatim
322  *
323  * @ingroup xlat_functions
324  */
326  xlat_ctx_t const *xctx,
327  request_t *request, fr_value_box_list_t *in)
328 {
330 
331  fr_redis_cluster_key_slot_t const *key_slot;
332  fr_redis_cluster_node_t const *node;
333  fr_ipaddr_t ipaddr;
334  uint16_t port;
335 
336  unsigned long idx = 0;
337  fr_value_box_t *vb;
338  fr_value_box_t *key = fr_value_box_list_head(in);
339  fr_value_box_t *idx_vb = fr_value_box_list_next(in, key);
340 
341  if (idx_vb) idx = idx_vb->vb_uint32;
342 
343  key_slot = fr_redis_cluster_slot_by_key(inst->cluster, request, (uint8_t const *)key->vb_strvalue,
344  key->vb_length);
345  if (idx == 0) {
346  node = fr_redis_cluster_master(inst->cluster, key_slot);
347  } else {
348  node = fr_redis_cluster_slave(inst->cluster, key_slot, idx - 1);
349  }
350 
351  if (!node) {
352  RDEBUG2("No node available for this key slot");
353  return XLAT_ACTION_DONE;
354  }
355 
356  if ((fr_redis_cluster_ipaddr(&ipaddr, node) < 0) || (fr_redis_cluster_port(&port, node) < 0)) {
357  REDEBUG("Failed retrieving node information");
358  return XLAT_ACTION_FAIL;
359  }
360 
361  MEM(vb = fr_value_box_alloc_null(ctx));
362  fr_value_box_asprintf(vb, vb, NULL, false, "%pV:%u", fr_box_ipaddr(ipaddr), port);
363  fr_dcursor_append(out, vb);
364 
365  return XLAT_ACTION_DONE;
366 }
367 
369  { .required = true, .single = true, .type = FR_TYPE_UINT64 }, /* key count */
370  { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .concat = true, .type = FR_TYPE_STRING }, /* keys and args */
372 };
373 
374 /** Call a lua function on the redis server
375  *
376  * Lua functions either get uploaded when the module is instantiated or the first
377  * time they get executed.
378  */
380  xlat_ctx_t const *xctx,
381  request_t *request, fr_value_box_list_t *in)
382 {
383  rlm_redis_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_redis_t);
385  redis_lua_func_t *func = xlat_inst->func;
386 
387  fr_redis_conn_t *conn;
389  fr_redis_rcode_t status;
390 
391  redisReply *reply = NULL;
392  int s_ret;
393 
394  char const *argv[MAX_REDIS_ARGS];
395  size_t arg_len[MAX_REDIS_ARGS];
396  int argc;
397  char key_count[sizeof("184467440737095551615")];
398  uint8_t const *key = NULL;
399  size_t key_len = 0;
400 
402  fr_value_box_t *vb_out;
403 
404  /*
405  * First argument is always the key count
406  */
407  if (unlikely(fr_value_box_print(&FR_SBUFF_OUT(key_count, sizeof(key_count)), fr_value_box_list_head(in), NULL) < 0)) {
408  RPERROR("Failed converting key count to string");
409  return XLAT_ACTION_FAIL;
410  }
411  fr_value_box_list_talloc_free_head(in);
412 
413  /*
414  * Try EVALSHA first, and if that fails fall back to SCRIPT LOAD
415  */
416  argv[0] = "EVALSHA";
417  arg_len[0] = sizeof("EVALSHA") - 1;
418  argv[1] = func->digest;
419  arg_len[1] = sizeof(func->digest) - 1;
420  argv[2] = key_count;
421  arg_len[2] = strlen(key_count);
422  argc = 3;
423 
425  if (argc == NUM_ELEMENTS(argv)) {
426  REDEBUG("Too many arguments (%i)", argc);
427  REXDENT();
428  return XLAT_ACTION_FAIL;
429  }
430 
431  /*
432  * Fixup null or empty arguments to be
433  * zero length strings so that the position
434  * of subsequent arguments are maintained.
435  */
436  if (!fr_type_is_string(vb->type)) {
437  argv[argc] = "";
438  arg_len[argc++] = 0;
439  continue;
440  }
441 
442  argv[argc] = vb->vb_strvalue;
443  arg_len[argc++] = vb->vb_length;
444  }
445 
446  /*
447  * For eval commands all keys should hash to the same redis instance
448  * so we just use the first key (the arg after the key count).
449  */
450  if (argc > 3) {
451  key = (uint8_t const *)argv[3];
452  key_len = arg_len[3];
453  }
454 
455  for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request, key, key_len, func->read_only);
456  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
457  s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &reply)) {
458  bool script_load_done = false;
459 
460  again:
461  RDEBUG3("Calling script 0x%s", func->digest);
462  if (argc > 2) {
463  RDEBUG3("With arguments");
464  RINDENT();
465  for (int i = 2; i < argc; i++) RDEBUG3("[%i] %s", i, argv[i]);
466  REXDENT();
467  }
468  if (redis_command(&status, &reply, request, conn,
469  func->read_only, argc, argv, arg_len) == -2) {
470  state.close_conn = true;
471  }
472 
473  if (status != REDIS_RCODE_NO_SCRIPT) continue;
474 
475  /*
476  * Discard the error we received, and attempt load the function.
477  */
478  fr_redis_reply_free(&reply);
479 
480  RDEBUG3("Loading lua function \"%s\" (0x%s)", func->name, func->digest);
481  {
482  char const *script_load_argv[] = {
483  "SCRIPT",
484  "LOAD",
485  func->body
486  };
487 
488  size_t script_load_arg_len[] = {
489  (sizeof("SCRIPT") - 1),
490  (sizeof("LOAD") - 1),
491  (talloc_array_length(func->body) - 1)
492  };
493 
494  /*
495  * Loading the script failed... fail the call.
496  */
497  if (script_load_done) {
498  script_load_failed:
499  status = REDIS_RCODE_ERROR;
500  fr_redis_reply_free(&reply);
501  continue;
502  }
503 
504  /*
505  * Fixme: Really the script load and the eval call should be
506  * handled in a single MULTI/EXEC block, but the complexity
507  * in handling this properly is great, and most of this
508  * synchronous code will need to be rewritten, so for now
509  * we just load the script and try again.
510  */
511  if (redis_command(&status, &reply, request, conn, func->read_only,
512  NUM_ELEMENTS(script_load_argv),
513  script_load_argv, script_load_arg_len) == -2) {
514  state.close_conn = true;
515  }
516 
517  if (status == REDIS_RCODE_SUCCESS) {
518  script_load_done = true;
519 
520  /*
521  * Verify we got a sane response
522  */
523  if (reply->type != REDIS_REPLY_STRING) {
524  REDEBUG("Unexpected reply type after loading function");
525  fr_redis_reply_print(L_DBG_LVL_OFF, reply, request, 0);
526  goto script_load_failed;
527  }
528 
529  if (strcmp(reply->str, func->digest) != 0) {
530  REDEBUG("Function digest %s, does not match calculated digest %s", reply->str, func->digest);
531  goto script_load_failed;
532  }
533  fr_redis_reply_free(&reply);
534  goto again;
535  }
536  }
537  }
538 
539  if (s_ret != REDIS_RCODE_SUCCESS) {
540  action = XLAT_ACTION_FAIL;
541  goto finish;
542  }
543 
544  if (!fr_cond_assert(reply)) {
545  action = XLAT_ACTION_FAIL;
546  goto finish;
547  }
548 
549  MEM(vb_out = fr_value_box_alloc_null(ctx));
550  if (fr_redis_reply_to_value_box(ctx, vb_out, reply, FR_TYPE_VOID, NULL, false, false) < 0) {
551  RPERROR("Failed processing reply");
552  action = XLAT_ACTION_FAIL;
553  goto finish;
554  }
555  fr_dcursor_append(out, vb_out);
556 
557 finish:
558  fr_redis_reply_free(&reply);
559 
560  return action;
561 }
562 
563 /** Copies the function configuration into xlat function instance data
564  *
565  */
567 {
568  redis_lua_func_inst_t *inst = talloc_get_type_abort(xctx->inst, redis_lua_func_inst_t);
569 
570  inst->func = talloc_get_type_abort(xctx->uctx, redis_lua_func_t);
571 
572  return 0;
573 }
574 
575 static xlat_arg_parser_t const redis_args[] = {
576  { .required = true, .concat = true, .type = FR_TYPE_STRING },
577  { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .concat = true, .type = FR_TYPE_STRING },
579 };
580 
581 /** Xlat to make calls to redis
582  *
583 @verbatim
584 %redis(<redis command>)
585 @endverbatim
586  *
587  * @ingroup xlat_functions
588  */
589 static xlat_action_t redis_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
590  xlat_ctx_t const *xctx,
591  request_t *request, fr_value_box_list_t *in)
592 {
595  fr_redis_conn_t *conn;
596 
597  bool read_only = false;
598  uint8_t const *key = NULL;
599  size_t key_len = 0;
600 
602  fr_redis_rcode_t status;
603 
604  redisReply *reply = NULL;
605  int s_ret;
606 
607  fr_value_box_t *first = fr_value_box_list_head(in);
608  fr_sbuff_t sbuff = FR_SBUFF_IN(first->vb_strvalue, first->vb_length);
609 
610  int argc = 0;
611  char const *argv[MAX_REDIS_ARGS];
612  size_t arg_len[MAX_REDIS_ARGS];
613 
614  fr_value_box_t *vb_out;
615 
616  if (fr_sbuff_next_if_char(&sbuff, '-')) read_only = true;
617 
618  /*
619  * Hack to allow querying against a specific node for testing
620  */
621  if (fr_sbuff_next_if_char(&sbuff, '@')) {
622  fr_socket_t node_addr;
623  fr_pool_t *pool;
624 
625  RDEBUG3("Overriding node selection");
626 
627  if (fr_inet_pton_port(&node_addr.inet.dst_ipaddr, &node_addr.inet.dst_port,
628  fr_sbuff_current(&sbuff), fr_sbuff_remaining(&sbuff),
629  AF_UNSPEC, true, true) < 0) {
630  RPEDEBUG("Failed parsing node address");
631  return XLAT_ACTION_FAIL;
632  }
633 
634  if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &node_addr, true) < 0) {
635  RPEDEBUG("Failed locating cluster node");
636  return XLAT_ACTION_FAIL;
637  }
638 
639  conn = fr_pool_connection_get(pool, request);
640  if (!conn) {
641  REDEBUG("No connections available for cluster node");
642  return XLAT_ACTION_FAIL;
643  }
644 
645  fr_value_box_list_talloc_free_head(in); /* Remove and free server arg */
646 
648  if (argc == NUM_ELEMENTS(argv)) {
649  REDEBUG("Too many arguments (%i)", argc);
650  REXDENT();
651  goto fail;
652  }
653 
654  /*
655  * Fixup null or empty arguments to be
656  * zero length strings so that the position
657  * of subsequent arguments are maintained.
658  */
659  if (!fr_type_is_string(vb->type)) {
660  argv[argc] = "";
661  arg_len[argc++] = 0;
662  continue;
663  }
664 
665  argv[argc] = vb->vb_strvalue;
666  arg_len[argc++] = vb->vb_length;
667  }
668 
669  RDEBUG2("Executing command: %pV", fr_value_box_list_head(in));
670  if (argc > 1) {
671  RDEBUG2("With arguments");
672  RINDENT();
673  for (int i = 1; i < argc; i++) RDEBUG2("[%i] %s", i, argv[i]);
674  REXDENT();
675  }
676 
677  if (redis_command(&status, &reply, request, conn, read_only, argc, argv, arg_len) == -2) {
678  goto close_conn;
679  }
680 
681  if (!reply) goto fail;
682 
683  switch (status) {
684  case REDIS_RCODE_MOVE:
685  {
686  fr_value_box_t vb;
687 
688  if (fr_redis_reply_to_value_box(NULL, &vb, reply, FR_TYPE_STRING, NULL, false, true) == 0) {
689  REDEBUG("Key served by a different node: %pV", &vb);
690  }
691  goto fail;
692  }
693 
694  case REDIS_RCODE_SUCCESS:
695  goto reply_parse;
696 
698  close_conn:
699  fr_pool_connection_close(pool, request, conn);
700  action = XLAT_ACTION_FAIL;
701  goto finish;
702 
703  default:
704  fail:
705  fr_pool_connection_release(pool, request, conn);
706  action = XLAT_ACTION_FAIL;
707  goto finish;
708  }
709  }
710 
711  RDEBUG2("REDIS command arguments");
712  RINDENT();
714  if (argc == NUM_ELEMENTS(argv)) {
715  REDEBUG("Too many arguments (%i)", argc);
716  REXDENT();
717  goto finish;
718  }
719 
720  argv[argc] = vb->vb_strvalue;
721  arg_len[argc] = vb->vb_length;
722  argc++;
723  }
724  REXDENT();
725 
726  /*
727  * If we've got multiple arguments, the second one is usually the key.
728  * The Redis docs say commands should be analysed first to get key
729  * positions, but this involves sending them to the server, which is
730  * just as expensive as sending them to the wrong server and receiving
731  * a redirect.
732  */
733  if (argc > 1) {
734  key = (uint8_t const *)argv[1];
735  key_len = arg_len[1];
736  }
737 
738  for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request, key, key_len, read_only);
739  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
740  s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &reply)) {
741  RDEBUG2("Executing command: %pV", fr_value_box_list_head(in));
742  if (argc > 1) {
743  RDEBUG2("With arguments");
744  RINDENT();
745  for (int i = 1; i < argc; i++) RDEBUG2("[%i] %s", i, argv[i]);
746  REXDENT();
747  }
748 
749  if (redis_command(&status, &reply, request, conn, read_only, argc, argv, arg_len) == -2) {
750  state.close_conn = true;
751  }
752  }
753  if (s_ret != REDIS_RCODE_SUCCESS) {
754  action = XLAT_ACTION_FAIL;
755  goto finish;
756  }
757 
758  if (!fr_cond_assert(reply)) {
759  action = XLAT_ACTION_FAIL;
760  goto finish;
761  }
762 
763 reply_parse:
764  MEM(vb_out = fr_value_box_alloc_null(ctx));
765  if (fr_redis_reply_to_value_box(ctx, vb_out, reply, FR_TYPE_VOID, NULL, false, false) < 0) {
766  RPERROR("Failed processing reply");
767  action = XLAT_ACTION_FAIL;
768  goto finish;
769  }
770  fr_dcursor_append(out, vb_out);
771 
772 finish:
773  fr_redis_reply_free(&reply);
774 
775  return action;
776 }
777 
778 static int mod_instantiate(module_inst_ctx_t const *mctx)
779 {
780  rlm_redis_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
781  fr_socket_t *nodes;
782  int ret, i;
783 
784  inst->cluster = fr_redis_cluster_alloc(inst, mctx->mi->conf, &inst->conf, true, NULL, NULL, NULL);
785  if (!inst->cluster) return -1;
786 
787  /*
788  * Best effort - Try and load in scripts on startup
789  */
790  if (talloc_array_length(inst->lua.funcs) == 0) return 0;
791 
792  ret = fr_redis_cluster_node_addr_by_role(NULL, &nodes, inst->cluster, true, true);
793  if (ret <= 0) return 0;
794 
795  for (i = 0; i < ret; i++) {
796  fr_pool_t *pool;
797  fr_redis_conn_t *conn;
798 
799  if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &nodes[i], true) < 0) {
800  talloc_free(nodes);
801  return 0;
802  }
803 
804  conn = fr_pool_connection_get(pool, 0);
805  if (!conn) continue;
806 
807  talloc_foreach(inst->lua.funcs, func) {
808  char const *script_load_argv[] = {
809  "SCRIPT",
810  "LOAD",
811  func->body
812  };
813 
814  size_t script_load_arg_len[] = {
815  (sizeof("SCRIPT") - 1),
816  (sizeof("LOAD") - 1),
817  (talloc_array_length(func->body) - 1)
818  };
819 
820  fr_redis_rcode_t status;
821  redisReply *reply;
822 
823  /*
824  * preload onto every node, even replicas.
825  */
826  if (redis_command(&status, &reply, NULL, conn, false,
827  NUM_ELEMENTS(script_load_argv), script_load_argv, script_load_arg_len) == -2) {
828  error:
829  fr_pool_connection_release(pool, NULL, conn);
830  talloc_free(nodes);
831  return -1;
832  }
833 
834  fr_redis_reply_free(&reply);
835 
836  /*
837  * Only error on explicit errors, not on connectivity issues
838  */
839  switch (status) {
840  case REDIS_RCODE_ERROR:
841  PERROR("Loading lua function \"%s\" onto node failed", func->name);
842  goto error;
843 
844  case REDIS_RCODE_SUCCESS:
845  DEBUG2("Loaded lua function \"%s\" onto node", func->name);
846  break;
847 
848  default:
849  PWARN("Loading lua function \"%s\" onto node failed", func->name);
850  continue;
851  }
852  }
853 
854  fr_pool_connection_release(pool, NULL, conn);
855  }
856  talloc_free(nodes);
857 
858  return 0;
859 }
860 
861 static int mod_bootstrap(module_inst_ctx_t const *mctx)
862 {
863  rlm_redis_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_redis_t);
864  xlat_t *xlat;
865 
866  xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, redis_xlat, FR_TYPE_VOID);
868 
869  /*
870  * %redis.node(<key>[, idx])
871  */
872  if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "node", redis_node_xlat, FR_TYPE_STRING)) == NULL)) return -1;
874 
875  if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "remap", redis_remap_xlat, FR_TYPE_STRING)) == NULL)) return -1;
877 
878  /*
879  * Loop over the lua functions, registering an xlat
880  * that'll call that function specifically.
881  */
882  talloc_foreach(inst->lua.funcs, func) {
883  if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, func->name, redis_lua_func_xlat, FR_TYPE_VOID)) == NULL)) return -1;
886  }
887 
888  return 0;
889 }
890 
891 static int mod_load(void)
892 {
894 
895  return 0;
896 }
897 
898 extern module_rlm_t rlm_redis;
900  .common = {
901  .magic = MODULE_MAGIC_INIT,
902  .name = "redis",
903  .inst_size = sizeof(rlm_redis_t),
905  .onload = mod_load,
906  .bootstrap = mod_bootstrap,
908  }
909 };
#define fr_base16_encode(_out, _in)
Definition: base16.h:57
#define RCSID(id)
Definition: build.h:481
#define unlikely(_x)
Definition: build.h:379
#define NUM_ELEMENTS(_t)
Definition: build.h:335
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:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#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:268
#define FR_CONF_SUBSECTION_ALLOC(_name, _type, _flags, _struct, _field, _subcs)
A conf_parser_t multi-subsection.
Definition: cf_parse.h:345
#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:297
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition: cf_parse.h:419
@ CONF_FLAG_OK_MISSING
OK if it's missing.
Definition: cf_parse.h:427
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:399
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
Common header for all CONF_* types.
Definition: cf_priv.h:49
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1185
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:684
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:289
#define cf_parent(_cf)
Definition: cf_util.h:101
#define cf_log_debug(_cf, _fmt,...)
Definition: cf_util.h:292
#define CF_IDENT_ANY
Definition: cf_util.h:78
fr_redis_rcode_t fr_redis_cluster_state_next(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
Definition: cluster.c:1863
fr_redis_cluster_key_slot_t const * fr_redis_cluster_slot_by_key(fr_redis_cluster_t *cluster, request_t *request, uint8_t const *key, size_t key_len)
Implements the key slot selection scheme used by freeradius.
Definition: cluster.c:1603
int fr_redis_cluster_pool_by_node_addr(fr_pool_t **pool, fr_redis_cluster_t *cluster, fr_socket_t *node_addr, bool create)
Get the pool associated with a node in the cluster.
Definition: cluster.c:2070
ssize_t fr_redis_cluster_node_addr_by_role(TALLOC_CTX *ctx, fr_socket_t *out[], fr_redis_cluster_t *cluster, bool is_master, bool is_slave)
Return an array of IP addresses belonging to masters or slaves.
Definition: cluster.c:2138
int fr_redis_cluster_port(uint16_t *out, fr_redis_cluster_node_t const *node)
Return the port of a particular node.
Definition: cluster.c:1689
fr_redis_cluster_rcode_t fr_redis_cluster_remap(request_t *request, fr_redis_cluster_t *cluster, fr_redis_conn_t *conn)
Perform a runtime remap of the cluster.
Definition: cluster.c:1009
fr_redis_cluster_node_t const * fr_redis_cluster_slave(fr_redis_cluster_t *cluster, fr_redis_cluster_key_slot_t const *key_slot, uint8_t slave_num)
Return the slave node that would be used for a particular key.
Definition: cluster.c:1655
fr_redis_rcode_t fr_redis_cluster_state_init(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, uint8_t const *key, size_t key_len, bool read_only)
Resolve a key to a pool, and reserve a connection in that pool.
Definition: cluster.c:1741
fr_redis_cluster_node_t const * fr_redis_cluster_master(fr_redis_cluster_t *cluster, fr_redis_cluster_key_slot_t const *key_slot)
Return the master node that would be used for a particular key.
Definition: cluster.c:1639
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf, bool triggers_enabled, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Allocate and initialise a new cluster structure.
Definition: cluster.c:2261
fr_table_num_sorted_t const fr_redis_cluster_rcodes_table[]
Definition: cluster.c:281
int fr_redis_cluster_ipaddr(fr_ipaddr_t *out, fr_redis_cluster_node_t const *node)
Return the ipaddr of a particular node.
Definition: cluster.c:1672
A redis cluster.
Definition: cluster.c:251
Indexes in the fr_redis_cluster_node_t array for a single key slot.
Definition: cluster.c:241
A Redis cluster node.
Definition: cluster.c:213
bool close_conn
Set by caller of fr_redis_cluster_state_next, to indicate that connection must be closed,...
Definition: cluster.h:50
fr_redis_cluster_rcode_t
Return values for internal functions.
Definition: cluster.h:67
@ FR_REDIS_CLUSTER_RCODE_NO_CONNECTION
Operation failed because we couldn't find a live connection.
Definition: cluster.h:71
Redis connection sequence state.
Definition: cluster.h:49
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:514
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 fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:139
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_slen_t in
Definition: dict.h:821
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
static xlat_action_t redis_xlat(TALLOC_CTX *ctx, 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:589
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:325
static xlat_action_t redis_remap_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Force a redis cluster remap.
Definition: rlm_redis.c:263
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:937
IPv4/6 prefix.
Definition: merged_model.c:272
#define PERROR(_fmt,...)
Definition: log.h:228
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define PWARN(_fmt,...)
Definition: log.h:227
#define RPERROR(fmt,...)
Definition: log.h:302
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition: log.h:259
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
talloc_free(reap)
@ L_DBG_LVL_OFF
No debug messages.
Definition: log.h:69
unsigned short uint16_t
Definition: merged_model.c:31
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_UINT64
64 Bit unsigned integer.
Definition: merged_model.c:100
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
unsigned char uint8_t
Definition: merged_model.c:30
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
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:257
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition: pool.c:1407
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition: pool.c:1392
int fr_pool_connection_close(fr_pool_t *pool, request_t *request, void *conn)
Delete a connection from the connection pool.
Definition: pool.c:1537
A connection pool.
Definition: pool.c:87
static const conf_parser_t config[]
Definition: base.c:183
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
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:206
redisContext * handle
Hiredis context used when issuing commands.
Definition: base.h:101
#define REDIS_COMMON_CONFIG
Definition: base.h:133
#define MAX_REDIS_ARGS
Definition: base.h:44
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
Print the response data in a useful treelike form.
Definition: redis.c:141
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition: base.h:64
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition: redis.c:53
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition: redis.c:71
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: base.h:87
@ REDIS_RCODE_RECONNECT
Transitory error, caller should retry the operation with a new connection.
Definition: base.h:91
@ REDIS_RCODE_SUCCESS
Operation was successful.
Definition: base.h:88
@ REDIS_RCODE_MOVE
Attempt operation on an alternative node with remap.
Definition: base.h:94
@ REDIS_RCODE_TRY_AGAIN
Try the operation again.
Definition: base.h:90
@ REDIS_RCODE_NO_SCRIPT
Script doesn't exist.
Definition: base.h:95
@ REDIS_RCODE_ERROR
Unrecoverable library/server error.
Definition: base.h:89
Configuration parameters for a redis connection.
Definition: base.h:109
Connection handle, holding a redis context.
Definition: base.h:100
static int mod_load(void)
Definition: rlm_redis.c:891
rlm_redis_lua_t lua
Array of functions to register.
Definition: rlm_redis.c:83
static xlat_arg_parser_t const redis_remap_xlat_args[]
Definition: rlm_redis.c:250
static conf_parser_t module_lua[]
Definition: rlm_redis.c:96
module_rlm_t rlm_redis
Definition: rlm_redis.c:899
fr_redis_conf_t conf
Connection parameters for the Redis server.
Definition: rlm_redis.c:80
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:566
redis_lua_func_t ** funcs
Array of functions to register.
Definition: rlm_redis.c:72
char digest[(SHA1_DIGEST_LENGTH *2)+1]
pre-computed hash of lua code.
Definition: rlm_redis.c:58
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_redis.c:861
static int redis_command(fr_redis_rcode_t *status_out, redisReply **reply_out, request_t *request, fr_redis_conn_t *conn, bool read_only, int argc, char const **argv, size_t arg_len[])
Issue a command against redis and get a response.
Definition: rlm_redis.c:165
redis_lua_func_t * func
Function configuration.
Definition: rlm_redis.c:67
static conf_parser_t module_lua_func[]
Definition: rlm_redis.c:90
char const * body
the actual lua code.
Definition: rlm_redis.c:59
char const * name
Friendly name for the function. Used to register the equivalent xlat.
Definition: rlm_redis.c:57
static xlat_arg_parser_t const redis_node_xlat_args[]
Definition: rlm_redis.c:311
fr_redis_cluster_t * cluster
Redis cluster.
Definition: rlm_redis.c:85
static xlat_arg_parser_t const redis_args[]
Definition: rlm_redis.c:575
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:112
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_redis.c:778
static xlat_action_t redis_lua_func_xlat(TALLOC_CTX *ctx, 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:379
static conf_parser_t module_config[]
Definition: rlm_redis.c:103
static xlat_arg_parser_t const redis_lua_func_args[]
Definition: rlm_redis.c:368
bool read_only
Function has no side effects.
Definition: rlm_redis.c:60
Instance of a redis lua func xlat.
Definition: rlm_redis.c:66
A lua function or stored procedure we make available as an xlat.
Definition: rlm_redis.c:56
rlm_redis module instance
Definition: rlm_redis.c:79
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
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:2066
#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)
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
void * boot
Data allocated during the boostrap phase.
Definition: module.h:274
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
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
#define talloc_get_type_abort_const
Definition: talloc.h:282
#define talloc_foreach(_array, _iter)
Iterate over a talloced array of elements.
Definition: talloc.h:75
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
Definition: xlat.h:137
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
static fr_slen_t parent
Definition: pair.h:851
Holds information necessary for binding or connecting to a socket.
Definition: socket.h:63
#define fr_type_is_string(_x)
Definition: types.h:327
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
Definition: value.c:5352
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:4014
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:3927
#define fr_box_ipaddr(_val)
Definition: value.h:294
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:286
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:632
#define fr_value_box_list_foreach(_list_head, _iter)
Definition: value.h:206
static size_t char ** out
Definition: value.h:997
void const * inst
xlat instance data.
Definition: xlat_ctx.h:50
void * uctx
Passed to the registration function.
Definition: xlat_ctx.h:65
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:62
An xlat calling ctx.
Definition: xlat_ctx.h:49
An xlat instantiation ctx.
Definition: xlat_ctx.h:61
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:365
#define xlat_func_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx)
Set a callback for global instantiation of xlat functions.
Definition: xlat_func.h:93