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