26RCSID(
"$Id: ed3a46e421bdfb64aa9e5f07af2a5275e854e28a $")
28#define LOG_PREFIX mctx->mi->name
30#include <freeradius-devel/server/base.h>
31#include <freeradius-devel/server/module_rlm.h>
32#include <freeradius-devel/server/modpriv.h>
33#include <freeradius-devel/server/dl_module.h>
34#include <freeradius-devel/server/rcode.h>
35#include <freeradius-devel/server/tmpl.h>
36#include <freeradius-devel/util/debug.h>
37#include <freeradius-devel/util/types.h>
38#include <freeradius-devel/util/value.h>
39#include <freeradius-devel/unlang/xlat_func.h>
40#include <freeradius-devel/unlang/call_env.h>
134 if (
unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, cec, rule)) < 0))
return ret;
141 if (
inst->driver->key_parse)
return 0;
151 cf_log_err(ci,
"Driver only allows key type '%s', got '%s'",
157 cf_log_perr(ci,
"Can't convert key type '%s' to '%s'",
170 if (!
inst->driver->acquire) {
175 return inst->driver->acquire(
out, &
inst->config,
inst->driver_submodule->data, request);
183 if (!
inst->driver->release)
return;
184 if (!handle || !*handle)
return;
186 inst->driver->release(&
inst->config,
inst->driver_submodule->data, request, *handle);
197 return inst->driver->reconnect(handle, &
inst->config,
inst->driver_submodule->data, request);
210 if (
inst->driver->alloc)
return inst->driver->alloc(&
inst->config,
inst->driver_submodule->data, request);
231 if (!c || !*c || !
inst->driver->free)
return;
233 inst->driver->free(*c);
250 RDEBUG2(
"Merging cache entry into request");
252 while ((map = map_list_next(&c->
maps, map))) {
272 if (
inst->config.stats) {
301 ret =
inst->driver->find(&c, &
inst->config,
inst->driver_submodule->data, request, *handle, key);
312 RDEBUG2(
"No cache entry found for \"%pV\"", key);
328 RDEBUG2(
"Found entry for \"%pV\", but it expired %pV ago at %pV (packet received %pV). Removing it",
335 inst->driver->expire(&
inst->config,
inst->driver_submodule->data, request, handle, key);
341 RDEBUG2(
"Found entry for \"%pV\", but it was created before the current epoch. Removing it",
345 RDEBUG2(
"Found entry for \"%pV\"", key);
364 RDEBUG2(
"Expiring cache entry");
365 for (;;)
switch (
inst->driver->expire(&
inst->config,
inst->driver_submodule->data, request, *handle, key)) {
392 map_t const *map = NULL;
401 if ((
inst->config.max_entries > 0) &&
inst->driver->count &&
402 (
inst->driver->count(&
inst->config,
inst->driver_submodule->data, request, handle) >
inst->config.max_entries)) {
403 RWDEBUG(
"Cache is full: %d entries",
inst->config.max_entries);
411 map_list_init(&c->
maps);
413 RERROR(
"Failed copying key");
424 RDEBUG2(
"Creating new cache entry");
430 if (!maps)
goto skip_maps;
436 pool = talloc_pool(NULL, 2048);
437 while ((map = map_list_next(maps, map))) {
447 if (
map_to_vp(pool, &to_cache, request, map, NULL) < 0) {
461 case FR_CACHE_STATUS_ONLY:
462 case FR_CACHE_MERGE_NEW:
463 case FR_CACHE_ENTRY_HITS:
476 map_list_init(&c_map->
child);
481 switch (map->
lhs->type) {
510 REDEBUG(
"Failed copying attribute value");
522 map_list_insert_tail(&c->
maps, c_map);
524 talloc_free_children(pool);
534 if (
vp &&
vp->vp_bool) merge =
true;
541 ret =
inst->driver->insert(&
inst->config,
inst->driver_submodule->data, request, *handle, c);
572 if (!
inst->driver->set_ttl)
for (;;) {
575 ret =
inst->driver->insert(&
inst->config,
inst->driver_submodule->data, request, *handle, c);
597 ret =
inst->driver->set_ttl(&
inst->config,
inst->driver_submodule->data, request, *handle, c);
632 bool merge =
true, insert =
true, expire =
false, set_ttl =
false;
640 REDEBUG(
"Zero length key string is invalid");
649 if (
vp &&
vp->vp_bool) {
671 if (
vp) merge =
vp->vp_bool;
674 if (
vp) insert =
vp->vp_bool;
678 if (
vp->vp_int32 == 0) {
680 }
else if (
vp->vp_int32 < 0) {
691 RDEBUG3(
"merge : %s", merge ?
"yes" :
"no");
692 RDEBUG3(
"insert : %s", insert ?
"yes" :
"no");
693 RDEBUG3(
"expire : %s", expire ?
"yes" :
"no");
706 switch (p_result->
rcode) {
733 if (expire && ((exists == -1) || (exists == 1))) {
767 if ((exists < 0) && (insert || set_ttl)) {
794 if (set_ttl && (exists == 1)) {
823 if (insert && (exists == 0)) {
861 switch (
vp->
da->attr) {
863 case FR_CACHE_STATUS_ONLY:
864 case FR_CACHE_ALLOW_MERGE:
865 case FR_CACHE_ALLOW_INSERT:
866 case FR_CACHE_MERGE_NEW:
912 .dict_def = request->proto_dict,
913 .list_def = request_attr_request,
927 switch (result.rcode) {
937 while ((map = map_list_next(&c->maps, map))) {
979 switch (result.
rcode) {
1021 switch (
vp->
da->attr) {
1023 case FR_CACHE_STATUS_ONLY:
1024 case FR_CACHE_ALLOW_MERGE:
1025 case FR_CACHE_ALLOW_INSERT:
1026 case FR_CACHE_MERGE_NEW:
1054 REDEBUG(
"Zero length key string is invalid");
1093 REDEBUG(
"Zero length key string is invalid");
1106 RDEBUG2(
"Entry not found to load");
1131 bool expire =
false;
1139 REDEBUG(
"Zero length key string is invalid");
1149 ttl =
inst->config.ttl;
1152 if (
vp->vp_int32 == 0) {
1154 }
else if (
vp->vp_int32 < 0) {
1189 DEBUG3(
"Expiring cache entry");
1227 REDEBUG(
"Zero length key string is invalid");
1236 ttl =
inst->config.ttl;
1238 if (
vp && (
vp->vp_int32 > 0)) {
1248 switch (p_result->
rcode) {
1295 REDEBUG(
"Zero length key string is invalid");
1308 REDEBUG2(
"Entry not found to delete");
1342 REDEBUG(
"Zero length key string is invalid");
1352 ttl =
inst->config.ttl;
1355 if (
vp->vp_int32 < 0) {
1405 talloc_free_children(
inst);
1418 cf_log_err(map->
ci,
"Destination must be an attribute ref or a list");
1441 MEM(maps = talloc(parsed, map_list_t));
1442 map_list_init(maps);
1451 if (map_list_empty(maps)) {
1452 cf_log_err(update,
"Update section must not be empty");
1482 if (
inst->config.epoch != 0) {
1483 cf_log_err(
conf,
"Must not set 'epoch' in the configuration files");
Unlang interpreter actions.
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
static int const char char buffer[256]
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
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.
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.
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
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.
#define CALL_ENV_TERMINATOR
#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...
call_env_parser_t const * env
Parsing rules for call method env.
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
module_instance_t const * mi
Module instance that the callenv is registered to.
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
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.
#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.
#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.
#define CONF_PARSER_TERMINATOR
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
#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
#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
Defines a CONF_PAIR to C data type mapping.
Common header for all CONF_* types.
A section grouping multiple CONF_PAIR.
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
#define cf_log_err(_cf, _fmt,...)
#define cf_log_perr(_cf, _fmt,...)
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
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.
Specifies an attribute which must be present for the module to function.
Specifies a dictionary which must be loaded/loadable for the module to function.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
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.
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
#define REXDENT()
Exdent (unindent) R* messages by one level.
#define RPEDEBUG(fmt,...)
#define REDEBUG2(fmt,...)
#define RINDENT()
Indent R* messages by one level.
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.
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
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.
ssize_t map_print(fr_sbuff_t *out, map_t const *map)
Print a map to a string.
void map_debug_log(request_t *request, map_t const *map, fr_pair_t const *vp)
@ 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_BOOL
A truth value.
static bool is_printable(void const *value, size_t len)
Check whether the string is made up of printable UTF8 chars.
int unlang_fixup_update(map_t *map, void *ctx)
Validate and fixup a map that's part of an update section.
void * env_data
Per call environment data.
module_instance_t const * mi
Instance of the module being instantiated.
module_instance_t * mi
Module instance to detach.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for module calls.
Temporary structure to hold arguments for detach calls.
Temporary structure to hold arguments for instantiation calls.
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)
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.
module_t common
Common fields presented by all modules.
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.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
static const conf_parser_t config[]
#define pair_update_request(_attr, _da)
#define RDEBUG_ENABLED2()
#define RETURN_UNLANG_INVALID
#define RETURN_UNLANG_RCODE(_rcode)
#define RETURN_UNLANG_NOTFOUND
#define RETURN_UNLANG_FAIL
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_OK
The module is OK, continue.
@ RLM_MODULE_FAIL
Module failed, don't reply.
@ RLM_MODULE_NOTFOUND
User not found.
@ RLM_MODULE_UPDATED
OK (pairs modified).
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
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.
static int mod_detach(module_detach_ctx_t const *mctx)
Free any memory allocated under the instance.
static fr_dict_attr_t const * attr_cache_allow_merge
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)
fr_value_box_t * key
To lookup the cache entry with.
static unlang_action_t mod_method_store(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Create, or update a cache entry.
static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule)
static unlang_action_t mod_method_status(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Get the status by ${key} (without load)
static fr_dict_attr_t const * attr_cache_ttl
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}.
static fr_dict_attr_t const * attr_cache_merge_new
static void cache_release(rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle)
Release a handle we previously acquired.
static fr_dict_t const * dict_freeradius
static int cache_verify(map_t *map, void *uctx)
Verify that a map in the cache section makes sense.
map_list_t * maps
Attribute map applied to cache entries.
static rlm_cache_entry_t * cache_alloc(rlm_cache_t const *inst, request_t *request)
Allocate a cache entry.
static fr_dict_attr_t const * attr_cache_allow_insert
static unlang_action_t cache_set_ttl(unlang_result_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, rlm_cache_entry_t *c)
Update the TTL of an entry.
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Register module xlats.
static fr_dict_attr_t const * attr_cache_status_only
static void cache_free(rlm_cache_t const *inst, rlm_cache_entry_t **c)
Free memory associated with a cache entry.
static fr_dict_attr_t const * attr_cache_entry_hits
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.
static xlat_arg_parser_t const cache_xlat_args[]
static int cache_reconnect(rlm_cache_handle_t **handle, rlm_cache_t const *inst, request_t *request)
Reconnect an suspected inviable handle.
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}.
static const call_env_method_t cache_method_env
fr_dict_attr_autoload_t rlm_cache_dict_attr[]
static unlang_action_t cache_insert(unlang_result_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, fr_value_box_t const *key, map_list_t const *maps, fr_time_delta_t ttl)
Create and insert a cache entry.
fr_dict_autoload_t rlm_cache_dict[]
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.
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)
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.
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.
int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
static unlang_action_t cache_expire(unlang_result_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, fr_value_box_t const *key)
Expire a cache entry (removing it from the datastore)
static const conf_parser_t module_config[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
Create a new rlm_cache_instance.
static unlang_action_t mod_cache_it(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Do caching checks.
static unlang_action_t cache_find(unlang_result_t *p_result, rlm_cache_entry_t **out, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, fr_value_box_t const *key)
Find a cached entry.
fr_value_box_t key
Key used to identify entry.
map_list_t maps
Head of the maps list.
fr_unix_time_t created
When the entry was created.
long long int hits
How many times the entry has been retrieved.
@ CACHE_RECONNECT
Handle needs to be reconnected.
@ CACHE_OK
Cache entry found/updated.
@ CACHE_MISS
Cache entry notfound.
fr_unix_time_t expires
When the entry expires.
Configuration for the rlm_cache module.
static int instantiate(module_inst_ctx_t const *mctx)
#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.
char const * name
Instance name e.g. user_database.
CONF_SECTION * conf
Module's instance configuration.
size_t inst_size
Size of the module's instance data.
void * data
Module's instance data.
void * boot
Data allocated during the boostrap phase.
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
module_t * exported
Public module structure.
Named methods exported by a module.
#define tmpl_value(_tmpl)
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)
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
@ TMPL_TYPE_DATA
Value in native boxed format.
int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
Create a new tmpl from a list tmpl and a da.
static bool tmpl_attr_tail_da_is_structural(tmpl_t const *vpt)
Return true if the the last attribute reference is a structural attribute.
static bool tmpl_is_list(tmpl_t const *vpt)
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)
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
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.
Optional arguments passed to vp_tmpl functions.
eap_aka_sim_process_conf_t * inst
fr_token_t op
The operator that controls insertion of the dst attribute.
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
map_list_t child
parent map, for nested ones
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
CONF_ITEM * ci
Config item that the map was created from.
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
#define talloc_get_type_abort_const
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
#define fr_time_delta_ispos(_a)
static fr_unix_time_t fr_unix_time_from_sec(int64_t sec)
#define fr_unix_time_sub(_a, _b)
Subtract one time from another.
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)
#define fr_unix_time_lt(_a, _b)
#define fr_unix_time_add(_a, _b)
Add a time/time delta together.
A time delta, a difference in time measured in nanoseconds.
const bool fr_assignment_op[T_TOKEN_LAST]
char const * fr_tokens[T_TOKEN_LAST]
uint8_t required
Argument must be present, and non-empty.
#define XLAT_ARG_PARSER_TERMINATOR
@ XLAT_ACTION_FAIL
An xlat function failed.
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition for a single argument consumend by an xlat function.
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.
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
#define fr_type_is_variable_size(_x)
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
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.
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
#define fr_box_time_delta(_val)
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
#define fr_box_time(_val)
static size_t char ** out
#define fr_box_date(_val)
void * env_data
Expanded call env data.
module_ctx_t const * mctx
Synthesised module calling ctx.
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.