The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
25RCSID("$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
44
45int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
46static 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);
47static 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
49static const conf_parser_t module_config[] = {
50 { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_cache_t, driver_submodule), .dflt = "rbtree",
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
61typedef 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
66typedef struct {
67 fr_type_t ktype; //!< Key type
68
70
79
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
106int 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);
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
120static 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 */
180static 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 */
192static 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 */
242static 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)),
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;
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
527skip_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 */
620static 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
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
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
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
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
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
847finish:
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);
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 */
887static 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));
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));
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 */
1000static 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 */
1044static 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);
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
1069finish:
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 */
1082static 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);
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
1111finish:
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 */
1124static 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);
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
1199finish:
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 */
1212static 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);
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
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
1265finish:
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 */
1279static 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);
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
1310finish:
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 */
1323static 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);
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
1378finish:
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 */
1387static 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 */
1407static 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
1419static 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 */
1453static 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 */
1482static 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),
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:576
#define RCSID(id)
Definition build.h:483
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
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
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
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
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_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:242
@ 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:642
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:596
#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:579
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_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
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
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition dcursor.h:480
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define MEM(x)
Definition debug.h:36
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
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:757
static fr_slen_t in
Definition dict.h:824
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
#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
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
long int ssize_t
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
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
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
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
#define fr_assert(_expr)
Definition rad_assert.h:38
#define pair_update_request(_attr, _da)
#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
#define RETURN_MODULE_FAIL
Definition rcode.h:56
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
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 rlm_cache_entry_t * cache_alloc(rlm_cache_t const *inst, request_t *request)
Allocate a cache entry.
Definition rlm_cache.c:207
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:1310
#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
size_t inst_size
Size of the module's instance data.
Definition module.h:203
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
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.
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:915
@ 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_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
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
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:148
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:168
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:147
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
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
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