The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_cache.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: bddb466012355efd65b87ad783c546339d8d0943 $
19  * @file rlm_cache.c
20  * @brief Cache values and merge them back into future requests.
21  *
22  * @copyright 2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  * @copyright 2012-2014 The FreeRADIUS server project
24  */
25 RCSID("$Id: bddb466012355efd65b87ad783c546339d8d0943 $")
26 
27 #define LOG_PREFIX mctx->mi->name
28 
29 #include <freeradius-devel/server/base.h>
30 #include <freeradius-devel/server/module_rlm.h>
31 #include <freeradius-devel/server/modpriv.h>
32 #include <freeradius-devel/server/dl_module.h>
33 #include <freeradius-devel/server/rcode.h>
34 #include <freeradius-devel/server/tmpl.h>
35 #include <freeradius-devel/util/debug.h>
36 #include <freeradius-devel/util/types.h>
37 #include <freeradius-devel/util/value.h>
38 #include <freeradius-devel/unlang/xlat_func.h>
39 #include <freeradius-devel/unlang/call_env.h>
40 
41 #include "rlm_cache.h"
42 
43 extern module_rlm_t rlm_cache;
44 
45 int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
46 static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
47 static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
48 
49 static const conf_parser_t module_config[] = {
50  { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_cache_t, driver_submodule), .dflt = "rbtree",
51  .func = submodule_parse },
52  { FR_CONF_OFFSET("ttl", rlm_cache_config_t, ttl), .dflt = "500s" },
53  { FR_CONF_OFFSET("max_entries", rlm_cache_config_t, max_entries), .dflt = "0" },
54 
55  /* Should be a type which matches time_t, @fixme before 2038 */
56  { FR_CONF_OFFSET("epoch", rlm_cache_config_t, epoch), .dflt = "0" },
57  { FR_CONF_OFFSET("add_stats", rlm_cache_config_t, stats), .dflt = "no" },
59 };
60 
61 typedef struct {
62  fr_value_box_t *key; //!< To lookup the cache entry with.
63  map_list_t *maps; //!< Attribute map applied to cache entries.
65 
66 typedef struct {
67  fr_type_t ktype; //!< Key type
68 
70 
73  .env = (call_env_parser_t[]) {
77  }
78 };
79 
80 static fr_dict_t const *dict_freeradius;
81 
84  { .out = &dict_freeradius, .proto = "freeradius" },
85  { NULL }
86 };
87 
94 
97  { .out = &attr_cache_merge_new, .name = "Cache-Merge-New", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
98  { .out = &attr_cache_status_only, .name = "Cache-Status-Only", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
99  { .out = &attr_cache_allow_merge, .name = "Cache-Allow-Merge", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
100  { .out = &attr_cache_allow_insert, .name = "Cache-Allow-Insert", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
101  { .out = &attr_cache_ttl, .name = "Cache-TTL", .type = FR_TYPE_INT32, .dict = &dict_freeradius },
102  { .out = &attr_cache_entry_hits, .name = "Cache-Entry-Hits", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
103  { NULL }
104 };
105 
106 int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
107 {
108  rlm_cache_t *inst = talloc_get_type_abort(parent, rlm_cache_t);
109  module_instance_t *mi;
110  int ret;
111 
112  if (unlikely(ret = module_rlm_submodule_parse(ctx, out, parent, ci, rule) < 0)) return ret;
113 
114  mi = talloc_get_type_abort(*((void **)out), module_instance_t);
115  inst->driver = (rlm_cache_driver_t const *)mi->exported; /* Public symbol exported by the submodule */
116 
117  return 0;
118 }
119 
120 static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
121  call_env_ctx_t const *cec,
122  call_env_parser_t const *rule)
123 {
125  call_env_parse_pair_t func = inst->driver->key_parse ? inst->driver->key_parse : call_env_parse_pair;
126  tmpl_t *key_tmpl;
127  fr_type_t cast;
128  int ret;
129  /*
130  * Call the custom key parse function, OR the standard call_env_parse_pair
131  * function, depending on whether the driver calls a custom parsing function.
132  */
133  if (unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, inst->driver_submodule->data, rule)) < 0)) return ret;
134  *((tmpl_t **)out) = key_tmpl;
135 
136  /*
137  * Unless the driver has a custom key parse function, we only allow keys of
138  * type string.
139  */
140  if (inst->driver->key_parse) return 0;
141 
142  cast = tmpl_cast_get(key_tmpl);
143  switch (cast) {
144  case FR_TYPE_STRING:
145  case FR_TYPE_NULL:
146  case FR_TYPE_VOID:
147  break;
148 
149  default:
150  cf_log_err(ci, "Driver only allows key type '%s', got '%s'",
152  return -1;
153  }
154 
155  if (tmpl_cast_set(key_tmpl, FR_TYPE_STRING) < 0) {
156  cf_log_perr(ci, "Can't convert key type '%s' to '%s'",
158  return -1;
159  }
160 
161  return 0;
162 }
163 
164 /** Get exclusive use of a handle to access the cache
165  *
166  */
168 {
169  if (!inst->driver->acquire) {
170  *out = NULL;
171  return 0;
172  }
173 
174  return inst->driver->acquire(out, &inst->config, inst->driver_submodule->data, request);
175 }
176 
177 /** Release a handle we previously acquired
178  *
179  */
180 static void cache_release(rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle)
181 {
182  if (!inst->driver->release) return;
183  if (!handle || !*handle) return;
184 
185  inst->driver->release(&inst->config, inst->driver_submodule->data, request, *handle);
186  *handle = NULL;
187 }
188 
189 /** Reconnect an suspected inviable handle
190  *
191  */
192 static int cache_reconnect(rlm_cache_handle_t **handle, rlm_cache_t const *inst, request_t *request)
193 {
194  fr_assert(inst->driver->reconnect);
195 
196  return inst->driver->reconnect(handle, &inst->config, inst->driver_submodule->data, request);
197 }
198 
199 /** Allocate a cache entry
200  *
201  * This is used so that drivers may use their own allocation functions
202  * to allocate structures larger than the normal rlm_cache_entry_t.
203  *
204  * If the driver doesn't specify a custom allocation function, the cache
205  * entry is talloced in the NULL ctx.
206  */
208 {
209  if (inst->driver->alloc) return inst->driver->alloc(&inst->config, inst->driver_submodule->data, request);
210 
211  return talloc_zero(NULL, rlm_cache_entry_t);
212 }
213 
214 /** Free memory associated with a cache entry
215  *
216  * This does not necessarily remove the entry from the cache, cache_expire
217  * should be used for that.
218  *
219  * This function should be called when an entry that is known to have been
220  * retrieved or inserted into a data store successfully, is no longer needed.
221  *
222  * Some drivers (like rlm_cache_rbtree) don't register a free function.
223  * This means that the cache entry never needs to be explicitly freed.
224  *
225  * @param[in] inst Module instance.
226  * @param[in,out] c Cache entry to free.
227  */
229 {
230  if (!c || !*c || !inst->driver->free) return;
231 
232  inst->driver->free(*c);
233  *c = NULL;
234 }
235 
236 /** Merge a cached entry into a #request_t
237  *
238  * @return
239  * - #RLM_MODULE_OK if no entries were merged.
240  * - #RLM_MODULE_UPDATED if entries were merged.
241  */
242 static rlm_rcode_t cache_merge(rlm_cache_t const *inst, request_t *request, rlm_cache_entry_t *c) CC_HINT(nonnull);
244 {
245  fr_pair_t *vp;
246  map_t *map = NULL;
247  int merged = 0;
248 
249  RDEBUG2("Merging cache entry into request");
250  RINDENT();
251  while ((map = map_list_next(&c->maps, map))) {
252  /*
253  * The only reason that the application of a map entry
254  * can fail, is if the destination list or request
255  * isn't valid. For now we don't consider this fatal
256  * and continue merging the rest of the maps.
257  */
258  if (map_to_request(request, map, map_to_vp, NULL) < 0) {
259  char buffer[1024];
260 
261  map_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map);
262  REXDENT();
263  RDEBUG2("Skipping %s", buffer);
264  RINDENT();
265  continue;
266  }
267  merged++;
268  }
269  REXDENT();
270 
271  if (inst->config.stats) {
272  fr_assert(request->packet != NULL);
274  vp->vp_uint32 = c->hits;
275  }
276 
277  return merged > 0 ?
280 }
281 
282 /** Find a cached entry.
283  *
284  * @return
285  * - #RLM_MODULE_OK on cache hit.
286  * - #RLM_MODULE_FAIL on failure.
287  * - #RLM_MODULE_NOTFOUND on cache miss.
288  */
290  rlm_cache_t const *inst, request_t *request,
291  rlm_cache_handle_t **handle, fr_value_box_t const *key)
292 {
293  cache_status_t ret;
294 
296 
297  *out = NULL;
298 
299  for (;;) {
300  ret = inst->driver->find(&c, &inst->config, inst->driver_submodule->data, request, *handle, key);
301  switch (ret) {
302  case CACHE_RECONNECT:
303  RDEBUG2("Reconnecting...");
304  if (cache_reconnect(handle, inst, request) == 0) continue;
306 
307  case CACHE_OK:
308  break;
309 
310  case CACHE_MISS:
311  RDEBUG2("No cache entry found for \"%pV\"", key);
313 
314  default:
316 
317  }
318 
319  break;
320  }
321 
322  /*
323  * Yes, but it expired, OR the "forget all" epoch has
324  * passed. Delete it, and pretend it doesn't exist.
325  */
326  if (fr_unix_time_lt(c->expires, fr_time_to_unix_time(request->packet->timestamp))) {
327  RDEBUG2("Found entry for \"%pV\", but it expired %pV ago at %pV (packet received %pV). Removing it",
328  key,
329  fr_box_time_delta(fr_unix_time_sub(fr_time_to_unix_time(request->packet->timestamp), c->expires)),
330  fr_box_date(c->expires),
331  fr_box_time(request->packet->timestamp));
332 
333  expired:
334  inst->driver->expire(&inst->config, inst->driver_submodule->data, request, handle, key);
335  cache_free(inst, &c);
336  RETURN_MODULE_NOTFOUND; /* Couldn't find a non-expired entry */
337  }
338 
339  if (fr_unix_time_lt(c->created, fr_unix_time_from_sec(inst->config.epoch))) {
340  RDEBUG2("Found entry for \"%pV\", but it was created before the current epoch. Removing it",
341  key);
342  goto expired;
343  }
344  RDEBUG2("Found entry for \"%pV\"", key);
345 
346  c->hits++;
347  *out = c;
348 
350 }
351 
352 /** Expire a cache entry (removing it from the datastore)
353  *
354  * @return
355  * - #RLM_MODULE_OK on success.
356  * - #RLM_MODULE_NOTFOUND if no entry existed.
357  * - #RLM_MODULE_FAIL on failure.
358  */
360  rlm_cache_t const *inst, request_t *request,
361  rlm_cache_handle_t **handle, fr_value_box_t const *key)
362 {
363  RDEBUG2("Expiring cache entry");
364  for (;;) switch (inst->driver->expire(&inst->config, inst->driver_submodule->data, request, *handle, key)) {
365  case CACHE_RECONNECT:
366  if (cache_reconnect(handle, inst, request) == 0) continue;
367  FALL_THROUGH;
368 
369  default:
371 
372  case CACHE_OK:
374 
375  case CACHE_MISS:
377  }
378 }
379 
380 /** Create and insert a cache entry
381  *
382  * @return
383  * - #RLM_MODULE_OK on success.
384  * - #RLM_MODULE_UPDATED if we merged the cache entry.
385  * - #RLM_MODULE_FAIL on failure.
386  */
388  rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle,
389  fr_value_box_t const *key, map_list_t const *maps, fr_time_delta_t ttl)
390 {
391  map_t const *map = NULL;
392  map_t *c_map;
393 
394  fr_pair_t *vp;
395  bool merge = false;
397 
398  TALLOC_CTX *pool;
399 
400  if ((inst->config.max_entries > 0) && inst->driver->count &&
401  (inst->driver->count(&inst->config, inst->driver_submodule->data, request, handle) > inst->config.max_entries)) {
402  RWDEBUG("Cache is full: %d entries", inst->config.max_entries);
404  }
405 
406  c = cache_alloc(inst, request);
407  if (!c) {
409  }
410  map_list_init(&c->maps);
411  if (unlikely(fr_value_box_copy(c, &c->key, key) < 0)) {
412  RERROR("Failed copying key");
413  talloc_free(c);
415  }
416 
417  /*
418  * All in NSEC resolution
419  */
420  c->created = c->expires = fr_time_to_unix_time(request->packet->timestamp);
421  c->expires = fr_unix_time_add(c->expires, ttl);
422 
423  RDEBUG2("Creating new cache entry");
424 
425  /*
426  * We don't have any maps to apply to the cache entry
427  * so don't try to expand them.
428  */
429  if (!maps) goto skip_maps;
430 
431  /*
432  * Alloc a pool so we don't have excessive allocs when
433  * gathering fr_pair_ts to cache.
434  */
435  pool = talloc_pool(NULL, 2048);
436  while ((map = map_list_next(maps, map))) {
437  fr_pair_list_t to_cache;
438 
439  fr_pair_list_init(&to_cache);
440  fr_assert(map->lhs && map->rhs);
441 
442  /*
443  * Calling map_to_vp gives us exactly the same result,
444  * as if this were an update section.
445  */
446  if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) {
447  RDEBUG2("Skipping %s", map->rhs->name);
448  continue;
449  }
450 
451  for (vp = fr_pair_list_head(&to_cache);
452  vp;
453  vp = fr_pair_list_next(&to_cache, vp)) {
454  /*
455  * Prevent people from accidentally caching
456  * cache control attributes.
457  */
458  if (tmpl_is_list(map->rhs)) switch (vp->da->attr) {
459  case FR_CACHE_TTL:
460  case FR_CACHE_STATUS_ONLY:
461  case FR_CACHE_MERGE_NEW:
462  case FR_CACHE_ENTRY_HITS:
463  RDEBUG2("Skipping %s", vp->da->name);
464  continue;
465 
466  default:
467  break;
468  }
469  RINDENT();
470  if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
471  REXDENT();
472 
473  MEM(c_map = talloc_zero(c, map_t));
474  c_map->op = map->op;
475  map_list_init(&c_map->child);
476 
477  /*
478  * Now we turn the fr_pair_ts into maps.
479  */
480  switch (map->lhs->type) {
481  /*
482  * Attributes are easy, reuse the LHS, and create a new
483  * RHS with the fr_value_box_t from the fr_pair_t.
484  */
485  case TMPL_TYPE_ATTR:
486  {
487  fr_token_t quote;
488  /*
489  * If the LHS is structural, we need a new template
490  * which is the combination of the existing LHS and
491  * the attribute.
492  */
494  tmpl_attr_afrom_list(c_map, &c_map->lhs, map->lhs, vp->da);
495  } else {
496  c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */
497  }
498 
499  if (vp->vp_type == FR_TYPE_STRING) {
500  quote = is_printable(vp->vp_strvalue, vp->vp_length) ?
502  } else {
503  quote = T_BARE_WORD;
504  }
505 
506  MEM(c_map->rhs = tmpl_alloc(c_map,
507  TMPL_TYPE_DATA, quote, map->rhs->name, map->rhs->len));
508  if (fr_value_box_copy(c_map->rhs, tmpl_value(c_map->rhs), &vp->data) < 0) {
509  REDEBUG("Failed copying attribute value");
510  talloc_free(pool);
511  talloc_free(c);
513  }
514  }
515  break;
516 
517  default:
518  fr_assert(0);
519  }
520  MAP_VERIFY(c_map);
521  map_list_insert_tail(&c->maps, c_map);
522  }
523  talloc_free_children(pool); /* reset pool state */
524  }
525  talloc_free(pool);
526 
527 skip_maps:
528 
529  /*
530  * Check to see if we need to merge the entry into the request
531  */
532  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_merge_new);
533  if (vp && vp->vp_bool) merge = true;
534 
535  if (merge) cache_merge(inst, request, c);
536 
537  for (;;) {
538  cache_status_t ret;
539 
540  ret = inst->driver->insert(&inst->config, inst->driver_submodule->data, request, *handle, c);
541  switch (ret) {
542  case CACHE_RECONNECT:
543  if (cache_reconnect(handle, inst, request) == 0) continue;
545 
546  case CACHE_OK:
547  RDEBUG2("Committed entry, TTL %pV seconds", fr_box_time_delta(ttl));
548  cache_free(inst, &c);
550 
551  default:
552  talloc_free(c); /* Failed insertion - use talloc_free not the driver free */
554  }
555  }
556 }
557 
558 /** Update the TTL of an entry
559  *
560  * @return
561  * - #RLM_MODULE_OK on success.
562  * - #RLM_MODULE_FAIL on failure.
563  */
565  rlm_cache_t const *inst, request_t *request,
567 {
568  /*
569  * Call the driver's insert method to overwrite the old entry
570  */
571  if (!inst->driver->set_ttl) for (;;) {
572  cache_status_t ret;
573 
574  ret = inst->driver->insert(&inst->config, inst->driver_submodule->data, request, *handle, c);
575  switch (ret) {
576  case CACHE_RECONNECT:
577  if (cache_reconnect(handle, inst, request) == 0) continue;
579 
580  case CACHE_OK:
581  RDEBUG2("Updated entry TTL");
583 
584  default:
586  }
587  }
588 
589  /*
590  * Or call the set ttl method if the driver can do this more
591  * efficiently.
592  */
593  for (;;) {
594  cache_status_t ret;
595 
596  ret = inst->driver->set_ttl(&inst->config, inst->driver_submodule->data, request, *handle, c);
597  switch (ret) {
598  case CACHE_RECONNECT:
599  if (cache_reconnect(handle, inst, request) == 0) continue;
601 
602  case CACHE_OK:
603  RDEBUG2("Updated entry TTL");
605 
606  default:
608  }
609  }
610 }
611 
612 /** Do caching checks
613  *
614  * Since we can update ANY VP list, we do exactly the same thing for all sections
615  * (autz / auth / etc.)
616  *
617  * If you want to cache something different in different sections, configure
618  * another cache module.
619  */
620 static unlang_action_t CC_HINT(nonnull) mod_cache_it(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
621 {
622  rlm_cache_entry_t *c = NULL;
624  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
625 
626  rlm_cache_handle_t *handle;
627 
628  fr_dcursor_t cursor;
629  fr_pair_t *vp;
630 
631  bool merge = true, insert = true, expire = false, set_ttl = false;
632  int exists = -1;
633 
635 
636  fr_time_delta_t ttl = inst->config.ttl;
637 
638  if (env->key->vb_length == 0) {
639  REDEBUG("Zero length key string is invalid");
641  }
642 
643  /*
644  * If Cache-Status-Only == yes, only return whether we found a
645  * valid cache entry
646  */
647  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_status_only);
648  if (vp && vp->vp_bool) {
649  RINDENT();
650  RDEBUG3("status-only: yes");
651  REXDENT();
652 
653  if (cache_acquire(&handle, inst, request) < 0) {
655  }
656 
657  cache_find(&rcode, &c, inst, request, &handle, env->key);
658  if (rcode == RLM_MODULE_FAIL) goto finish;
659  fr_assert(!inst->driver->acquire || handle);
660 
661  rcode = c ? RLM_MODULE_OK:
663  goto finish;
664  }
665 
666  /*
667  * Figure out what operation we're doing
668  */
669  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_allow_merge);
670  if (vp) merge = vp->vp_bool;
671 
672  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_allow_insert);
673  if (vp) insert = vp->vp_bool;
674 
675  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
676  if (vp) {
677  if (vp->vp_int32 == 0) {
678  expire = true;
679  } else if (vp->vp_int32 < 0) {
680  expire = true;
681  ttl = fr_time_delta_from_sec(-(vp->vp_int32));
682  /* Updating the TTL */
683  } else {
684  set_ttl = true;
685  ttl = fr_time_delta_from_sec(vp->vp_int32);
686  }
687  }
688 
689  RINDENT();
690  RDEBUG3("merge : %s", merge ? "yes" : "no");
691  RDEBUG3("insert : %s", insert ? "yes" : "no");
692  RDEBUG3("expire : %s", expire ? "yes" : "no");
693  RDEBUG3("ttl : %pV", fr_box_time_delta(ttl));
694  REXDENT();
695  if (cache_acquire(&handle, inst, request) < 0) {
697  }
698 
699  /*
700  * Retrieve the cache entry and merge it with the current request
701  * recording whether the entry existed.
702  */
703  if (merge) {
704  cache_find(&rcode, &c, inst, request, &handle, env->key);
705  switch (rcode) {
706  case RLM_MODULE_FAIL:
707  goto finish;
708 
709  case RLM_MODULE_OK:
710  rcode = cache_merge(inst, request, c);
711  exists = 1;
712  break;
713 
714  case RLM_MODULE_NOTFOUND:
715  rcode = RLM_MODULE_NOTFOUND;
716  exists = 0;
717  break;
718 
719  default:
720  fr_assert(0);
721  }
722  fr_assert(!inst->driver->acquire || handle);
723  }
724 
725  /*
726  * Expire the entry if told to, and we either don't know whether
727  * it exists, or we know it does.
728  *
729  * We only expire if we're not inserting, as driver insert methods
730  * should perform upserts.
731  */
732  if (expire && ((exists == -1) || (exists == 1))) {
733  if (!insert) {
734  rlm_rcode_t tmp;
735 
736  fr_assert(!set_ttl);
737  cache_expire(&tmp, inst, request, &handle, env->key);
738  switch (tmp) {
739  case RLM_MODULE_FAIL:
740  rcode = RLM_MODULE_FAIL;
741  goto finish;
742 
743  case RLM_MODULE_OK:
744  if (rcode == RLM_MODULE_NOOP) rcode = RLM_MODULE_OK;
745  break;
746 
747  case RLM_MODULE_NOTFOUND:
748  if (rcode == RLM_MODULE_NOOP) rcode = RLM_MODULE_NOTFOUND;
749  break;
750 
751  default:
752  fr_assert(0);
753  break;
754  }
755  /* If it previously existed, it doesn't now */
756  }
757  /* Otherwise use insert to overwrite */
758  exists = 0;
759  }
760 
761  /*
762  * If we still don't know whether it exists or not
763  * and we need to do an insert or set_ttl operation
764  * determine that now.
765  */
766  if ((exists < 0) && (insert || set_ttl)) {
767  rlm_rcode_t tmp;
768 
769  cache_find(&tmp, &c, inst, request, &handle, env->key);
770  switch (tmp) {
771  case RLM_MODULE_FAIL:
772  rcode = RLM_MODULE_FAIL;
773  goto finish;
774 
775  case RLM_MODULE_OK:
776  exists = 1;
777  if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
778  break;
779 
780  case RLM_MODULE_NOTFOUND:
781  exists = 0;
782  break;
783 
784  default:
785  fr_assert(0);
786  }
787  fr_assert(!inst->driver->acquire || handle);
788  }
789 
790  /*
791  * We can only alter the TTL on an entry if it exists.
792  */
793  if (set_ttl && (exists == 1)) {
794  rlm_rcode_t tmp;
795 
796  fr_assert(c);
797 
798  c->expires = fr_unix_time_add(fr_time_to_unix_time(request->packet->timestamp), ttl);
799 
800  cache_set_ttl(&tmp, inst, request, &handle, c);
801  switch (tmp) {
802  case RLM_MODULE_FAIL:
803  rcode = RLM_MODULE_FAIL;
804  goto finish;
805 
806  case RLM_MODULE_NOTFOUND:
807  case RLM_MODULE_OK:
808  if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
809  goto finish;
810 
811  default:
812  fr_assert(0);
813  }
814  }
815 
816  /*
817  * Inserts are upserts, so we don't care about the
818  * entry state, just that we're not meant to be
819  * setting the TTL, which precludes performing an
820  * insert.
821  */
822  if (insert && (exists == 0)) {
823  rlm_rcode_t tmp;
824 
825  cache_insert(&tmp, inst, request, &handle, env->key, env->maps, ttl);
826  switch (tmp) {
827  case RLM_MODULE_FAIL:
828  rcode = RLM_MODULE_FAIL;
829  goto finish;
830 
831  case RLM_MODULE_OK:
832  if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
833  break;
834 
835  case RLM_MODULE_UPDATED:
836  rcode = RLM_MODULE_UPDATED;
837  break;
838 
839  default:
840  fr_assert(0);
841  }
842  fr_assert(!inst->driver->acquire || handle);
843  goto finish;
844  }
845 
846 
847 finish:
848  cache_free(inst, &c);
849  cache_release(inst, request, &handle);
850 
851  /*
852  * Clear control attributes
853  */
854  for (vp = fr_pair_dcursor_init(&cursor, &request->control_pairs);
855  vp;
856  vp = fr_dcursor_next(&cursor)) {
857  again:
858  if (!fr_dict_attr_is_top_level(vp->da)) continue;
859 
860  switch (vp->da->attr) {
861  case FR_CACHE_TTL:
862  case FR_CACHE_STATUS_ONLY:
863  case FR_CACHE_ALLOW_MERGE:
864  case FR_CACHE_ALLOW_INSERT:
865  case FR_CACHE_MERGE_NEW:
866  RDEBUG2("Removing &control.%s", vp->da->name);
867  vp = fr_dcursor_remove(&cursor);
868  talloc_free(vp);
869  vp = fr_dcursor_current(&cursor);
870  if (!vp) break;
871  goto again;
872  }
873  }
874 
875  RETURN_MODULE_RCODE(rcode);
876 }
877 
879  { .required = true, .single = true, .type = FR_TYPE_STRING },
881 };
882 
883 /** Allow single attribute values to be retrieved from the cache
884  *
885  * @ingroup xlat_functions
886  */
887 static CC_HINT(nonnull)
889  xlat_ctx_t const *xctx,
890  request_t *request, fr_value_box_list_t *in)
891 {
892  rlm_cache_entry_t *c = NULL;
893  rlm_cache_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_cache_t);
894  cache_call_env_t *env = talloc_get_type_abort(xctx->env_data, cache_call_env_t);
895  rlm_cache_handle_t *handle = NULL;
896 
897  ssize_t slen;
898 
899  fr_value_box_t *attr = fr_value_box_list_head(in);
900  fr_value_box_t *vb;
901 
902  tmpl_t *target = NULL;
903  map_t *map = NULL;
905 
906  slen = tmpl_afrom_attr_substr(ctx, NULL, &target,
907  &FR_SBUFF_IN(attr->vb_strvalue, attr->vb_length),
908  NULL,
909  &(tmpl_rules_t){
910  .attr = {
911  .dict_def = request->dict,
912  .list_def = request_attr_request,
913  .prefix = TMPL_ATTR_REF_PREFIX_AUTO
914  }
915  });
916  if (slen <= 0) {
917  RPEDEBUG("Invalid key");
918  return XLAT_ACTION_FAIL;
919  }
920 
921  if (cache_acquire(&handle, inst, request) < 0) {
922  talloc_free(target);
923  return XLAT_ACTION_FAIL;
924  }
925 
926  cache_find(&rcode, &c, inst, request, &handle, env->key);
927  switch (rcode) {
928  case RLM_MODULE_OK: /* found */
929  break;
930 
931  default:
932  talloc_free(target);
933  cache_release(inst, request, &handle);
934  return XLAT_ACTION_FAIL;
935  }
936 
937  while ((map = map_list_next(&c->maps, map))) {
938  if ((tmpl_attr_tail_da(map->lhs) != tmpl_attr_tail_da(target)) ||
939  (tmpl_list(map->lhs) != tmpl_list(target))) continue;
940 
941  MEM(vb = fr_value_box_alloc_null(ctx));
942  fr_value_box_copy(vb, vb, tmpl_value(map->rhs));
943  fr_dcursor_append(out, vb);
944  break;
945  }
946 
947  talloc_free(target);
948 
949  cache_free(inst, &c);
950  cache_release(inst, request, &handle);
951 
952  /*
953  * Check if we found a matching map
954  */
955  if (!map) return XLAT_ACTION_FAIL;
956 
957  return XLAT_ACTION_DONE;
958 }
959 
961  xlat_ctx_t const *xctx,
962  request_t *request, UNUSED fr_value_box_list_t *in)
963 {
964 
965  rlm_cache_entry_t *c = NULL;
966  rlm_cache_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_cache_t);
967  cache_call_env_t *env = talloc_get_type_abort(xctx->env_data, cache_call_env_t);
968  rlm_cache_handle_t *handle = NULL;
969 
971 
972  fr_value_box_t *vb;
973 
974  if (cache_acquire(&handle, inst, request) < 0) {
975  return XLAT_ACTION_FAIL;
976  }
977 
978  cache_find(&rcode, &c, inst, request, &handle, env->key);
979  switch (rcode) {
980  case RLM_MODULE_OK: /* found */
981  break;
982 
983  default:
984  cache_release(inst, request, &handle);
985  return XLAT_ACTION_DONE;
986  }
987 
988  MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
989  vb->vb_time_delta = fr_unix_time_sub(c->expires, fr_time_to_unix_time(request->packet->timestamp));
990  fr_dcursor_append(out, vb);
991 
992  cache_free(inst, &c);
993  cache_release(inst, request, &handle);
994 
995  return XLAT_ACTION_DONE;
996 }
997 
998 /** Release the allocated resources and cleanup the avps
999  */
1000 static void cache_unref(request_t *request, rlm_cache_t const *inst, rlm_cache_entry_t *entry,
1001  rlm_cache_handle_t *handle)
1002 {
1003  fr_dcursor_t cursor;
1004  fr_pair_t *vp;
1005 
1006  /*
1007  * Release the driver calls
1008  */
1009  cache_free(inst, &entry);
1010  cache_release(inst, request, &handle);
1011 
1012  /*
1013  * Clear control attributes
1014  */
1015  for (vp = fr_pair_dcursor_init(&cursor, &request->control_pairs);
1016  vp;
1017  vp = fr_dcursor_next(&cursor)) {
1018  again:
1019  if (!fr_dict_attr_is_top_level(vp->da)) continue;
1020 
1021  switch (vp->da->attr) {
1022  case FR_CACHE_TTL:
1023  case FR_CACHE_STATUS_ONLY:
1024  case FR_CACHE_ALLOW_MERGE:
1025  case FR_CACHE_ALLOW_INSERT:
1026  case FR_CACHE_MERGE_NEW:
1027  RDEBUG2("Removing &control:%s", vp->da->name);
1028  vp = fr_dcursor_remove(&cursor);
1029  TALLOC_FREE(vp);
1030  vp = fr_dcursor_current(&cursor);
1031  if (!vp) break;
1032  goto again;
1033  }
1034  }
1035 }
1036 
1037 /** Get the status by ${key} (without load)
1038  *
1039  * @return
1040  * - #RLM_MODULE_OK on success.
1041  * - #RLM_MODULE_NOTFOUND on cache miss.
1042  * - #RLM_MODULE_FAIL on failure.
1043  */
1044 static unlang_action_t CC_HINT(nonnull) mod_method_status(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1045 {
1046  rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1047  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1048  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1049  rlm_cache_entry_t *entry = NULL;
1050  rlm_cache_handle_t *handle = NULL;
1051 
1052  if (env->key->vb_length == 0) {
1053  REDEBUG("Zero length key string is invalid");
1055  }
1056 
1057  /* Good to go? */
1058  if (cache_acquire(&handle, inst, request) < 0) {
1060  }
1061 
1062  fr_assert(!inst->driver->acquire || handle);
1063 
1064  cache_find(&rcode, &entry, inst, request, &handle, env->key);
1065  if (rcode == RLM_MODULE_FAIL) goto finish;
1066 
1067  rcode = (entry) ? RLM_MODULE_OK : RLM_MODULE_NOTFOUND;
1068 
1069 finish:
1070  cache_unref(request, inst, entry, handle);
1071 
1072  RETURN_MODULE_RCODE(rcode);
1073 }
1074 
1075 /** Load the avps by ${key}.
1076  *
1077  * @return
1078  * - #RLM_MODULE_UPDATED on success.
1079  * - #RLM_MODULE_NOTFOUND on cache miss.
1080  * - #RLM_MODULE_FAIL on failure.
1081  */
1082 static unlang_action_t CC_HINT(nonnull) mod_method_load(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1083 {
1084  rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1085  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1086  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1087  rlm_cache_entry_t *entry = NULL;
1088  rlm_cache_handle_t *handle = NULL;
1089 
1090  if (env->key->vb_length == 0) {
1091  REDEBUG("Zero length key string is invalid");
1093  }
1094 
1095  /* Good to go? */
1096  if (cache_acquire(&handle, inst, request) < 0) {
1098  }
1099 
1100  cache_find(&rcode, &entry, inst, request, &handle, env->key);
1101  if (rcode == RLM_MODULE_FAIL) goto finish;
1102 
1103  if (!entry) {
1104  RDEBUG2("Entry not found to load");
1105  rcode = RLM_MODULE_NOTFOUND;
1106  goto finish;
1107  }
1108 
1109  rcode = cache_merge(inst, request, entry);
1110 
1111 finish:
1112  cache_unref(request, inst, entry, handle);
1113 
1114  RETURN_MODULE_RCODE(rcode);
1115 }
1116 
1117 /** Create, or update a cache entry
1118  *
1119  * @return
1120  * - #RLM_MODULE_OK on success.
1121  * - #RLM_MODULE_UPDATED if we merged the cache entry.
1122  * - #RLM_MODULE_FAIL on failure.
1123  */
1124 static unlang_action_t CC_HINT(nonnull) mod_method_update(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1125 {
1126  rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1127  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1128  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1129  fr_time_delta_t ttl;
1130  bool expire = false;
1131  rlm_cache_entry_t *entry = NULL;
1132  rlm_cache_handle_t *handle = NULL;
1133  fr_pair_t *vp;
1134 
1135  if (env->key->vb_length == 0) {
1136  REDEBUG("Zero length key string is invalid");
1138  }
1139 
1140  /* Good to go? */
1141  if (cache_acquire(&handle, inst, request) < 0) {
1143  }
1144 
1145  /* Process the TTL */
1146  ttl = inst->config.ttl; /* Set the default value from cache { ttl=... } */
1147  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
1148  if (vp) {
1149  if (vp->vp_int32 == 0) {
1150  expire = true;
1151  } else if (vp->vp_int32 < 0) {
1152  ttl = fr_time_delta_from_sec(-(vp->vp_int32));
1153  /* Updating the TTL */
1154  } else {
1155  ttl = fr_time_delta_from_sec(vp->vp_int32);
1156  }
1157 
1158  DEBUG3("Overwriting the default TTL %pV -> %d", fr_box_time_delta(ttl), vp->vp_int32);
1159  }
1160 
1161  /*
1162  * We can only alter the TTL on an entry if it exists.
1163  */
1164  cache_find(&rcode, &entry, inst, request, &handle, env->key);
1165  if (rcode == RLM_MODULE_FAIL) goto finish;
1166 
1167  if (rcode == RLM_MODULE_OK) {
1168  fr_assert(entry != NULL);
1169 
1170  DEBUG3("Updating the TTL -> %pV", fr_box_time_delta(ttl));
1171 
1172  entry->expires = fr_unix_time_add(fr_time_to_unix_time(request->packet->timestamp), ttl);
1173 
1174  cache_set_ttl(&rcode, inst, request, &handle, entry);
1175  if (rcode == RLM_MODULE_FAIL) goto finish;
1176  }
1177 
1178  /*
1179  * Expire the entry if told to, and we either don't know whether
1180  * it exists, or we know it does.
1181  *
1182  * We only expire if we're not inserting, as driver insert methods
1183  * should perform upserts.
1184  */
1185  if (expire) {
1186  DEBUG3("Expiring cache entry");
1187 
1188  cache_expire(&rcode, inst, request, &handle, env->key);
1189  if (rcode == RLM_MODULE_FAIL) goto finish;
1190  }
1191 
1192  /*
1193  * Inserts are upserts, so we don't care about the
1194  * entry state.
1195  */
1196  cache_insert(&rcode, inst, request, &handle, env->key, env->maps, ttl);
1197  if (rcode == RLM_MODULE_OK) rcode = RLM_MODULE_UPDATED;
1198 
1199 finish:
1200  cache_unref(request, inst, entry, handle);
1201 
1202  RETURN_MODULE_RCODE(rcode);
1203 }
1204 
1205 /** Create, or update a cache entry
1206  *
1207  * @return
1208  * - #RLM_MODULE_NOOP if an entry already existed.
1209  * - #RLM_MODULE_UPDATED if we inserted a cache entry.
1210  * - #RLM_MODULE_FAIL on failure.
1211  */
1212 static unlang_action_t CC_HINT(nonnull) mod_method_store(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1213 {
1214  rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1215  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1216  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1217  fr_time_delta_t ttl;
1218  rlm_cache_entry_t *entry = NULL;
1219  rlm_cache_handle_t *handle = NULL;
1220  fr_pair_t *vp;
1221 
1222  if (env->key->vb_length == 0) {
1223  REDEBUG("Zero length key string is invalid");
1225  }
1226 
1227  if (cache_acquire(&handle, inst, request) < 0) {
1229  }
1230 
1231  /* Process the TTL */
1232  ttl = inst->config.ttl; /* Set the default value from cache { ttl=... } */
1233  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
1234  if (vp && (vp->vp_int32 > 0)) {
1235  ttl = fr_time_delta_from_sec(vp->vp_int32);
1236 
1237  DEBUG3("Overriding default TTL %pV -> %d", fr_box_time_delta(ttl), vp->vp_int32);
1238  }
1239 
1240  /*
1241  * We can only alter the TTL on an entry if it exists.
1242  */
1243  cache_find(&rcode, &entry, inst, request, &handle, env->key);
1244  switch (rcode) {
1245  default:
1246  case RLM_MODULE_OK:
1247  rcode = RLM_MODULE_NOOP;
1248  goto finish;
1249 
1250  case RLM_MODULE_FAIL:
1251  goto finish;
1252 
1253  case RLM_MODULE_NOTFOUND:
1254  break;
1255  }
1256 
1257  /*
1258  * Inserts are upserts, so we don't care about the
1259  * entry state, just that we're not meant to be
1260  * setting the TTL, which precludes performing an
1261  * insert.
1262  */
1263  cache_insert(&rcode, inst, request, &handle, env->key, env->maps, ttl);
1264 
1265 finish:
1266  cache_unref(request, inst, entry, handle);
1267  if (rcode == RLM_MODULE_OK) rcode = RLM_MODULE_UPDATED;
1268 
1269  RETURN_MODULE_RCODE(rcode);
1270 }
1271 
1272 /** Delete the entries by ${key}
1273  *
1274  * @return
1275  * - #RLM_MODULE_OK on success.
1276  * - #RLM_MODULE_NOTFOUND on cache miss.
1277  * - #RLM_MODULE_FAIL on failure.
1278  */
1279 static unlang_action_t CC_HINT(nonnull) mod_method_clear(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1280 {
1281  rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1282  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1283  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1284  rlm_cache_entry_t *entry = NULL;
1285  rlm_cache_handle_t *handle = NULL;
1286 
1287  DEBUG3("Calling %s.clear", mctx->mi->name);
1288 
1289  if (env->key->vb_length == 0) {
1290  REDEBUG("Zero length key string is invalid");
1292  }
1293 
1294  /* Good to go? */
1295  if (cache_acquire(&handle, inst, request) < 0) {
1297  }
1298 
1299  cache_find(&rcode, &entry, inst, request, &handle, env->key);
1300  if (rcode == RLM_MODULE_FAIL) goto finish;
1301 
1302  if (!entry) {
1303  REDEBUG2("Entry not found to delete");
1304  rcode = RLM_MODULE_NOTFOUND;
1305  goto finish;
1306  }
1307 
1308  cache_expire(&rcode, inst, request, &handle, env->key);
1309 
1310 finish:
1311  cache_unref(request, inst, entry, handle);
1312 
1313  RETURN_MODULE_RCODE(rcode);
1314 }
1315 
1316 /** Change the TTL on an existing entry.
1317  *
1318  * @return
1319  * - #RLM_MODULE_UPDATED on success.
1320  * - #RLM_MODULE_NOTFOUND on cache miss.
1321  * - #RLM_MODULE_FAIL on failure.
1322  */
1323 static unlang_action_t CC_HINT(nonnull) mod_method_ttl(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1324 {
1325  rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1326  cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1327  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1328  fr_time_delta_t ttl;
1329  rlm_cache_entry_t *entry = NULL;
1330  rlm_cache_handle_t *handle = NULL;
1331  fr_pair_t *vp;
1332 
1333  DEBUG3("Calling %s.ttl", mctx->mi->name);
1334 
1335  if (env->key->vb_length == 0) {
1336  REDEBUG("Zero length key string is invalid");
1338  }
1339 
1340  /* Good to go? */
1341  if (cache_acquire(&handle, inst, request) < 0) {
1343  }
1344 
1345  /* Process the TTL */
1346  ttl = inst->config.ttl; /* Set the default value from cache { ttl=... } */
1347  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
1348  if (vp) {
1349  if (vp->vp_int32 < 0) {
1350  ttl = fr_time_delta_from_sec(-(vp->vp_int32));
1351  /* Updating the TTL */
1352  } else {
1353  ttl = fr_time_delta_from_sec(vp->vp_int32);
1354  }
1355 
1356  DEBUG3("Overwriting the default TTL %pV -> %d", fr_box_time_delta(inst->config.ttl), vp->vp_int32);
1357  }
1358 
1359  /*
1360  * We can only alter the TTL on an entry if it exists.
1361  */
1362  cache_find(&rcode, &entry, inst, request, &handle, env->key);
1363  if (rcode == RLM_MODULE_FAIL) goto finish;
1364 
1365  if (rcode == RLM_MODULE_OK) {
1366  fr_assert(entry != NULL);
1367 
1368  DEBUG3("Updating the TTL -> %pV", fr_box_time_delta(ttl));
1369 
1370  entry->expires = fr_unix_time_add(fr_time_to_unix_time(request->packet->timestamp), ttl);
1371 
1372  cache_set_ttl(&rcode, inst, request, &handle, entry);
1373  if (rcode == RLM_MODULE_FAIL) goto finish;
1374 
1375  rcode = RLM_MODULE_UPDATED;
1376  }
1377 
1378 finish:
1379  cache_unref(request, inst, entry, handle);
1380 
1381  RETURN_MODULE_RCODE(rcode);
1382 }
1383 
1384 /** Free any memory allocated under the instance
1385  *
1386  */
1387 static int mod_detach(module_detach_ctx_t const *mctx)
1388 {
1389  rlm_cache_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1390 
1391  /*
1392  * We need to explicitly free all children, so if the driver
1393  * parented any memory off the instance, their destructors
1394  * run before we unload the bytecode for them.
1395  *
1396  * If we don't do this, we get a SEGV deep inside the talloc code
1397  * when it tries to call a destructor that no longer exists.
1398  */
1399  talloc_free_children(inst);
1400 
1401  return 0;
1402 }
1403 
1404 /** Verify that a map in the cache section makes sense
1405  *
1406  */
1407 static int cache_verify(map_t *map, void *uctx)
1408 {
1409  if (unlang_fixup_update(map, uctx) < 0) return -1;
1410 
1411  if (!tmpl_is_attr(map->lhs)) {
1412  cf_log_err(map->ci, "Destination must be an attribute ref or a list");
1413  return -1;
1414  }
1415 
1416  return 0;
1417 }
1418 
1419 static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1420  CONF_ITEM *ci,
1421  UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1422 {
1423  CONF_SECTION *update = cf_item_to_section(ci);
1424  call_env_parsed_t *parsed;
1425  map_list_t *maps;
1426 
1427  MEM(parsed = call_env_parsed_add(ctx, out,
1429 
1430  MEM(maps = talloc(parsed, map_list_t));
1431  map_list_init(maps);
1432 
1433  if (map_afrom_cs(maps, maps, update,
1434  t_rules, t_rules, cache_verify, NULL, MAX_ATTRMAP) < 0) {
1435  error:
1436  call_env_parsed_free(out, parsed);
1437  return -1;
1438  }
1439 
1440  if (map_list_empty(maps)) {
1441  cf_log_err(update, "Update section must not be empty");
1442  goto error;
1443  }
1444 
1445  call_env_parsed_set_data(parsed, maps);
1446 
1447  return 0;
1448 }
1449 
1450 /** Create a new rlm_cache_instance
1451  *
1452  */
1453 static int mod_instantiate(module_inst_ctx_t const *mctx)
1454 {
1455  rlm_cache_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1456  CONF_SECTION *conf = mctx->mi->conf;
1457 
1458  /*
1459  * Non optional fields and callbacks
1460  */
1461  fr_assert(inst->driver->common.name);
1462  fr_assert(inst->driver->find);
1463  fr_assert(inst->driver->insert);
1464  fr_assert(inst->driver->expire);
1465 
1466  if (!fr_time_delta_ispos(inst->config.ttl)) {
1467  cf_log_err(conf, "Must set 'ttl' to non-zero");
1468  return -1;
1469  }
1470 
1471  if (inst->config.epoch != 0) {
1472  cf_log_err(conf, "Must not set 'epoch' in the configuration files");
1473  return -1;
1474  }
1475 
1476  return 0;
1477 }
1478 
1479 /** Register module xlats
1480  *
1481  */
1482 static int mod_bootstrap(module_inst_ctx_t const *mctx)
1483 {
1484  xlat_t *xlat;
1485 
1486  /*
1487  * Register the cache xlat function
1488  */
1489  xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, cache_xlat, FR_TYPE_VOID);
1492 
1493  xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "ttl.get", cache_ttl_get_xlat, FR_TYPE_VOID);
1495 
1496  return 0;
1497 }
1498 
1499 /*
1500  * The module name should be the only globally exported symbol.
1501  * That is, everything else should be 'static'.
1502  *
1503  * If the module needs to temporarily modify it's instantiation
1504  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1505  * The server will then take care of ensuring that the module
1506  * is single-threaded.
1507  */
1509  .common = {
1510  .magic = MODULE_MAGIC_INIT,
1511  .name = "cache",
1512  .inst_size = sizeof(rlm_cache_t),
1513  .config = module_config,
1514  .bootstrap = mod_bootstrap,
1516  .detach = mod_detach
1517  },
1518  .method_group = {
1519  .bindings = (module_method_binding_t[]){
1520  { .section = SECTION_NAME("clear", CF_IDENT_ANY), .method = mod_method_clear, .method_env = &cache_method_env },
1521  { .section = SECTION_NAME("load", CF_IDENT_ANY), .method = mod_method_load, .method_env = &cache_method_env },
1522  { .section = SECTION_NAME("status", CF_IDENT_ANY), .method = mod_method_status, .method_env = &cache_method_env },
1523  { .section = SECTION_NAME("store", CF_IDENT_ANY), .method = mod_method_store, .method_env = &cache_method_env },
1524  { .section = SECTION_NAME("ttl", CF_IDENT_ANY), .method = mod_method_ttl, .method_env = &cache_method_env },
1525  { .section = SECTION_NAME("update", CF_IDENT_ANY), .method = mod_method_update, .method_env = &cache_method_env },
1526  { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_cache_it, .method_env = &cache_method_env },
1528  }
1529  }
1530 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
static int const char char buffer[256]
Definition: acutest.h:574
#define RCSID(id)
Definition: build.h:481
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr)
Remove a call_env_parsed_t from the list of parsed call envs.
Definition: call_env.c:718
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition: call_env.c:631
int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Standard function we use for parsing call env pairs.
Definition: call_env.c:375
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
Definition: call_env.c:688
#define CALL_ENV_TERMINATOR
Definition: call_env.h:231
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition: call_env.h:235
@ CALL_ENV_FLAG_NONE
Definition: call_env.h:74
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:75
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition: call_env.h:224
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition: call_env.h:407
int(* call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule)
Callback for performing custom parsing of a CONF_PAIR.
Definition: call_env.h:146
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition: call_env.h:335
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition: call_env.h:384
Per method call config.
Definition: call_env.h:175
#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_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:241
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
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
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_log_perr(_cf, _fmt,...)
Definition: cf_util.h:296
#define CF_IDENT_ANY
Definition: cf_util.h:78
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition: dcursor.h:480
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
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:288
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:337
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
static bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da)
Return true if this attribute is parented directly off the dictionary root.
Definition: dict.h:754
static fr_slen_t in
Definition: dict.h:821
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
static xlat_action_t cache_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Allow single attribute values to be retrieved from the cache.
Definition: rlm_cache.c:888
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define REDEBUG2(fmt,...)
Definition: log.h:372
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition: map.c:1487
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
Definition: map.c:1014
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition: map.c:1781
ssize_t map_print(fr_sbuff_t *out, map_t const *map)
Print a map to a string.
Definition: map.c:2295
void map_debug_log(request_t *request, map_t const *map, fr_pair_t const *vp)
Definition: map.c:2350
talloc_free(reap)
#define MAP_VERIFY(_x)
Definition: map.h:108
fr_type_t
Definition: merged_model.c:80
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
Definition: merged_model.c:113
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_INT32
32 Bit signed integer.
Definition: merged_model.c:105
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
@ FR_TYPE_BOOL
A truth value.
Definition: merged_model.c:95
long int ssize_t
Definition: merged_model.c:24
static bool is_printable(void const *value, size_t len)
Check whether the string is made up of printable UTF8 chars.
Definition: misc.h:107
int unlang_fixup_update(map_t *map, void *ctx)
Validate and fixup a map that's part of an update section.
Definition: compile.c:470
void * env_data
Per call environment data.
Definition: module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
module_instance_t * mi
Module instance to detach.
Definition: module_ctx.h:57
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for detach calls.
Definition: module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
int module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic conf_parser_t func for loading drivers.
Definition: module_rlm.c:950
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
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
static const conf_parser_t config[]
Definition: base.c:183
static rc_stats_t stats
Definition: radclient-ng.c:74
#define pair_update_request(_attr, _da)
Definition: radclient-ng.c:60
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG_ENABLED2()
Definition: radclient.h:50
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
static rs_t * conf
Definition: radsniff.c:53
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
#define RETURN_MODULE_INVALID
Definition: rcode.h:59
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
#define RETURN_MODULE_NOTFOUND
Definition: rcode.h:61
static int cache_acquire(rlm_cache_handle_t **out, rlm_cache_t const *inst, request_t *request)
Get exclusive use of a handle to access the cache.
Definition: rlm_cache.c:167
static int mod_detach(module_detach_ctx_t const *mctx)
Free any memory allocated under the instance.
Definition: rlm_cache.c:1387
static unlang_action_t mod_method_update(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Create, or update a cache entry.
Definition: rlm_cache.c:1124
static fr_dict_attr_t const * attr_cache_allow_merge
Definition: rlm_cache.c:90
static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule)
Definition: rlm_cache.c:120
fr_type_t ktype
Key type.
Definition: rlm_cache.c:67
fr_value_box_t * key
To lookup the cache entry with.
Definition: rlm_cache.c:62
static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule)
static unlang_action_t mod_method_ttl(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Change the TTL on an existing entry.
Definition: rlm_cache.c:1323
static fr_dict_attr_t const * attr_cache_ttl
Definition: rlm_cache.c:92
static fr_dict_attr_t const * attr_cache_merge_new
Definition: rlm_cache.c:88
static void cache_release(rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle)
Release a handle we previously acquired.
Definition: rlm_cache.c:180
static fr_dict_t const * dict_freeradius
Definition: rlm_cache.c:80
static int cache_verify(map_t *map, void *uctx)
Verify that a map in the cache section makes sense.
Definition: rlm_cache.c:1407
static rlm_cache_entry_t * cache_alloc(rlm_cache_t const *inst, request_t *request)
Allocate a cache entry.
Definition: rlm_cache.c:207
map_list_t * maps
Attribute map applied to cache entries.
Definition: rlm_cache.c:63
static unlang_action_t cache_set_ttl(rlm_rcode_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, rlm_cache_entry_t *c)
Update the TTL of an entry.
Definition: rlm_cache.c:564
static unlang_action_t mod_cache_it(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Do caching checks.
Definition: rlm_cache.c:620
static fr_dict_attr_t const * attr_cache_allow_insert
Definition: rlm_cache.c:91
static unlang_action_t cache_insert(rlm_rcode_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, fr_value_box_t const *key, map_list_t const *maps, fr_time_delta_t ttl)
Create and insert a cache entry.
Definition: rlm_cache.c:387
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Register module xlats.
Definition: rlm_cache.c:1482
static fr_dict_attr_t const * attr_cache_status_only
Definition: rlm_cache.c:89
static void cache_free(rlm_cache_t const *inst, rlm_cache_entry_t **c)
Free memory associated with a cache entry.
Definition: rlm_cache.c:228
static unlang_action_t mod_method_status(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Get the status by ${key} (without load)
Definition: rlm_cache.c:1044
static unlang_action_t mod_method_store(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Create, or update a cache entry.
Definition: rlm_cache.c:1212
static fr_dict_attr_t const * attr_cache_entry_hits
Definition: rlm_cache.c:93
static unlang_action_t mod_method_load(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Load the avps by ${key}.
Definition: rlm_cache.c:1082
static xlat_arg_parser_t const cache_xlat_args[]
Definition: rlm_cache.c:878
module_rlm_t rlm_cache
Definition: rlm_cache.c:1508
static int cache_reconnect(rlm_cache_handle_t **handle, rlm_cache_t const *inst, request_t *request)
Reconnect an suspected inviable handle.
Definition: rlm_cache.c:192
static const call_env_method_t cache_method_env
Definition: rlm_cache.c:71
fr_dict_attr_autoload_t rlm_cache_dict_attr[]
Definition: rlm_cache.c:96
fr_dict_autoload_t rlm_cache_dict[]
Definition: rlm_cache.c:83
static xlat_action_t cache_ttl_get_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition: rlm_cache.c:960
static rlm_rcode_t cache_merge(rlm_cache_t const *inst, request_t *request, rlm_cache_entry_t *c)
Merge a cached entry into a request_t.
Definition: rlm_cache.c:243
static void cache_unref(request_t *request, rlm_cache_t const *inst, rlm_cache_entry_t *entry, rlm_cache_handle_t *handle)
Release the allocated resources and cleanup the avps.
Definition: rlm_cache.c:1000
int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition: rlm_cache.c:106
static const conf_parser_t module_config[]
Definition: rlm_cache.c:49
static unlang_action_t cache_expire(rlm_rcode_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, fr_value_box_t const *key)
Expire a cache entry (removing it from the datastore)
Definition: rlm_cache.c:359
static unlang_action_t mod_method_clear(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Delete the entries by ${key}.
Definition: rlm_cache.c:1279
static unlang_action_t cache_find(rlm_rcode_t *p_result, rlm_cache_entry_t **out, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, fr_value_box_t const *key)
Find a cached entry.
Definition: rlm_cache.c:289
static int mod_instantiate(module_inst_ctx_t const *mctx)
Create a new rlm_cache_instance.
Definition: rlm_cache.c:1453
fr_value_box_t key
Key used to identify entry.
Definition: rlm_cache.h:73
map_list_t maps
Head of the maps list.
Definition: rlm_cache.h:78
fr_unix_time_t created
When the entry was created.
Definition: rlm_cache.h:75
long long int hits
How many times the entry has been retrieved.
Definition: rlm_cache.h:74
#define MAX_ATTRMAP
Definition: rlm_cache.h:37
cache_status_t
Definition: rlm_cache.h:39
@ CACHE_RECONNECT
Handle needs to be reconnected.
Definition: rlm_cache.h:40
@ CACHE_OK
Cache entry found/updated.
Definition: rlm_cache.h:42
@ CACHE_MISS
Cache entry notfound.
Definition: rlm_cache.h:43
void rlm_cache_handle_t
Definition: rlm_cache.h:35
fr_unix_time_t expires
When the entry expires.
Definition: rlm_cache.h:76
Configuration for the rlm_cache module.
Definition: rlm_cache.h:51
Definition: rlm_cache.h:72
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
char const * name
Instance name e.g. user_database.
Definition: module.h:335
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
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
module_t * exported
Public module structure.
Definition: module.h:276
Module instance data.
Definition: module.h:265
Named methods exported by a module.
Definition: module.h:173
#define tmpl_value(_tmpl)
Definition: tmpl.h:948
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
#define tmpl_is_attr(vpt)
Definition: tmpl.h:213
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition: tmpl.h:146
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition: tmpl.h:142
int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
Create a new tmpl from a list tmpl and a da.
static bool tmpl_attr_tail_da_is_structural(tmpl_t const *vpt)
Return true if the the last attribute reference is a structural attribute.
Definition: tmpl.h:846
static bool tmpl_is_list(tmpl_t const *vpt)
Definition: tmpl.h:931
ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
static fr_type_t tmpl_cast_get(tmpl_t *vpt)
Definition: tmpl.h:1226
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition: tmpl.h:915
tmpl_t * tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
Create a new heap allocated tmpl_t.
int tmpl_cast_set(tmpl_t *vpt, fr_type_t type)
Set a cast for a tmpl.
fr_type_t tmpl_expanded_type(tmpl_t const *vpt)
Return the native data type of the expression.
Definition: tmpl_eval.c:209
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Value pair map.
Definition: map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition: map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
map_list_t child
parent map, for nested ones
Definition: map.h:89
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:79
CONF_ITEM * ci
Config item that the map was created from.
Definition: map.h:85
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
#define talloc_get_type_abort_const
Definition: talloc.h:282
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:590
#define fr_time_delta_ispos(_a)
Definition: time.h:290
static fr_unix_time_t fr_unix_time_from_sec(int64_t sec)
Definition: time.h:449
#define fr_unix_time_sub(_a, _b)
Subtract one time from another.
Definition: time.h:357
static fr_unix_time_t fr_time_to_unix_time(fr_time_t when)
Convert an fr_time_t (internal time) to our version of unix time (wallclock time)
Definition: time.h:688
#define fr_unix_time_lt(_a, _b)
Definition: time.h:367
#define fr_unix_time_add(_a, _b)
Add a time/time delta together.
Definition: time.h:324
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
enum fr_token fr_token_t
@ T_SINGLE_QUOTED_STRING
Definition: token.h:122
@ T_BARE_WORD
Definition: token.h:120
@ T_DOUBLE_QUOTED_STRING
Definition: token.h:121
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
#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
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition: pair_inline.c:43
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition: pair_inline.c:70
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:591
static fr_slen_t parent
Definition: pair.h:851
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:3740
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition: value.h:621
#define fr_box_time_delta(_val)
Definition: value.h:343
int nonnull(2, 5))
#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_box_time(_val)
Definition: value.h:326
static size_t char ** out
Definition: value.h:997
#define fr_box_date(_val)
Definition: value.h:324
void * env_data
Expanded call env data.
Definition: xlat_ctx.h:53
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:52
An xlat calling ctx.
Definition: xlat_ctx.h:49
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
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition: xlat_func.c:392