The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: 06851b77d75d9812802e3fff61bf1945d12f1f96 $
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: 06851b77d75d9812802e3fff61bf1945d12f1f96 $")
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, void **rctx_out, 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
67/** Resume context when driver is async
68 */
69typedef struct {
74 void *rctx; //!< Driver resume context
75 void *uctx; //!< Module method context
77
78/** Additional resume context used by mod_cache_it
79 */
80typedef struct {
81 bool merge;
82 bool insert;
83 bool expire;
84 bool set_ttl;
85 int exists;
88
97
99
102 { .out = &dict_freeradius, .proto = "freeradius" },
104};
105
112
115 { .out = &attr_cache_merge_new, .name = "Cache-Merge-New", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
116 { .out = &attr_cache_status_only, .name = "Cache-Status-Only", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
117 { .out = &attr_cache_allow_merge, .name = "Cache-Allow-Merge", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
118 { .out = &attr_cache_allow_insert, .name = "Cache-Allow-Insert", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
119 { .out = &attr_cache_ttl, .name = "Cache-TTL", .type = FR_TYPE_INT32, .dict = &dict_freeradius },
120 { .out = &attr_cache_entry_hits, .name = "Cache-Entry-Hits", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
122};
123
124int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
125{
126 rlm_cache_t *inst = talloc_get_type_abort(parent, rlm_cache_t);
128 int ret;
129
130 if (unlikely((ret = module_rlm_submodule_parse(ctx, out, parent, ci, rule)) < 0)) return ret;
131 mi = talloc_get_type_abort(*((void **)out), module_instance_t);
132 inst->driver = (rlm_cache_driver_t const *)mi->exported; /* Public symbol exported by the submodule */
133
134 return 0;
135}
136
137static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
138 call_env_ctx_t const *cec,
139 call_env_parser_t const *rule)
140{
142 call_env_parse_pair_t func = inst->driver->key_parse ? inst->driver->key_parse : call_env_parse_pair;
143 tmpl_t *key_tmpl;
144 fr_type_t cast;
145 int ret;
146 /*
147 * Call the custom key parse function, OR the standard call_env_parse_pair
148 * function, depending on whether the driver calls a custom parsing function.
149 */
150 if (unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, cec, rule)) < 0)) return ret;
151 *((tmpl_t **)out) = key_tmpl;
152
153 /*
154 * Unless the driver has a custom key parse function, we only allow keys of
155 * type string.
156 */
157 if (inst->driver->key_parse) return 0;
158
159 cast = tmpl_cast_get(key_tmpl);
160 switch (cast) {
161 case FR_TYPE_STRING:
162 case FR_TYPE_NULL:
163 case FR_TYPE_VOID:
164 break;
165
166 default:
167 cf_log_err(ci, "Driver only allows key type '%s', got '%s'",
169 return -1;
170 }
171
172 if (tmpl_cast_set(key_tmpl, FR_TYPE_STRING) < 0) {
173 cf_log_perr(ci, "Can't convert key type '%s' to '%s'",
175 return -1;
176 }
177
178 return 0;
179}
180
183 unlang_module_signal_t cancel, void *driver_rctx, void *uctx)
184{
185 cache_rctx_t *rctx;
186
187 MEM(rctx = talloc(unlang_interpret_frame_talloc_ctx(request), cache_rctx_t));
188 rctx->handle = handle;
189 rctx->rctx = driver_rctx;
190 rctx->key = key;
191 rctx->entry = entry;
192 rctx->ttl = *ttl;
193 rctx->uctx = uctx;
194
195 return unlang_module_yield(request, resume, cancel, ~FR_SIGNAL_CANCEL, rctx);
196}
197
198/** Get exclusive use of a handle to access the cache
199 *
200 */
202{
203 if (!inst->driver->acquire) {
204 *out = NULL;
205 return 0;
206 }
207
208 return inst->driver->acquire(out, &inst->config, inst->driver_submodule->data, request);
209}
210
211/** Release a handle we previously acquired
212 *
213 */
214static void cache_release(rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle)
215{
216 if (!inst->driver->release) return;
217 if (!handle || !*handle) return;
218
219 inst->driver->release(&inst->config, inst->driver_submodule->data, request, *handle);
220 *handle = NULL;
221}
222
223/** Reconnect an suspected inviable handle
224 *
225 */
226static int cache_reconnect(rlm_cache_handle_t **handle, rlm_cache_t const *inst, request_t *request)
227{
228 fr_assert(inst->driver->reconnect);
229
230 return inst->driver->reconnect(handle, &inst->config, inst->driver_submodule->data, request);
231}
232
233/** Allocate a cache entry
234 *
235 * This is used so that drivers may use their own allocation functions
236 * to allocate structures larger than the normal rlm_cache_entry_t.
237 *
238 * If the driver doesn't specify a custom allocation function, the cache
239 * entry is talloced in the NULL ctx.
240 */
242{
243 if (inst->driver->alloc) return inst->driver->alloc(&inst->config, inst->driver_submodule->data, request);
244
245 return talloc_zero(NULL, rlm_cache_entry_t);
246}
247
248/** Free memory associated with a cache entry
249 *
250 * This does not necessarily remove the entry from the cache, cache_expire
251 * should be used for that.
252 *
253 * This function should be called when an entry that is known to have been
254 * retrieved or inserted into a data store successfully, is no longer needed.
255 *
256 * Some drivers (like rlm_cache_rbtree) don't register a free function.
257 * This means that the cache entry never needs to be explicitly freed.
258 *
259 * @param[in] inst Module instance.
260 * @param[in,out] c Cache entry to free.
261 */
263{
264 if (!c || !*c || !inst->driver->free) return;
265
266 inst->driver->free(*c);
267 *c = NULL;
268}
269
270/** Merge a cached entry into a #request_t
271 *
272 * @return
273 * - #RLM_MODULE_OK if no entries were merged.
274 * - #RLM_MODULE_UPDATED if entries were merged.
275 */
276static rlm_rcode_t cache_merge(rlm_cache_t const *inst, request_t *request, rlm_cache_entry_t *c) CC_HINT(nonnull);
278{
279 fr_pair_t *vp;
280 map_t *map = NULL;
281 int merged = 0;
282
283 RDEBUG2("Merging cache entry into request");
284 RINDENT();
285 while ((map = map_list_next(&c->maps, map))) {
286 /*
287 * The only reason that the application of a map entry
288 * can fail, is if the destination list or request
289 * isn't valid. For now we don't consider this fatal
290 * and continue merging the rest of the maps.
291 */
292 if (map_to_request(request, map, map_to_vp, NULL) < 0) {
293 char buffer[1024];
294
295 map_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map);
296 REXDENT();
297 RDEBUG2("Skipping %s", buffer);
298 RINDENT();
299 continue;
300 }
301 merged++;
302 }
303 REXDENT();
304
305 if (inst->config.stats) {
306 fr_assert(request->packet != NULL);
308 vp->vp_uint32 = c->hits;
309 }
310
311 return merged > 0 ?
314}
315
316/** Process results of cache find - either synchronous or async
317 */
319 rlm_cache_t const *inst, request_t *request,
320 rlm_cache_handle_t **handle, fr_value_box_t const *key,
322{
323 /*
324 * Yes, but it expired, OR the "forget all" epoch has
325 * passed. Delete it, and pretend it doesn't exist.
326 */
327 if (fr_unix_time_lt(c->expires, fr_time_to_unix_time(request->packet->timestamp))) {
328 unlang_result_t tmp;
329 void *driver_rctx;
330
331 RDEBUG2("Found entry for \"%pV\", but it expired %pV ago at %pV (packet received %pV). Removing it",
332 key,
333 fr_box_time_delta(fr_unix_time_sub(fr_time_to_unix_time(request->packet->timestamp), c->expires)),
335 fr_box_time(request->packet->timestamp));
336
337 expired:
338 /*
339 * @todo - handle async entry expiry during fetch.
340 * At present only Redis is async and expiry is handled by Redis so
341 * this is not required.
342 */
343 if (!inst->driver->expire_resume) cache_expire(&tmp, &driver_rctx, inst, request, handle, key);
344 cache_free(inst, &c);
345 RETURN_UNLANG_NOTFOUND; /* Couldn't find a non-expired entry */
346 }
347
348 if (fr_unix_time_lt(c->created, fr_unix_time_from_sec(inst->config.epoch))) {
349 RDEBUG2("Found entry for \"%pV\", but it was created before the current epoch. Removing it",
350 key);
351 goto expired;
352 }
353 RDEBUG2("Found entry for \"%pV\"", key);
354
355 c->hits++;
356 *out = c;
357
359}
360
361/** Resume callback after async cache find
362 */
364 rlm_cache_t const *inst, request_t *request,
365 rlm_cache_handle_t **handle, fr_value_box_t const *key,
366 void *rctx)
367{
368 cache_status_t ret;
370
371 ret = inst->driver->find_resume(&c, &inst->config, inst->driver_submodule->data, request, *handle, rctx);
372 switch (ret) {
373 case CACHE_OK:
374 break;
375
376 case CACHE_MISS:
377 RDEBUG2("No cache entry found for \"%pV\"", key);
379
380 case CACHE_YIELD:
381 return UNLANG_ACTION_YIELD;
382
383 default:
385 }
386
387 return cache_find_results(p_result, out, inst, request, handle, key, c);
388}
389
390static void cache_find_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
391{
392 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
393 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
394
395 if (!inst->driver->find_cancel) return;
396
397 inst->driver->find_cancel(&inst->config, inst->driver_submodule->data, request, rctx->handle, rctx->rctx);
398}
399
400/** Find a cached entry.
401 *
402 * @return
403 * - #RLM_MODULE_OK on cache hit.
404 * - #RLM_MODULE_FAIL on failure.
405 * - #RLM_MODULE_NOTFOUND on cache miss.
406 * - #UNLANG_ACTION_YIELD if the driver has yielded.
407 */
409 rlm_cache_t const *inst, request_t *request,
410 rlm_cache_handle_t **handle, fr_value_box_t const *key)
411{
412 cache_status_t ret;
413
415
416 *out = NULL;
417
418 for (;;) {
419 ret = inst->driver->find(&c, out_rctx, &inst->config, inst->driver_submodule->data, request, *handle, key);
420 switch (ret) {
421 case CACHE_RECONNECT:
422 RDEBUG2("Reconnecting...");
423 if (cache_reconnect(handle, inst, request) == 0) continue;
425
426 case CACHE_OK:
427 break;
428
429 case CACHE_MISS:
430 RDEBUG2("No cache entry found for \"%pV\"", key);
432
433 case CACHE_YIELD:
434 return UNLANG_ACTION_YIELD;
435
436 default:
438
439 }
440
441 break;
442 }
443
444 return cache_find_results(p_result, out, inst, request, handle, key, c);
445}
446
447/** Resume callback after async cache expire
448 */
450 rlm_cache_handle_t **handle, void *rctx)
451{
452 cache_status_t ret;
453
454 ret = inst->driver->expire_resume(&inst->config, inst->driver_submodule->data, request, handle, rctx);
455 switch (ret) {
456 case CACHE_OK:
458
459 case CACHE_MISS:
461
462 case CACHE_YIELD:
463 return UNLANG_ACTION_YIELD;
464
465 default:
467 }
468}
469
470static void cache_expire_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
471{
472 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
473 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
474
475 if (!inst->driver->expire_cancel) return;
476
477 inst->driver->expire_cancel(&inst->config, inst->driver_submodule->data, request, rctx->handle, rctx->rctx);
478}
479
480/** Expire a cache entry (removing it from the datastore)
481 *
482 * @return
483 * - #RLM_MODULE_OK on success.
484 * - #RLM_MODULE_NOTFOUND if no entry existed.
485 * - #RLM_MODULE_FAIL on failure.
486 * - #UNLANG_ACTION_YIELD if the driver yielded.
487 */
488static unlang_action_t cache_expire(unlang_result_t *p_result, void **rctx_out,
489 rlm_cache_t const *inst, request_t *request,
490 rlm_cache_handle_t **handle, fr_value_box_t const *key)
491{
492 RDEBUG2("Expiring cache entry");
493 for (;;) switch (inst->driver->expire(rctx_out, &inst->config, inst->driver_submodule->data, request, *handle, key)) {
494 case CACHE_RECONNECT:
495 if (cache_reconnect(handle, inst, request) == 0) continue;
497
498 default:
500
501 case CACHE_OK:
503
504 case CACHE_MISS:
506
507 case CACHE_YIELD:
508 return UNLANG_ACTION_YIELD;
509 }
510}
511
513 rlm_cache_t const *inst, rlm_cache_handle_t **handle,
514 fr_time_delta_t *ttl, void *rctx)
515{
516 cache_status_t ret;
517 fr_pair_t *vp;
519
520 ret = inst->driver->insert_resume(&c, &inst->config, inst->driver_submodule->data, request, *handle, rctx);
521 switch (ret) {
522 case CACHE_OK:
523 RDEBUG2("Committed entry, TTL %pV seconds", fr_box_time_delta(*ttl));
524 cache_free(inst, &c);
525
526 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_merge_new);
528
529 case CACHE_YIELD:
530 return UNLANG_ACTION_YIELD;
531
532 default:
534 }
535}
536
537static void cache_insert_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
538{
539 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
540 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
541
542 if (!inst->driver->insert_cancel) return;
543
544 inst->driver->insert_cancel(&inst->config, inst->driver_submodule->data, request, rctx->handle, rctx->rctx);
545}
546
547/** Create and insert a cache entry
548 *
549 * @return
550 * - #RLM_MODULE_OK on success.
551 * - #RLM_MODULE_UPDATED if we merged the cache entry.
552 * - #RLM_MODULE_FAIL on failure.
553 */
554static unlang_action_t cache_insert(unlang_result_t *p_result, void **out_rctx,
555 rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle,
556 fr_value_box_t const *key, map_list_t const *maps, fr_time_delta_t ttl)
557{
558 map_t const *map = NULL;
559 map_t *c_map;
560
561 fr_pair_t *vp;
562 bool merge = false;
564
565 TALLOC_CTX *pool;
566
567 if ((inst->config.max_entries > 0) && inst->driver->count &&
568 (inst->driver->count(&inst->config, inst->driver_submodule->data, request, *handle) > inst->config.max_entries)) {
569 RWDEBUG("Cache is full: %d entries", inst->config.max_entries);
571 }
572
573 c = cache_alloc(inst, request);
574 if (!c) {
576 }
577 map_list_init(&c->maps);
578 if (unlikely(fr_value_box_copy(c, &c->key, key) < 0)) {
579 RERROR("Failed copying key");
580 talloc_free(c);
582 }
583
584 /*
585 * All in NSEC resolution
586 */
587 c->created = c->expires = fr_time_to_unix_time(request->packet->timestamp);
588 c->expires = fr_unix_time_add(c->expires, ttl);
589
590 RDEBUG2("Creating new cache entry");
591
592 /*
593 * We don't have any maps to apply to the cache entry
594 * so don't try to expand them.
595 */
596 if (!maps) goto skip_maps;
597
598 /*
599 * Alloc a pool so we don't have excessive allocs when
600 * gathering fr_pair_ts to cache.
601 */
602 pool = talloc_pool(NULL, 2048);
603 while ((map = map_list_next(maps, map))) {
604 fr_pair_list_t to_cache;
605
606 fr_pair_list_init(&to_cache);
607 fr_assert(map->lhs && map->rhs);
608
609 /*
610 * Calling map_to_vp gives us exactly the same result,
611 * as if this were an update section.
612 */
613 if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) {
614 RDEBUG2("Skipping %s", map->rhs->name);
615 continue;
616 }
617
618 for (vp = fr_pair_list_head(&to_cache);
619 vp;
620 vp = fr_pair_list_next(&to_cache, vp)) {
621 /*
622 * Prevent people from accidentally caching
623 * cache control attributes.
624 */
625 if (tmpl_is_list(map->rhs)) switch (vp->da->attr) {
626 case FR_CACHE_TTL:
627 case FR_CACHE_STATUS_ONLY:
628 case FR_CACHE_MERGE_NEW:
629 case FR_CACHE_ENTRY_HITS:
630 RDEBUG2("Skipping %s", vp->da->name);
631 continue;
632
633 default:
634 break;
635 }
636 RINDENT();
637 if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
638 REXDENT();
639
640 MEM(c_map = talloc_zero(c, map_t));
641 c_map->op = map->op;
642 map_list_init(&c_map->child);
643
644 /*
645 * Now we turn the fr_pair_ts into maps.
646 */
647 switch (map->lhs->type) {
648 /*
649 * Attributes are easy, reuse the LHS, and create a new
650 * RHS with the fr_value_box_t from the fr_pair_t.
651 */
652 case TMPL_TYPE_ATTR:
653 {
654 fr_token_t quote;
655 /*
656 * If the LHS is structural, we need a new template
657 * which is the combination of the existing LHS and
658 * the attribute.
659 */
661 tmpl_attr_afrom_list(c_map, &c_map->lhs, map->lhs, vp->da);
662 } else {
663 c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */
664 }
665
666 if (vp->vp_type == FR_TYPE_STRING) {
667 quote = is_printable(vp->vp_strvalue, vp->vp_length) ?
669 } else {
670 quote = T_BARE_WORD;
671 }
672
673 MEM(c_map->rhs = tmpl_alloc(c_map,
674 TMPL_TYPE_DATA, quote, map->rhs->name, map->rhs->len));
675 if (fr_value_box_copy(c_map->rhs, tmpl_value(c_map->rhs), &vp->data) < 0) {
676 REDEBUG("Failed copying attribute value");
677 talloc_free(pool);
678 talloc_free(c);
680 }
681 }
682 break;
683
684 default:
685 fr_assert(0);
686 }
687 MAP_VERIFY(c_map);
688 map_list_insert_tail(&c->maps, c_map);
689 }
690 talloc_free_children(pool); /* reset pool state */
691 }
692 talloc_free(pool);
693
694skip_maps:
695
696 /*
697 * Check to see if we need to merge the entry into the request
698 */
699 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_merge_new);
700 if (vp && vp->vp_bool) merge = true;
701
702 if (merge) cache_merge(inst, request, c);
703
704 for (;;) {
705 cache_status_t ret;
706
707 ret = inst->driver->insert(out_rctx, &inst->config, inst->driver_submodule->data, request, *handle, c);
708 switch (ret) {
709 case CACHE_RECONNECT:
710 if (cache_reconnect(handle, inst, request) == 0) continue;
712
713 case CACHE_OK:
714 RDEBUG2("Committed entry, TTL %pV seconds", fr_box_time_delta(ttl));
715 cache_free(inst, &c);
717
718 case CACHE_YIELD:
719 return UNLANG_ACTION_YIELD;
720
721 default:
722 talloc_free(c); /* Failed insertion - use talloc_free not the driver free */
724 }
725 }
726}
727
729 rlm_cache_t const *inst, rlm_cache_handle_t *handle, void *rctx)
730{
731 if (!inst->driver->set_ttl) {
732 cache_status_t ret;
734 ret = inst->driver->insert_resume(&c, &inst->config, inst->driver_submodule->data, request, handle, rctx);
735 switch (ret) {
736 case CACHE_OK:
737 RDEBUG2("Updated entry TTL");
739
740 case CACHE_YIELD:
741 return UNLANG_ACTION_YIELD;
742
743 default:
745 }
746 }
747
748 /*
749 * Async ttl driver call not defined yet.
750 */
751 fr_assert(0);
752
754}
755
756static void cache_set_ttl_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
757{
758 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
759 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
760
761 if (inst->driver->set_ttl) return;
762 if (!inst->driver->insert_cancel) return;
763
764 inst->driver->insert_cancel(&inst->config, inst->driver_submodule->data, request, rctx->handle, rctx->rctx);
765}
766
767/** Update the TTL of an entry
768 *
769 * @return
770 * - #RLM_MODULE_OK on success.
771 * - #RLM_MODULE_FAIL on failure.
772 */
773static unlang_action_t cache_set_ttl(unlang_result_t *p_result, void **out_rctx,
774 rlm_cache_t const *inst, request_t *request,
776{
777 /*
778 * Call the driver's insert method to overwrite the old entry
779 */
780 if (!inst->driver->set_ttl) for (;;) {
781 cache_status_t ret;
782
783 ret = inst->driver->insert(out_rctx, &inst->config, inst->driver_submodule->data, request, *handle, c);
784 switch (ret) {
785 case CACHE_RECONNECT:
786 if (cache_reconnect(handle, inst, request) == 0) continue;
788
789 case CACHE_OK:
790 RDEBUG2("Updated entry TTL");
792
793 case CACHE_YIELD:
794 return UNLANG_ACTION_YIELD;
795
796 default:
798 }
799 }
800
801 /*
802 * Or call the set ttl method if the driver can do this more
803 * efficiently.
804 */
805 for (;;) {
806 cache_status_t ret;
807
808 ret = inst->driver->set_ttl(&inst->config, inst->driver_submodule->data, request, *handle, c);
809 switch (ret) {
810 case CACHE_RECONNECT:
811 if (cache_reconnect(handle, inst, request) == 0) continue;
813
814 case CACHE_OK:
815 RDEBUG2("Updated entry TTL");
817
818 default:
820 }
821 }
822}
823
824/** Macro to reduce boilerplate in all the module methods / xlat functions
825 * If multiple values are in the input list, concat them as a string
826 * Then check that a variable length key is longer than zero bytes
827 */
828#define FIXUP_KEY(_fail, _invalid) \
829if ((fr_value_box_list_num_elements(&env->key) > 1) && \
830 (fr_value_box_list_concat_in_place(key, key, &env->key, FR_TYPE_STRING, \
831 FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0)) { \
832 REDEBUG("Failed concatenating values to form the key"); \
833 _fail; \
834} \
835if (fr_type_is_variable_size(key->type) && (key->vb_length == 0)) { \
836 REDEBUG("Zero length key string is invalid"); \
837 _invalid; \
838}
839
841 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
842 rlm_cache_entry_t *entry);
843
845 request_t *request);
846
849{
850 fr_pair_t *vp;
851 fr_dcursor_t cursor;
852
853 cache_free(inst, &c);
854 cache_release(inst, request, &handle);
855
856 /*
857 * Clear control attributes
858 */
859 for (vp = fr_pair_dcursor_init(&cursor, &request->control_pairs);
860 vp;
861 vp = fr_dcursor_next(&cursor)) {
862 again:
863 if (!fr_dict_attr_is_top_level(vp->da)) continue;
864
865 switch (vp->da->attr) {
866 case FR_CACHE_TTL:
867 case FR_CACHE_STATUS_ONLY:
868 case FR_CACHE_ALLOW_MERGE:
869 case FR_CACHE_ALLOW_INSERT:
870 case FR_CACHE_MERGE_NEW:
871 RDEBUG2("Removing control.%s", vp->da->name);
872 vp = fr_dcursor_remove(&cursor);
874 vp = fr_dcursor_current(&cursor);
875 if (!vp) break;
876 goto again;
877 }
878 }
879
881}
882
884 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
886{
887 switch (tmp->rcode) {
888 case RLM_MODULE_FAIL:
889 p_result->rcode = RLM_MODULE_FAIL;
890 goto finish;
891
892 case RLM_MODULE_OK:
893 if (p_result->rcode != RLM_MODULE_UPDATED) p_result->rcode = RLM_MODULE_OK;
894 break;
895
897 p_result->rcode = RLM_MODULE_UPDATED;
898 break;
899
900 default:
901 fr_assert(0);
902 }
903 fr_assert(!inst->driver->acquire || handle);
904finish:
905
906 return mod_cache_it_finish(request, inst, handle, c);
907}
908
910 request_t *request)
911{
912 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
913 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
914 unlang_result_t tmp;
915
916 if (cache_insert_resume(&tmp, request, inst, &rctx->handle, &rctx->ttl, rctx->rctx) == UNLANG_ACTION_YIELD) {
918 ~FR_SIGNAL_CANCEL, rctx);
919 }
920
921 return mod_cache_it_insert_results(p_result, &tmp, request, inst, rctx->handle, rctx->entry);
922}
923
925 rlm_cache_t const *inst, rlm_cache_handle_t *handle, cache_rctx_t *rctx)
926{
927 switch (tmp->rcode) {
928 case RLM_MODULE_FAIL:
929 p_result->rcode = RLM_MODULE_FAIL;
930 break;
931
933 case RLM_MODULE_OK:
934 if (p_result->rcode != RLM_MODULE_UPDATED) p_result->rcode = RLM_MODULE_OK;
935 break;
936
937 default:
938 fr_assert(0);
939 }
940
941 return mod_cache_it_finish(request, inst, handle, rctx->entry);
942}
943
945 request_t *request)
946{
947 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
948 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
949 unlang_result_t tmp;
950
951 if (cache_set_ttl_resume(&tmp, request, inst, &rctx->handle, rctx->rctx) == UNLANG_ACTION_YIELD) {
953 ~FR_SIGNAL_CANCEL, rctx);
954 }
955
956 return mod_cache_it_ttl_results(p_result, &tmp, request, inst, rctx->handle, rctx);
957}
958
960 rlm_cache_handle_t *handle, cache_rctx_t *rctx)
961{
962 cache_it_ctx_t *ctx = talloc_get_type_abort(rctx->uctx, cache_it_ctx_t);
963 cache_call_env_t *env = ctx->env;
964 fr_value_box_t const *key = fr_value_box_list_head(&env->key);
965 void *driver_rctx;
966
967 /*
968 * We can only alter the TTL on an entry if it exists.
969 */
970 if (ctx->set_ttl && (ctx->exists == 1)) {
971 unlang_result_t tmp;
972
973 fr_assert(rctx->entry);
974
975 rctx->entry->expires = fr_unix_time_add(fr_time_to_unix_time(request->packet->timestamp), rctx->ttl);
976
977 if (cache_set_ttl(&tmp, &driver_rctx, inst, request, &handle, rctx->entry) == UNLANG_ACTION_YIELD)
978 {
979 return cache_module_yield(request, handle, key, rctx->entry, &rctx->ttl,
981 }
982 return mod_cache_it_ttl_results(p_result, &tmp, request, inst, handle, rctx);
983 }
984
985 /*
986 * Inserts are upserts, so we don't care about the
987 * entry state, just that we're not meant to be
988 * setting the TTL, which precludes performing an
989 * insert.
990 */
991 if (ctx->insert && (ctx->exists == 0)) {
992 unlang_result_t tmp;
993
994 if (cache_insert(&tmp, &driver_rctx, inst, request, &handle, key,
995 env->maps, rctx->ttl) == UNLANG_ACTION_YIELD) {
996 cache_module_yield(request, handle, key, rctx->entry, &rctx->ttl,
998 return UNLANG_ACTION_YIELD;
999
1000 }
1001 return mod_cache_it_insert_results(p_result, &tmp, request, inst, handle, rctx->entry);
1002 }
1003
1004 return mod_cache_it_finish(request, inst, handle, rctx->entry);
1005}
1006
1010 cache_it_ctx_t *ctx)
1011{
1012 switch (tmp->rcode) {
1013 case RLM_MODULE_FAIL:
1014 p_result->rcode = RLM_MODULE_FAIL;
1015 return -1;
1016
1017 case RLM_MODULE_OK:
1018 ctx->exists = 1;
1019 if (p_result->rcode != RLM_MODULE_UPDATED) p_result->rcode = RLM_MODULE_OK;
1020 break;
1021
1023 ctx->exists = 0;
1024 break;
1025
1026 default:
1027 fr_assert(0);
1028 }
1029 fr_assert(!inst->driver->acquire || handle);
1030
1031 return 0;
1032}
1033
1035 request_t *request)
1036{
1037 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1038 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1039 cache_it_ctx_t *ctx = talloc_get_type_abort(rctx->uctx, cache_it_ctx_t);
1040 unlang_result_t tmp;
1041
1042 if (cache_find_resume(&tmp, &rctx->entry, inst, request, &rctx->handle,
1043 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1045 ~FR_SIGNAL_CANCEL, rctx);
1046 }
1047
1048 if (mod_cache_it_find_results(p_result, &tmp, inst, rctx->handle, ctx) < 0) {
1049 p_result->rcode = RLM_MODULE_FAIL;
1050 return mod_cache_it_finish(request, inst, rctx->handle, rctx->entry);
1051 }
1052
1053 return mod_cache_it_ttl(p_result, request, inst, rctx->handle, rctx);
1054}
1055
1057 request_t *request, rlm_cache_t const *inst,
1058 rlm_cache_handle_t *handle, rlm_cache_entry_t *entry)
1059{
1060 switch (tmp->rcode) {
1061 case RLM_MODULE_FAIL:
1062 p_result->rcode = RLM_MODULE_FAIL;
1063 break;
1064
1065 case RLM_MODULE_OK:
1066 if (p_result->rcode == RLM_MODULE_NOOP) p_result->rcode = RLM_MODULE_OK;
1067 break;
1068
1070 if (p_result->rcode == RLM_MODULE_NOOP) p_result->rcode = RLM_MODULE_NOTFOUND;
1071 break;
1072
1073 default:
1074 fr_assert(0);
1075 break;
1076 }
1077
1078 return mod_cache_it_finish(request, inst, handle, entry);
1079}
1080
1082 request_t *request)
1083{
1084 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1085 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1086 unlang_result_t tmp;
1087
1088 if (cache_expire_resume(&tmp, inst, request, &rctx->handle, rctx->rctx) == UNLANG_ACTION_YIELD) {
1090 ~FR_SIGNAL_CANCEL, rctx);
1091 }
1092
1093 return mod_cache_it_expire_results(p_result, &tmp, request, inst, rctx->handle, rctx->entry);
1094}
1095
1097 rlm_cache_handle_t *handle, cache_rctx_t *rctx)
1098{
1099 cache_it_ctx_t *ctx = talloc_get_type_abort(rctx->uctx, cache_it_ctx_t);
1100 void *driver_rctx;
1101 cache_call_env_t *env = ctx->env;
1102 fr_value_box_t const *key = fr_value_box_list_head(&env->key);
1103
1104 /*
1105 * Expire the entry if told to, and we either don't know whether
1106 * it exists, or we know it does.
1107 *
1108 * We only expire if we're not inserting, as driver insert methods
1109 * should perform upserts.
1110 */
1111 if (ctx->expire && ((ctx->exists == -1) || (ctx->exists == 1))) {
1112 if (!ctx->insert) {
1113 unlang_result_t tmp;
1114
1115 fr_assert(!ctx->set_ttl);
1116 if (cache_expire(&tmp, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1117 return cache_module_yield(request, handle, key, rctx->entry, &fr_time_delta_wrap(0),
1119 }
1120
1121 return mod_cache_it_expire_results(p_result, &tmp, request, inst, handle, rctx->entry);
1122 }
1123 /* Otherwise use insert to overwrite */
1124 ctx->exists = 0;
1125 }
1126
1127 /*
1128 * If we still don't know whether it exists or not
1129 * and we need to do an insert or set_ttl operation
1130 * determine that now.
1131 */
1132 if ((ctx->exists < 0) && (ctx->insert || ctx->set_ttl)) {
1133 unlang_result_t tmp;
1134
1135 if (cache_find(&tmp, &rctx->entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1136 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
1137 mod_cache_it_find_resume, cache_find_cancel, driver_rctx, ctx);
1138 }
1139
1140 if (mod_cache_it_find_results(p_result, &tmp, inst, handle, ctx) < 0) {
1141 return mod_cache_it_finish(request, inst, handle, rctx->entry);
1142 }
1143 }
1144
1145 return mod_cache_it_ttl(p_result, request, inst, handle, rctx);
1146}
1147
1148static inline int mod_cache_it_merge_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst,
1151{
1152 switch (p_result->rcode) {
1153 case RLM_MODULE_FAIL:
1154 return -1;
1155
1156 case RLM_MODULE_OK:
1157 p_result->rcode = cache_merge(inst, request, c);
1158 ctx->exists = 1;
1159 break;
1160
1162 p_result->rcode = RLM_MODULE_NOTFOUND;
1163 ctx->exists = 0;
1164 break;
1165
1166 default:
1167 fr_assert(0);
1168 }
1169 fr_assert(!inst->driver->acquire || handle);
1170 return 0;
1171}
1172
1174 request_t *request)
1175{
1176 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1177 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1178 cache_it_ctx_t *ctx = talloc_get_type_abort(rctx->uctx, cache_it_ctx_t);
1179
1180 if (cache_find_resume(p_result, &rctx->entry, inst, request, &rctx->handle,
1181 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1183 ~FR_SIGNAL_CANCEL, rctx);
1184 }
1185
1186 if (mod_cache_it_merge_results(p_result, request, inst, rctx->handle, ctx, rctx->entry) < 0) {
1187 p_result->rcode = RLM_MODULE_FAIL;
1188 return mod_cache_it_finish(request, inst, rctx->handle, rctx->entry);
1189 }
1190
1191 return mod_cache_it_expire(p_result, request, inst, rctx->handle, rctx);
1192}
1193
1194/** Do caching checks
1195 *
1196 * Since we can update ANY VP list, we do exactly the same thing for all sections
1197 * (autz / auth / etc.)
1198 *
1199 * If you want to cache something different in different sections, configure
1200 * another cache module.
1201 */
1202static unlang_action_t CC_HINT(nonnull) mod_cache_it(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1203{
1204 void *driver_rctx = NULL;
1206 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1207 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1208 rlm_cache_handle_t *handle;
1209 cache_it_ctx_t *ctx;
1210 fr_time_delta_t ttl = inst->config.ttl;
1211 fr_pair_t *vp;
1212 rlm_cache_entry_t *entry = NULL;
1213
1214 p_result->rcode = RLM_MODULE_NOOP;
1215
1217
1218 /*
1219 * If Cache-Status-Only == yes, only return whether we found a
1220 * valid cache entry
1221 */
1222 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_status_only);
1223 if (vp && vp->vp_bool) {
1224 rlm_cache_entry_t *c = NULL;
1225
1226 RINDENT();
1227 RDEBUG3("status-only: yes");
1228 REXDENT();
1229
1230 if (cache_acquire(&handle, inst, request) < 0) {
1232 }
1233
1234 if (cache_find(p_result, &c, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1235 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
1236 mod_method_status_resume, cache_find_cancel, driver_rctx, NULL);
1237 }
1238 return mod_method_status_results(p_result, request, inst, handle, c);
1239 }
1240
1241 MEM(ctx = talloc(unlang_interpret_frame_talloc_ctx(request), cache_it_ctx_t));
1242 *ctx = (cache_it_ctx_t) {
1243 .merge = true,
1244 .insert = true,
1245 .expire = false,
1246 .set_ttl = false,
1247 .exists = -1,
1248 .env = env,
1249 };
1250
1251 /*
1252 * Figure out what operation we're doing
1253 */
1254 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_allow_merge);
1255 if (vp) ctx->merge = vp->vp_bool;
1256
1257 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_allow_insert);
1258 if (vp) ctx->insert = vp->vp_bool;
1259
1260 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
1261 if (vp) {
1262 if (vp->vp_int32 == 0) {
1263 ctx->expire = true;
1264 } else if (vp->vp_int32 < 0) {
1265 ctx->expire = true;
1266 ttl = fr_time_delta_from_sec(-(vp->vp_int32));
1267 /* Updating the TTL */
1268 } else {
1269 ctx->set_ttl = true;
1270 ttl = fr_time_delta_from_sec(vp->vp_int32);
1271 }
1272 }
1273
1274 RINDENT();
1275 RDEBUG3("merge : %s", ctx->merge ? "yes" : "no");
1276 RDEBUG3("insert : %s", ctx->insert ? "yes" : "no");
1277 RDEBUG3("expire : %s", ctx->expire ? "yes" : "no");
1278 RDEBUG3("ttl : %pV", fr_box_time_delta(ttl));
1279 REXDENT();
1280 if (cache_acquire(&handle, inst, request) < 0) {
1282 }
1283
1284 /*
1285 * Retrieve the cache entry and merge it with the current request
1286 * recording whether the entry existed.
1287 */
1288 if (ctx->merge) {
1289 if (cache_find(p_result, &entry, &driver_rctx,inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1290 return cache_module_yield(request, handle, key, NULL, &ttl,
1291 mod_cache_it_merge_resume, cache_find_cancel, driver_rctx, ctx);
1292 }
1293 if (mod_cache_it_merge_results(p_result, request, inst, handle, ctx, entry) < 0) {
1294 return mod_cache_it_finish(request, inst, handle, entry);
1295 }
1296 }
1297
1298 return mod_cache_it_expire(p_result, request, inst, handle,
1299 &(cache_rctx_t){.uctx = ctx, .ttl = ttl, .entry = entry});
1300}
1301
1303 { .required = true, .single = true, .type = FR_TYPE_STRING },
1305};
1306
1307static xlat_action_t cache_xlat_results(TALLOC_CTX *ctx, unlang_result_t *result, request_t *request,
1309 tmpl_t *target, fr_dcursor_t *out)
1310{
1311 fr_value_box_t *vb;
1312 map_t *map = NULL;
1313
1314 switch (result->rcode) {
1315 case RLM_MODULE_OK: /* found */
1316 break;
1317
1318 case RLM_MODULE_NOTFOUND: /* !found is "no data" */
1319 talloc_free(target);
1320 cache_release(inst, request, &handle);
1321 return XLAT_ACTION_DONE;
1322
1323 default:
1324 talloc_free(target);
1325 cache_release(inst, request, &handle);
1326 return XLAT_ACTION_FAIL;
1327 }
1328
1329 while ((map = map_list_next(&c->maps, map))) {
1330 if ((tmpl_attr_tail_da(map->lhs) != tmpl_attr_tail_da(target)) ||
1331 (tmpl_list(map->lhs) != tmpl_list(target))) continue;
1332
1333 MEM(vb = fr_value_box_alloc_null(ctx));
1334 if (unlikely(fr_value_box_copy(vb, vb, tmpl_value(map->rhs)) < 0)) {
1335 RPEDEBUG("Failed copying value from cache entry");
1336 talloc_free(vb);
1337 talloc_free(target);
1338 cache_free(inst, &c);
1339 cache_release(inst, request, &handle);
1340 return XLAT_ACTION_FAIL;
1341 }
1343 break;
1344 }
1345
1346 talloc_free(target);
1347
1348 cache_free(inst, &c);
1349 cache_release(inst, request, &handle);
1350
1351 /*
1352 * If we found a value, then the output has been updated.
1353 * Otherwise, there is no output. Either way, the xlat succeeded.
1354 */
1355
1356 return XLAT_ACTION_DONE;
1357}
1358
1359static xlat_action_t cache_xlat_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
1360 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1361{
1362 unlang_result_t result;
1363 rlm_cache_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_cache_t);
1364 cache_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, cache_rctx_t);
1365 tmpl_t *target = talloc_get_type_abort(rctx->uctx, tmpl_t);
1366 rlm_cache_entry_t *entry = NULL;
1367
1368 if (cache_find_resume(&result, &entry, inst, request, &rctx->handle,
1369 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1370 unlang_xlat_yield(request, cache_xlat_resume, NULL, 0, rctx);
1371 return XLAT_ACTION_YIELD;
1372 }
1373
1374 return cache_xlat_results(ctx, &result, request, inst, rctx->handle, entry, target, out);
1375}
1376
1377/** Allow single attribute values to be retrieved from the cache
1378 *
1379 * @ingroup xlat_functions
1380 */
1381static CC_HINT(nonnull)
1383 xlat_ctx_t const *xctx,
1384 request_t *request, fr_value_box_list_t *in)
1385{
1386 rlm_cache_entry_t *c = NULL;
1387 void *driver_rctx = NULL;
1388 rlm_cache_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_cache_t);
1389 cache_call_env_t *env = talloc_get_type_abort(xctx->env_data, cache_call_env_t);
1390 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1391 rlm_cache_handle_t *handle = NULL;
1392
1393 ssize_t slen;
1394
1395 fr_value_box_t *attr = fr_value_box_list_head(in);
1396
1397 tmpl_t *target = NULL;
1398 unlang_result_t result = { .rcode = RLM_MODULE_NOOP };
1399
1401
1402 slen = tmpl_afrom_attr_substr(ctx, NULL, &target,
1403 &FR_SBUFF_IN(attr->vb_strvalue, attr->vb_length),
1404 NULL,
1405 &(tmpl_rules_t){
1406 .attr = {
1407 .dict_def = request->local_dict,
1408 .list_def = request_attr_request,
1409 }
1410 });
1411 if (slen <= 0) {
1412 RPEDEBUG("Invalid key");
1413 return XLAT_ACTION_FAIL;
1414 }
1415
1416 if (cache_acquire(&handle, inst, request) < 0) {
1417 talloc_free(target);
1418 return XLAT_ACTION_FAIL;
1419 }
1420
1421 if (cache_find(&result, &c, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1422 cache_rctx_t *rctx;
1423
1424 MEM(rctx = talloc(unlang_interpret_frame_talloc_ctx(request), cache_rctx_t));
1425 *rctx = (cache_rctx_t) {
1426 .handle = handle,
1427 .key = key,
1428 .rctx = driver_rctx,
1429 .uctx = target
1430 };
1431
1432 unlang_xlat_yield(request, cache_xlat_resume, NULL, 0, rctx);
1433 return XLAT_ACTION_YIELD;
1434 }
1435 return cache_xlat_results(ctx, &result, request, inst, handle, c, target, out);
1436}
1437
1438static xlat_action_t cache_ttl_get_xlat_results(TALLOC_CTX *ctx, unlang_result_t *result, request_t *request,
1439 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
1441{
1442 fr_value_box_t *vb;
1443
1444 switch (result->rcode) {
1445 case RLM_MODULE_OK: /* found */
1446 break;
1447
1448 default:
1449 cache_release(inst, request, &handle);
1450 return XLAT_ACTION_DONE;
1451 }
1452
1453 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
1454 vb->vb_time_delta = fr_unix_time_sub(c->expires, fr_time_to_unix_time(request->packet->timestamp));
1456
1457 cache_free(inst, &c);
1458 cache_release(inst, request, &handle);
1459
1460 return XLAT_ACTION_DONE;
1461}
1462
1464 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1465{
1466 unlang_result_t result;
1467 rlm_cache_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_cache_t);
1468 cache_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, cache_rctx_t);
1469 rlm_cache_entry_t *entry = NULL;
1470
1471 if (cache_find_resume(&result, &entry, inst, request, &rctx->handle,
1472 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1473 unlang_xlat_yield(request, cache_xlat_resume, NULL, 0, rctx);
1474 return XLAT_ACTION_YIELD;
1475 }
1476
1477 return cache_ttl_get_xlat_results(ctx, &result, request, inst, rctx->handle, entry, out);
1478}
1479
1481 xlat_ctx_t const *xctx,
1482 request_t *request, UNUSED fr_value_box_list_t *in)
1483{
1484 rlm_cache_entry_t *c = NULL;
1485 void *driver_rctx = NULL;
1486 rlm_cache_t *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_cache_t);
1487 cache_call_env_t *env = talloc_get_type_abort(xctx->env_data, cache_call_env_t);
1488 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1489 rlm_cache_handle_t *handle = NULL;
1490
1491 unlang_result_t result = { .rcode = RLM_MODULE_NOOP };
1492
1494
1495 if (cache_acquire(&handle, inst, request) < 0) {
1496 return XLAT_ACTION_FAIL;
1497 }
1498
1499 if (cache_find(&result, &c, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1500 cache_rctx_t *rctx;
1501
1502 MEM(rctx = talloc(unlang_interpret_frame_talloc_ctx(request), cache_rctx_t));
1503 *rctx = (cache_rctx_t) {
1504 .handle = handle,
1505 .key = key,
1506 .rctx = driver_rctx,
1507 };
1508
1509 unlang_xlat_yield(request, cache_ttl_get_xlat_resume, NULL, 0, rctx);
1510 return XLAT_ACTION_YIELD;
1511 }
1512 return cache_ttl_get_xlat_results(ctx, &result, request, inst, handle, c, out);
1513}
1514
1515/** Release the allocated resources and cleanup the avps
1516 */
1517static void cache_unref(request_t *request, rlm_cache_t const *inst, rlm_cache_entry_t *entry,
1518 rlm_cache_handle_t *handle)
1519{
1520 fr_dcursor_t cursor;
1521 fr_pair_t *vp;
1522
1523 /*
1524 * Release the driver calls
1525 */
1526 cache_free(inst, &entry);
1527 cache_release(inst, request, &handle);
1528
1529 /*
1530 * Clear control attributes
1531 */
1532 for (vp = fr_pair_dcursor_init(&cursor, &request->control_pairs);
1533 vp;
1534 vp = fr_dcursor_next(&cursor)) {
1535 again:
1536 if (!fr_dict_attr_is_top_level(vp->da)) continue;
1537
1538 switch (vp->da->attr) {
1539 case FR_CACHE_TTL:
1540 case FR_CACHE_STATUS_ONLY:
1541 case FR_CACHE_ALLOW_MERGE:
1542 case FR_CACHE_ALLOW_INSERT:
1543 case FR_CACHE_MERGE_NEW:
1544 RDEBUG2("Removing control:%s", vp->da->name);
1545 vp = fr_dcursor_remove(&cursor);
1546 TALLOC_FREE(vp);
1547 vp = fr_dcursor_current(&cursor);
1548 if (!vp) break;
1549 goto again;
1550 }
1551 }
1552}
1553
1554/** Common result handling for status method
1555 */
1557 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
1558 rlm_cache_entry_t *entry)
1559{
1560 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
1561
1562 p_result->rcode = (entry) ? RLM_MODULE_OK : RLM_MODULE_NOTFOUND;
1563
1564finish:
1565 cache_unref(request, inst, entry, handle);
1566
1568}
1569
1571 request_t *request)
1572{
1573 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1574 rlm_cache_entry_t *entry = NULL;
1575 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1576
1577 if (cache_find_resume(p_result, &entry, inst, request, &rctx->handle,
1578 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1580 ~FR_SIGNAL_CANCEL, rctx);
1581 }
1582
1583 return mod_method_status_results(p_result, request, inst, rctx->handle, entry);
1584}
1585
1586/** Get the status by ${key} (without load)
1587 *
1588 * @return
1589 * - #RLM_MODULE_OK on success.
1590 * - #RLM_MODULE_NOTFOUND on cache miss.
1591 * - #RLM_MODULE_FAIL on failure.
1592 */
1593static unlang_action_t CC_HINT(nonnull) mod_method_status(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1594{
1595 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1596 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1597 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1598 rlm_cache_entry_t *entry = NULL;
1599 rlm_cache_handle_t *handle = NULL;
1600 void *driver_rctx = NULL;
1601
1602 p_result->rcode = RLM_MODULE_NOOP;
1603
1605
1606 /* Good to go? */
1607 if (cache_acquire(&handle, inst, request) < 0) {
1609 }
1610
1611 fr_assert(!inst->driver->acquire || handle);
1612
1613 if (cache_find(p_result, &entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1614 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
1615 mod_method_status_resume, cache_find_cancel, driver_rctx, NULL);
1616 }
1617 return mod_method_status_results(p_result, request, inst, handle, entry);
1618}
1619
1620/** Common result handling for load method
1621 */
1623 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
1624 rlm_cache_entry_t *entry)
1625{
1626 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
1627
1628 if (!entry) {
1629 RDEBUG2("Entry not found to load");
1630 p_result->rcode = RLM_MODULE_NOTFOUND;
1631 goto finish;
1632 }
1633
1634 p_result->rcode = cache_merge(inst, request, entry);
1635
1636finish:
1637 cache_unref(request, inst, entry, handle);
1639}
1640
1642 request_t *request)
1643{
1644 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1645 rlm_cache_entry_t *entry = NULL;
1646 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1647
1648 if (cache_find_resume(p_result, &entry, inst, request, &rctx->handle,
1649 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1651 }
1652
1653 return mod_method_load_results(p_result, request, inst, rctx->handle, entry);
1654}
1655
1656/** Load the avps by ${key}.
1657 *
1658 * @return
1659 * - #RLM_MODULE_UPDATED on success.
1660 * - #RLM_MODULE_NOTFOUND on cache miss.
1661 * - #RLM_MODULE_FAIL on failure.
1662 */
1663static unlang_action_t CC_HINT(nonnull) mod_method_load(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1664{
1665 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1666 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1667 rlm_cache_entry_t *entry = NULL;
1668 rlm_cache_handle_t *handle = NULL;
1669 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1670 void *driver_rctx;
1671
1672 p_result->rcode = RLM_MODULE_NOOP;
1673
1675
1676 /* Good to go? */
1677 if (cache_acquire(&handle, inst, request) < 0) {
1679 }
1680
1681 if (cache_find(p_result, &entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1682 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
1683 mod_method_load_resume, cache_find_cancel, driver_rctx, NULL);
1684 }
1685 return mod_method_load_results(p_result, request, inst, handle, entry);
1686}
1687
1688/** Common result handling for any module method which returns `updated` for success
1689 */
1691 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
1692 rlm_cache_entry_t *entry)
1693{
1694 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
1695 p_result->rcode = RLM_MODULE_UPDATED;
1696
1697finish:
1698 cache_unref(request, inst, entry, handle);
1700}
1701
1703 request_t *request)
1704{
1705 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1706 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1707
1708 if (cache_insert_resume(p_result, request, inst, &rctx->handle, &rctx->ttl, rctx->rctx) == UNLANG_ACTION_YIELD) {
1710 ~FR_SIGNAL_CANCEL, rctx);
1711 }
1712
1713 return mod_method_common_results(p_result, request, inst, rctx->handle, rctx->entry);
1714}
1715
1717 request_t *request)
1718{
1719 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1720 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1721 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1722 void *driver_rctx;
1723
1724 if (cache_expire_resume(p_result, inst, request, &rctx->handle, rctx->rctx) == UNLANG_ACTION_YIELD) {
1726 ~FR_SIGNAL_CANCEL, rctx);
1727 }
1728
1729 if (p_result->rcode == RLM_MODULE_FAIL) {
1730 cache_unref(request, inst, rctx->handle, &rctx->handle);
1732 }
1733
1734 /*
1735 * Insert a new entry
1736 */
1737 if (cache_insert(p_result, &driver_rctx, inst, request, &rctx->handle, rctx->key,
1738 env->maps, rctx->ttl) == UNLANG_ACTION_YIELD) {
1739 return cache_module_yield(request, rctx->handle, rctx->key, rctx->entry, &rctx->ttl,
1740 mod_method_common_resume, cache_insert_cancel, driver_rctx, NULL);
1741 }
1742 return mod_method_common_results(p_result, request, inst, rctx->handle, rctx->entry);
1743}
1744
1745/** Common result handdling after update method does a find
1746 */
1748 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
1750{
1751 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1752 fr_time_delta_t ttl;
1753 bool expire = false;
1754 fr_pair_t *vp;
1755 void *driver_rctx;
1756
1757 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
1758
1759 /* Process the TTL */
1760 ttl = inst->config.ttl; /* Set the default value from cache { ttl=... } */
1761 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
1762 if (vp) {
1763 if (vp->vp_int32 == 0) {
1764 /*
1765 * Expire old one, and insert new one with default TTL.
1766 */
1767 expire = true;
1768
1769 } else if (vp->vp_int32 < 0) {
1770 /*
1771 * Expire old one, and insert new one with this TTL.
1772 */
1773 ttl = fr_time_delta_from_sec(-(vp->vp_int32));
1774 /* Updating the TTL */
1775 expire = true;
1776
1777 } else {
1778 /*
1779 * Write entry, and insert new one.
1780 */
1781 ttl = fr_time_delta_from_sec(vp->vp_int32);
1782 }
1783
1784 RDEBUG3("Using TTL %pV (%d)", fr_box_time_delta(ttl), vp->vp_int32);
1785 }
1786
1787 /*
1788 * Expire the entry if it exists.
1789 *
1790 * Then, we always insert a new entry.
1791 */
1792 if (expire && (p_result->rcode == RLM_MODULE_OK)) {
1793 RDEBUG3("Expiring cache entry");
1794
1795 if (cache_expire(p_result, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1796 return cache_module_yield(request, handle, key, entry, &ttl,
1798 }
1799 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
1800 goto insert_new;
1801 }
1802
1803 /*
1804 * If it was found, update its TTL.
1805 */
1806 if (p_result->rcode == RLM_MODULE_OK) {
1807 fr_assert(entry != NULL);
1808
1809 RDEBUG3("Updating the TTL -> %pV", fr_box_time_delta(ttl));
1810
1811 entry->expires = fr_unix_time_add(fr_time_to_unix_time(request->packet->timestamp), ttl);
1812
1813 if (cache_set_ttl(p_result, &driver_rctx, inst, request, &handle, entry) == UNLANG_ACTION_YIELD) {
1814 return cache_module_yield(request, handle, key, NULL, &ttl,
1816 }
1817 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
1818 } else {
1819 insert_new:
1820 /*
1821 * Insert a new entry.
1822 */
1823 if (cache_insert(p_result, &driver_rctx, inst, request, &handle, key, env->maps, ttl) == UNLANG_ACTION_YIELD) {
1824 return cache_module_yield(request, handle, key, entry, &ttl,
1825 mod_method_common_resume, cache_insert_cancel, driver_rctx, NULL);
1826 }
1827 return mod_method_common_results(p_result, request, inst, handle, entry);
1828 }
1829
1830 /*
1831 * We did something, so the result is "updated".
1832 */
1833 p_result->rcode = RLM_MODULE_UPDATED;
1834
1835finish:
1836 cache_unref(request, inst, entry, handle);
1837
1839}
1840
1842 module_ctx_t const *mctx, request_t *request)
1843{
1844 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1845 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1846 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1847 rlm_cache_entry_t *entry = NULL;
1848
1849 if (cache_find_resume(p_result, &entry, inst, request, &rctx->handle,
1850 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1852 ~FR_SIGNAL_CANCEL, rctx);
1853 }
1854
1855 return mod_method_update_find_results(p_result, request, inst, rctx->handle, env, entry);
1856}
1857
1858/** Create, or update a cache entry
1859 *
1860 * @return
1861 * - #RLM_MODULE_OK on success.
1862 * - #RLM_MODULE_UPDATED if we merged the cache entry.
1863 * - #RLM_MODULE_FAIL on failure.
1864 */
1865static unlang_action_t CC_HINT(nonnull) mod_method_update(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1866{
1867 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1868 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1869 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1870 rlm_cache_entry_t *entry = NULL;
1871 rlm_cache_handle_t *handle = NULL;
1872 void *driver_rctx = NULL;
1873
1874 p_result->rcode = RLM_MODULE_NOOP;
1875
1877
1878 /* Good to go? */
1879 if (cache_acquire(&handle, inst, request) < 0) {
1881 }
1882
1883 /*
1884 * We can only alter the TTL on an entry if it exists.
1885 */
1886 if (cache_find(p_result, &entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1887 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
1889 }
1890
1891 return mod_method_update_find_results(p_result, request, inst, handle, env, entry);
1892}
1893
1894/** Common result handdling after store method does a find
1895 */
1897 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
1899{
1900 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1901 fr_time_delta_t ttl;
1902 fr_pair_t *vp;
1903 void *driver_rctx;
1904
1905 switch (p_result->rcode) {
1906 default:
1907 case RLM_MODULE_OK:
1908 p_result->rcode = RLM_MODULE_NOOP;
1910
1911 case RLM_MODULE_FAIL:
1912 cache_unref(request, inst, entry, handle);
1914
1916 break;
1917 }
1918
1919 /* Process the TTL */
1920 ttl = inst->config.ttl; /* Set the default value from cache { ttl=... } */
1921 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
1922 if (vp && (vp->vp_int32 > 0)) {
1923 ttl = fr_time_delta_from_sec(vp->vp_int32);
1924
1925 RDEBUG3("Overriding default TTL %pV -> %d", fr_box_time_delta(ttl), vp->vp_int32);
1926 }
1927
1928 /*
1929 * Inserts are upserts, so we don't care about the
1930 * entry state, just that we're not meant to be
1931 * setting the TTL, which precludes performing an
1932 * insert.
1933 */
1934 if (cache_insert(p_result, &driver_rctx, inst, request, &handle, key, env->maps, ttl) == UNLANG_ACTION_YIELD) {
1935 return cache_module_yield(request, handle, key, entry, &ttl, mod_method_common_resume,
1936 cache_insert_cancel, driver_rctx, NULL);
1937 }
1938
1939 return mod_method_common_results(p_result, request, inst, handle, entry);
1940}
1941
1943 module_ctx_t const *mctx, request_t *request)
1944{
1945 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1946 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1947 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1948 rlm_cache_entry_t *entry = NULL;
1949
1950 if (cache_find_resume(p_result, &entry, inst, request, &rctx->handle,
1951 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
1953 ~FR_SIGNAL_CANCEL, rctx);
1954 }
1955
1956 return mod_method_store_find_results(p_result, request, inst, rctx->handle, env, entry);
1957}
1958
1959/** Create a cache entry if it does not already exist.
1960 *
1961 * @return
1962 * - #RLM_MODULE_NOOP if an entry already existed.
1963 * - #RLM_MODULE_UPDATED if we inserted a cache entry.
1964 * - #RLM_MODULE_FAIL on failure.
1965 */
1966static unlang_action_t CC_HINT(nonnull) mod_method_store(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1967{
1968 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1969 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
1970 fr_value_box_t *key = fr_value_box_list_head(&env->key);
1971 rlm_cache_entry_t *entry = NULL;
1972 rlm_cache_handle_t *handle = NULL;
1973 void *driver_rctx = NULL;
1974
1975 p_result->rcode = RLM_MODULE_NOOP;
1976
1978
1979 if (cache_acquire(&handle, inst, request) < 0) {
1981 }
1982
1983 /*
1984 * We only insert an entry if it doesn't already exist.
1985 */
1986 if (cache_find(p_result, &entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
1987 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
1989 }
1990
1991 return mod_method_store_find_results(p_result, request, inst, handle, env, entry);
1992}
1993
1995 request_t *request)
1996{
1997 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
1998 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
1999
2000 if (cache_expire_resume(p_result, inst, request, &rctx->handle, rctx->rctx) == UNLANG_ACTION_YIELD) {
2002 ~FR_SIGNAL_CANCEL, rctx);
2003 }
2004
2005 cache_unref(request, inst, rctx->entry, rctx->handle);
2007}
2008
2009/** Common result handdling after clear method does a find
2010 */
2012 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
2013 fr_value_box_t const *key, rlm_cache_entry_t *entry)
2014{
2015 void *driver_rctx;
2016
2017 if (p_result->rcode == RLM_MODULE_FAIL) goto finish;
2018
2019 if (!entry) {
2020 REDEBUG2("Entry not found to delete");
2021 p_result->rcode = RLM_MODULE_NOTFOUND;
2022 goto finish;
2023 }
2024
2025 if (cache_expire(p_result, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
2026 return cache_module_yield(request, handle, key, entry, &fr_time_delta_wrap(0),
2028 }
2029
2030finish:
2031 cache_unref(request, inst, entry, handle);
2032
2034}
2035
2037 module_ctx_t const *mctx, request_t *request)
2038{
2039 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2040 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
2041 rlm_cache_entry_t *entry = NULL;
2042
2043 if (cache_find_resume(p_result, &entry, inst, request, &rctx->handle,
2044 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
2046 ~FR_SIGNAL_CANCEL, rctx);
2047 }
2048
2049 return mod_method_clear_find_results(p_result, request, inst, rctx->handle, rctx->key, entry);
2050}
2051
2052/** Delete the entries by ${key}
2053 *
2054 * @return
2055 * - #RLM_MODULE_OK on success.
2056 * - #RLM_MODULE_NOTFOUND on cache miss.
2057 * - #RLM_MODULE_FAIL on failure.
2058 */
2059static unlang_action_t CC_HINT(nonnull) mod_method_clear(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
2060{
2061 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2062 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
2063 fr_value_box_t *key = fr_value_box_list_head(&env->key);
2064 rlm_cache_entry_t *entry = NULL;
2065 rlm_cache_handle_t *handle = NULL;
2066 void *driver_rctx = NULL;
2067
2068 p_result->rcode = RLM_MODULE_NOOP;
2069
2070 DEBUG3("Calling %s.clear", mctx->mi->name);
2071
2073
2074 /* Good to go? */
2075 if (cache_acquire(&handle, inst, request) < 0) {
2077 }
2078
2079 if (cache_find(p_result, &entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
2080 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
2082 }
2083
2084 return mod_method_clear_find_results(p_result, request, inst, handle, key, entry);
2085}
2086
2088 request_t *request)
2089{
2090 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2091 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
2092
2093 if (cache_set_ttl_resume(p_result, request, inst, rctx->handle, rctx->rctx) == UNLANG_ACTION_YIELD) {
2095 ~FR_SIGNAL_CANCEL, rctx);
2096 }
2097
2098 return mod_method_common_results(p_result, request, inst, rctx->handle, rctx->entry);
2099}
2100
2101/** Common result handling after ttl method does a find
2102 */
2104 rlm_cache_t const *inst, rlm_cache_handle_t *handle,
2105 rlm_cache_entry_t *entry)
2106{
2107 fr_time_delta_t ttl;
2108 fr_pair_t *vp;
2109 void *driver_rctx;
2110
2111 if (p_result->rcode != RLM_MODULE_OK) {
2112 cache_unref(request, inst, entry, handle);
2114 }
2115
2116 /* Process the TTL */
2117 ttl = inst->config.ttl; /* Set the default value from cache { ttl=... } */
2118 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
2119 if (vp) {
2120 if (vp->vp_int32 < 0) {
2121 ttl = fr_time_delta_from_sec(-(vp->vp_int32));
2122 /* Updating the TTL */
2123 } else {
2124 ttl = fr_time_delta_from_sec(vp->vp_int32);
2125 }
2126
2127 RDEBUG3("Overwriting the default TTL %pV -> %d", fr_box_time_delta(inst->config.ttl), vp->vp_int32);
2128 }
2129
2130 fr_assert(entry != NULL);
2131
2132 RDEBUG3("Updating the TTL -> %pV", fr_box_time_delta(ttl));
2133
2134 entry->expires = fr_unix_time_add(fr_time_to_unix_time(request->packet->timestamp), ttl);
2135
2136 if (cache_set_ttl(p_result, &driver_rctx, inst, request, &handle, entry) == UNLANG_ACTION_YIELD) {
2137 return cache_module_yield(request, handle, NULL, entry, &ttl, mod_method_ttl_resume,
2138 cache_set_ttl_cancel, driver_rctx, NULL);
2139 }
2140
2141 return mod_method_common_results(p_result, request, inst, handle, entry);
2142}
2143
2145 module_ctx_t const *mctx, request_t *request)
2146{
2147 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2148 cache_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, cache_rctx_t);
2149 rlm_cache_entry_t *entry = NULL;
2150
2151 if (cache_find_resume(p_result, &entry, inst, request, &rctx->handle,
2152 rctx->key, rctx->rctx) == UNLANG_ACTION_YIELD) {
2154 ~FR_SIGNAL_CANCEL, rctx);
2155 }
2156
2157 return mod_method_ttl_find_results(p_result, request, inst, rctx->handle, entry);
2158}
2159
2160/** Change the TTL on an existing entry.
2161 *
2162 * @return
2163 * - #RLM_MODULE_UPDATED on success.
2164 * - #RLM_MODULE_NOTFOUND on cache miss.
2165 * - #RLM_MODULE_FAIL on failure.
2166 */
2167static unlang_action_t CC_HINT(nonnull) mod_method_ttl(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
2168{
2169 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2170 cache_call_env_t *env = talloc_get_type_abort(mctx->env_data, cache_call_env_t);
2171 fr_value_box_t *key = fr_value_box_list_head(&env->key);
2172 rlm_cache_entry_t *entry = NULL;
2173 rlm_cache_handle_t *handle = NULL;
2174 fr_pair_t *vp;
2175 void *driver_rctx = NULL;
2176
2177 p_result->rcode = RLM_MODULE_NOOP;
2178
2179 DEBUG3("Calling %s.ttl", mctx->mi->name);
2180
2182
2183 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_cache_ttl);
2184 if (vp && (vp->vp_int32 == 0)) RETURN_UNLANG_NOOP;
2185
2186 /* Good to go? */
2187 if (cache_acquire(&handle, inst, request) < 0) {
2189 }
2190
2191 /*
2192 * We can only alter the TTL on an entry if it exists.
2193 */
2194 if (cache_find(p_result, &entry, &driver_rctx, inst, request, &handle, key) == UNLANG_ACTION_YIELD) {
2195 return cache_module_yield(request, handle, key, NULL, &fr_time_delta_wrap(0),
2196 mod_method_ttl_find_resume, cache_find_cancel, driver_rctx, NULL);
2197 }
2198
2199 return mod_method_ttl_find_results(p_result, request, inst, handle, entry);
2200}
2201
2202/** Free any memory allocated under the instance
2203 *
2204 */
2205static int mod_detach(module_detach_ctx_t const *mctx)
2206{
2207 rlm_cache_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2208
2209 /*
2210 * We need to explicitly free all children, so if the driver
2211 * parented any memory off the instance, their destructors
2212 * run before we unload the bytecode for them.
2213 *
2214 * If we don't do this, we get a SEGV deep inside the talloc code
2215 * when it tries to call a destructor that no longer exists.
2216 */
2217 talloc_free_children(inst);
2218
2219 return 0;
2220}
2221
2222/** Verify that a map in the cache section makes sense
2223 *
2224 */
2225static int cache_verify(map_t *map, void *uctx)
2226{
2227 if (unlang_fixup_update(map, uctx) < 0) return -1;
2228
2229 if (!tmpl_is_attr(map->lhs)) {
2230 cf_log_err(map->ci, "Destination must be an attribute ref or a list");
2231 return -1;
2232 }
2233
2234 if (!fr_assignment_op[map->op]) {
2235 cf_log_err(map->ci, "Invalid operator '%s'", fr_tokens[map->op]);
2236 return -1;
2237 }
2238
2239 return 0;
2240}
2241
2242static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
2243 CONF_ITEM *ci,
2244 UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
2245{
2246 CONF_SECTION *update = cf_item_to_section(ci);
2247 call_env_parsed_t *parsed;
2248 map_list_t *maps;
2249
2250 MEM(parsed = call_env_parsed_add(ctx, out,
2252
2253 MEM(maps = talloc(parsed, map_list_t));
2254 map_list_init(maps);
2255
2256 if (map_afrom_cs(maps, maps, update,
2257 t_rules, t_rules, cache_verify, NULL, MAX_ATTRMAP) < 0) {
2258 error:
2259 call_env_parsed_free(out, parsed);
2260 return -1;
2261 }
2262
2263 if (map_list_empty(maps)) {
2264 cf_log_err(update, "Update section must not be empty");
2265 goto error;
2266 }
2267
2268 call_env_parsed_set_data(parsed, maps);
2269
2270 return 0;
2271}
2272
2273/** Create a new rlm_cache_instance
2274 *
2275 */
2276static int mod_instantiate(module_inst_ctx_t const *mctx)
2277{
2278 rlm_cache_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_t);
2279 CONF_SECTION *conf = mctx->mi->conf;
2280
2281 /*
2282 * Non optional fields and callbacks
2283 */
2284 fr_assert(inst->driver->common.name);
2285 fr_assert(inst->driver->find);
2286 fr_assert(inst->driver->insert);
2287 fr_assert(inst->driver->expire);
2288
2289 if (!fr_time_delta_ispos(inst->config.ttl)) {
2290 cf_log_err(conf, "Must set 'ttl' to non-zero");
2291 return -1;
2292 }
2293
2294 if (inst->config.epoch != 0) {
2295 cf_log_err(conf, "Must not set 'epoch' in the configuration files");
2296 return -1;
2297 }
2298
2299 return 0;
2300}
2301
2302/** Register module xlats
2303 *
2304 */
2305static int mod_bootstrap(module_inst_ctx_t const *mctx)
2306{
2307 xlat_t *xlat;
2308
2309 /*
2310 * Register the cache xlat function
2311 */
2312 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, cache_xlat, FR_TYPE_VOID);
2315
2316 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "ttl.get", cache_ttl_get_xlat, FR_TYPE_VOID);
2318
2319 return 0;
2320}
2321
2322/*
2323 * The module name should be the only globally exported symbol.
2324 * That is, everything else should be 'static'.
2325 *
2326 * If the module needs to temporarily modify it's instantiation
2327 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
2328 * The server will then take care of ensuring that the module
2329 * is single-threaded.
2330 */
2332 .common = {
2333 .magic = MODULE_MAGIC_INIT,
2334 .name = "cache",
2335 .inst_size = sizeof(rlm_cache_t),
2337 .bootstrap = mod_bootstrap,
2338 .instantiate = mod_instantiate,
2339 .detach = mod_detach
2340 },
2341 .method_group = {
2342 .bindings = (module_method_binding_t[]){
2343 { .section = SECTION_NAME("clear", CF_IDENT_ANY), .method = mod_method_clear, .method_env = &cache_method_env },
2344 { .section = SECTION_NAME("load", CF_IDENT_ANY), .method = mod_method_load, .method_env = &cache_method_env },
2345 { .section = SECTION_NAME("status", CF_IDENT_ANY), .method = mod_method_status, .method_env = &cache_method_env },
2346 { .section = SECTION_NAME("store", CF_IDENT_ANY), .method = mod_method_store, .method_env = &cache_method_env },
2347 { .section = SECTION_NAME("ttl", CF_IDENT_ANY), .method = mod_method_ttl, .method_env = &cache_method_env },
2348 { .section = SECTION_NAME("update", CF_IDENT_ANY), .method = mod_method_update, .method_env = &cache_method_env },
2349 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_cache_it, .method_env = &cache_method_env },
2351 }
2352 }
2353};
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
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:41
static int const char char buffer[256]
Definition acutest.h:576
#define RCSID(id)
Definition build.h:560
#define NDEBUG_UNUSED
Definition build.h:395
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
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:776
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:689
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:746
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:412
#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:669
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:623
#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:606
Common header for all CONF_* types.
Definition cf_priv.h:54
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:695
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:345
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:352
#define CF_IDENT_ANY
Definition cf_util.h:80
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:38
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
Definition dwarf.c:424
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:1382
talloc_free(hp)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2053
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:140
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define DEBUG3(_fmt,...)
Definition log.h:271
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RERROR(fmt,...)
Definition log.h:315
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define REDEBUG2(fmt,...)
Definition log.h:389
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
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:1534
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION const *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:1814
ssize_t map_print(fr_sbuff_t *out, map_t const *map)
Print a map to a string.
Definition map.c:2317
void map_debug_log(request_t *request, map_t const *map, fr_pair_t const *vp)
Definition map.c:2373
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
long int ssize_t
static bool is_printable(void const *value, size_t len)
Check whether the string is made up of printable UTF8 chars.
Definition misc.h:107
int unlang_fixup_update(map_t *map, void *ctx)
Validate and fixup a map that's part of an update section.
Definition compile.c:346
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
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
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:234
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:957
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:162
#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
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
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:201
fr_value_box_list_t key
To lookup the cache entry with.
Definition rlm_cache.c:58
static int mod_cache_it_merge_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, NDEBUG_UNUSED rlm_cache_handle_t *handle, cache_it_ctx_t *ctx, rlm_cache_entry_t *c)
Definition rlm_cache.c:1148
static unlang_action_t mod_method_clear_find_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, fr_value_box_t const *key, rlm_cache_entry_t *entry)
Common result handdling after clear method does a find.
Definition rlm_cache.c:2011
static unlang_action_t mod_method_update_find_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, cache_call_env_t *env, rlm_cache_entry_t *entry)
Common result handdling after update method does a find.
Definition rlm_cache.c:1747
static int mod_cache_it_find_results(unlang_result_t *p_result, unlang_result_t *tmp, NDEBUG_UNUSED rlm_cache_t const *inst, NDEBUG_UNUSED rlm_cache_handle_t *handle, cache_it_ctx_t *ctx)
Definition rlm_cache.c:1007
static int mod_detach(module_detach_ctx_t const *mctx)
Free any memory allocated under the instance.
Definition rlm_cache.c:2205
static xlat_action_t cache_xlat_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_cache.c:1359
static unlang_action_t cache_expire(unlang_result_t *p_result, void **rctx_out, 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:488
static fr_dict_attr_t const * attr_cache_allow_merge
Definition rlm_cache.c:108
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:137
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 a cache entry if it does not already exist.
Definition rlm_cache.c:1966
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)
void * uctx
Module method context.
Definition rlm_cache.c:75
static unlang_action_t mod_cache_it_insert_results(unlang_result_t *p_result, unlang_result_t *tmp, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *c)
Definition rlm_cache.c:883
void * rctx
Driver resume context.
Definition rlm_cache.c:74
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:1593
static unlang_action_t mod_method_ttl_find_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *entry)
Common result handling after ttl method does a find.
Definition rlm_cache.c:2103
static fr_dict_attr_t const * attr_cache_ttl
Definition rlm_cache.c:110
static unlang_action_t cache_module_yield(request_t *request, rlm_cache_handle_t *handle, fr_value_box_t const *key, rlm_cache_entry_t *entry, fr_time_delta_t *ttl, module_method_t resume, unlang_module_signal_t cancel, void *driver_rctx, void *uctx)
Definition rlm_cache.c:181
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:1663
static xlat_action_t cache_ttl_get_xlat_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_cache.c:1463
rlm_cache_handle_t * handle
Definition rlm_cache.c:70
static unlang_action_t mod_method_load_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *entry)
Common result handling for load method.
Definition rlm_cache.c:1622
static unlang_action_t mod_cache_it_ttl_results(unlang_result_t *p_result, unlang_result_t *tmp, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, cache_rctx_t *rctx)
Definition rlm_cache.c:924
static fr_dict_attr_t const * attr_cache_merge_new
Definition rlm_cache.c:106
static unlang_action_t cache_find_resume(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, void *rctx)
Resume callback after async cache find.
Definition rlm_cache.c:363
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:214
static fr_dict_t const * dict_freeradius
Definition rlm_cache.c:98
static unlang_action_t cache_expire_resume(unlang_result_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, void *rctx)
Resume callback after async cache expire.
Definition rlm_cache.c:449
static int cache_verify(map_t *map, void *uctx)
Verify that a map in the cache section makes sense.
Definition rlm_cache.c:2225
static void cache_set_ttl_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_cache.c:756
map_list_t * maps
Attribute map applied to cache entries.
Definition rlm_cache.c:59
static unlang_action_t mod_method_ttl_find_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:2144
static unlang_action_t mod_method_clear_expire_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1994
static unlang_action_t mod_cache_it_merge_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1173
static void cache_find_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_cache.c:390
static rlm_cache_entry_t * cache_alloc(rlm_cache_t const *inst, request_t *request)
Allocate a cache entry.
Definition rlm_cache.c:241
#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:828
static fr_dict_attr_t const * attr_cache_allow_insert
Definition rlm_cache.c:109
fr_value_box_t const * key
Definition rlm_cache.c:71
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Register module xlats.
Definition rlm_cache.c:2305
static void cache_insert_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_cache.c:537
static unlang_action_t mod_cache_it_ttl_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:944
static void cache_expire_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_cache.c:470
static fr_dict_attr_t const * attr_cache_status_only
Definition rlm_cache.c:107
static unlang_action_t cache_insert_resume(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t **handle, fr_time_delta_t *ttl, void *rctx)
Definition rlm_cache.c:512
static unlang_action_t mod_method_store_find_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1942
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:262
static unlang_action_t mod_method_update_expire_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1716
static unlang_action_t cache_insert(unlang_result_t *p_result, void **out_rctx, 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:554
static unlang_action_t cache_find(unlang_result_t *p_result, rlm_cache_entry_t **out, void **out_rctx, 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:408
static unlang_action_t cache_set_ttl_resume(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, void *rctx)
Definition rlm_cache.c:728
static unlang_action_t mod_cache_it_expire_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1081
static fr_dict_attr_t const * attr_cache_entry_hits
Definition rlm_cache.c:111
static unlang_action_t cache_find_results(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, rlm_cache_entry_t *c)
Process results of cache find - either synchronous or async.
Definition rlm_cache.c:318
static unlang_action_t mod_cache_it_find_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1034
static unlang_action_t mod_cache_it_expire_results(unlang_result_t *p_result, unlang_result_t *tmp, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *entry)
Definition rlm_cache.c:1056
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:2167
static xlat_arg_parser_t const cache_xlat_args[]
Definition rlm_cache.c:1302
static unlang_action_t mod_method_store_find_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, cache_call_env_t *env, rlm_cache_entry_t *entry)
Common result handdling after store method does a find.
Definition rlm_cache.c:1896
module_rlm_t rlm_cache
Definition rlm_cache.c:2331
cache_call_env_t * env
Definition rlm_cache.c:86
static unlang_action_t mod_method_status_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *entry)
Common result handling for status method.
Definition rlm_cache.c:1556
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:226
static xlat_action_t cache_xlat_results(TALLOC_CTX *ctx, unlang_result_t *result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *c, tmpl_t *target, fr_dcursor_t *out)
Definition rlm_cache.c:1307
fr_time_delta_t ttl
Definition rlm_cache.c:73
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:2059
static const call_env_method_t cache_method_env
Definition rlm_cache.c:89
fr_dict_attr_autoload_t rlm_cache_dict_attr[]
Definition rlm_cache.c:114
static unlang_action_t cache_set_ttl(unlang_result_t *p_result, void **out_rctx, 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:773
fr_dict_autoload_t rlm_cache_dict[]
Definition rlm_cache.c:101
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:1865
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:1480
static unlang_action_t mod_method_common_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1702
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:277
static unlang_action_t mod_method_update_find_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1841
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:1517
static unlang_action_t mod_method_ttl_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:2087
int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition rlm_cache.c:124
static const conf_parser_t module_config[]
Definition rlm_cache.c:45
static unlang_action_t mod_method_status_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1570
static unlang_action_t mod_method_common_results(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *entry)
Common result handling for any module method which returns updated for success.
Definition rlm_cache.c:1690
rlm_cache_entry_t * entry
Definition rlm_cache.c:72
static unlang_action_t mod_method_load_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:1641
static unlang_action_t mod_method_clear_find_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:2036
static int mod_instantiate(module_inst_ctx_t const *mctx)
Create a new rlm_cache_instance.
Definition rlm_cache.c:2276
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:1202
static xlat_action_t cache_ttl_get_xlat_results(TALLOC_CTX *ctx, unlang_result_t *result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *c, fr_dcursor_t *out)
Definition rlm_cache.c:1438
static unlang_action_t mod_cache_it_ttl(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, cache_rctx_t *rctx)
Definition rlm_cache.c:959
static unlang_action_t mod_cache_it_expire(unlang_result_t *p_result, request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, cache_rctx_t *rctx)
Definition rlm_cache.c:1096
static unlang_action_t mod_cache_it_insert_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_cache.c:909
static unlang_action_t mod_cache_it_finish(request_t *request, rlm_cache_t const *inst, rlm_cache_handle_t *handle, rlm_cache_entry_t *c)
Definition rlm_cache.c:847
Additional resume context used by mod_cache_it.
Definition rlm_cache.c:80
Resume context when driver is async.
Definition rlm_cache.c:69
fr_value_box_t key
Key used to identify entry.
Definition rlm_cache.h:74
map_list_t maps
Head of the maps list.
Definition rlm_cache.h:79
fr_unix_time_t created
When the entry was created.
Definition rlm_cache.h:76
long long int hits
How many times the entry has been retrieved.
Definition rlm_cache.h:75
#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_YIELD
The driver has pushed an async.
Definition rlm_cache.h:44
@ 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:77
Configuration for the rlm_cache module.
Definition rlm_cache.h:52
Definition rlm_cache.h:73
#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
unlang_action_t(* module_method_t)(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Module section callback.
Definition module.h:69
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:1218
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
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
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:117
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#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:236
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:146
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
void(* unlang_module_signal_t)(module_ctx_t const *mctx, request_t *request, fr_signal_t action)
A callback when the request gets a fr_signal_t.
Definition module.h:81
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:147
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:171
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition xlat.h:42
@ 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:146
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:4416
#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 * rctx
Resume context.
Definition xlat_ctx.h:54
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:374
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:391