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