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