25RCSID(
"$Id: 376354d83c641a8e972de243b8460fb991d95f7e $")
27#define LOG_PREFIX "perl"
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/unlang/xlat_func.h>
33#include <freeradius-devel/unlang/xlat.h>
34#include <freeradius-devel/radius/radius.h>
49#if defined(__APPLE__) || defined(__FreeBSD__)
54# error perl must be compiled with USE_ITHREADS
107#define RLM_PERL_CONF(_x) { FR_CONF_OFFSET("func_" STRINGIFY(_x), rlm_perl_t, func_##_x), \
108 .data = NULL, .dflt = STRINGIFY(_x), .quote = T_INVALID }
134# define dl_librefs "DynaLoader::dl_librefs"
135# define dl_modules "DynaLoader::dl_modules"
151 if (!librefs)
return NULL;
153 if (!(AvFILL(librefs) >= 0)) {
157 MEM(handles = talloc_array(NULL,
void *, AvFILL(librefs) + 2));
158 for (i = 0; i <= AvFILL(librefs); i++) {
160 SV *handle_sv = *av_fetch(librefs, i,
false);
165 handle = (
void *)SvIV(handle_sv);
167 if (handle) handles[i] = handle;
173 handles[i] = (
void *)0;
186 for (i = 0; handles[i]; i++) {
187 DEBUG(
"Close %p", handles[i]);
199static XS(XS_radiusd_log)
203 croak(
"Usage: radiusd::log(level, message)");
208 level = (int) SvIV(ST(0));
209 msg = (
char *) SvPV(ST(1), PL_na);
227static XS(XS_radiusd_xlat)
235 if (items != 1) croak(
"Usage: radiusd::xlat(string)");
239 in_str = (
char *) SvPV(ST(0), PL_na);
241 slen =
xlat_aeval(request, &expanded, request, in_str, NULL, NULL);
243 REDEBUG(
"Error parsing xlat '%s'", in_str);
247 XST_mPV(0, expanded);
254 char const *
file = __FILE__;
259 newXS(
"radiusd::log",XS_radiusd_log,
"rlm_perl");
260 newXS(
"radiusd::xlat",XS_radiusd_xlat,
"rlm_perl");
278 while ((vb = fr_value_box_list_next(
head, vb))) {
281 sv = newSVpvn(vb->vb_strvalue, vb->vb_length);
285 sv = newSVpvn((
char const *)vb->vb_octets, vb->vb_length);
293 sv = newRV_inc((SV *)sub_av);
302 if (slen < 0)
return -1;
303 sv = newSVpvn(
buffer, (
size_t)slen);
308 if (vb->tainted) SvTAINT(sv);
343 DEBUG3(
"Reference returned");
347 DEBUG3(
"Integer returned");
349 vb->vb_int32 = SvIV(sv);
356 vb->vb_float64 = SvNV(sv);
361 DEBUG3(
"String returned");
362 tmp = SvPVutf8(sv, len);
366 RPEDEBUG(
"Failed to allocate %ld for output", len);
378 for (i = 0; i <= sv_len; i++) {
379 av_sv = av_fetch(av, i, 0);
393 for (i = hv_iterinit(hv); i > 0; i--) {
394 hv_sv = hv_iternextsv(hv, &tmp, &sv_len);
401 RPEDEBUG(
"Failed to allocate %d for output", sv_len);
404 fr_value_box_list_insert_tail(list, vb);
422 RPEDEBUG(
"Perl returned unsupported data type %d",
type);
428 vb->tainted = SvTAINTED(sv);
429 fr_value_box_list_insert_tail(list, vb);
457 fr_value_box_list_t list, sub_list;
460 fr_value_box_list_init(&list);
461 fr_value_box_list_init(&sub_list);
465 PERL_SET_CONTEXT(t->
perl);
476 if (fr_value_box_list_empty(&arg->vb_group))
continue;
478 if (fr_value_box_list_num_elements(&arg->vb_group) == 1) {
479 child = fr_value_box_list_head(&arg->vb_group);
483 if (child->vb_length == 0)
continue;
484 DEBUG3(
"Passing single value %pV", child);
485 sv = newSVpvn(child->vb_strvalue, child->vb_length);
486 if (child->tainted) SvTAINT(sv);
487 XPUSHs(sv_2mortal(sv));
496 DEBUG3(
"Passing list as array %pM", &arg->vb_group);
497 sv = newRV_inc((SV *)av);
498 XPUSHs(sv_2mortal(sv));
503 count = call_pv(func->vb_strvalue, G_ARRAY | G_EVAL);
507 REDEBUG(
"Exit %s", SvPV(ERRSV,n_a));
517 for (i = 0; i <
count; i++) {
520 fr_value_box_list_move_head(&list, &sub_list);
545 int indent_section = (lvl + 1) * 4;
546 int indent_item = (lvl + 2) * 4;
548 if (!cs || !rad_hv)
return;
566 if (hv_exists(rad_hv, key, strlen(key))) {
567 WARN(
"Ignoring duplicate config section '%s'", key);
572 ref = newRV_inc((SV*) sub_hv);
574 (void)hv_store(rad_hv, key, strlen(key), ref, 0);
582 if (!key || !
value)
continue;
588 if (hv_exists(rad_hv, key, strlen(key))) {
589 WARN(
"Ignoring duplicate config item '%s'", key);
593 (void)hv_store(rad_hv, key, strlen(key), newSVpvn(
value, strlen(
value)), 0);
595 DEBUG(
"%*s%s = %s", indent_item,
" ", key,
value);
599 DEBUG(
"%*s}", indent_section,
" ");
603 const char *hash_name,
bool dbg_print);
606 int *i,
const char *hash_name,
bool dbg_print)
611 if (dbg_print)
RDEBUG2(
"$%s{'%s'}[%i] = %pP", hash_name,
vp->
da->name, *i,
vp);
612 switch (
vp->vp_type) {
614 sv = newSVpvn(
vp->vp_strvalue,
vp->vp_length);
618 sv = newSVpvn((
char const *)
vp->vp_octets,
vp->vp_length);
626 sv = newRV_noinc((SV *)hv);
636 if (slen < 0)
return;
638 sv = newSVpvn(
buffer, (
size_t)slen);
656 const char *hash_name,
bool dbg_print)
686 (void)hv_store(rad_hv,
name, strlen(
name), newRV_noinc((SV *)av), 0);
694 if (dbg_print)
RDEBUG2(
"$%s{'%s'} = %pP'", hash_name,
vp->
da->name,
vp);
695 switch (
vp->vp_type) {
697 (void)hv_store(rad_hv,
name, strlen(
name), newSVpvn(
vp->vp_strvalue,
vp->vp_length), 0);
701 (void)hv_store(rad_hv,
name, strlen(
name),
702 newSVpvn((
char const *)
vp->vp_octets,
vp->vp_length), 0);
710 (void)hv_store(rad_hv,
name, strlen(
name), newRV_noinc((SV *)hv), 0);
720 (void)hv_store(rad_hv,
name, strlen(
name),
721 newSVpvn(
buffer, (
size_t)(slen)), 0);
746 if (!SvOK(sv))
return -1;
752 REDEBUG(
"Ignoring unknown attribute '%s'", key);
761 RPEDEBUG(
"Failed to create pair %s.%s = %s", list_name, key, val);
765 switch (
vp->vp_type) {
777 if (!SvROK(sv) || (SvTYPE(SvRV(sv)) != SVt_PVHV)) {
778 RPEDEBUG(
"%s should be retuned as a hash",
vp->
da->name);
782 if (
get_hv_content(
vp, request, hv, &
vp->vp_group, list_name, da,
false) < 0)
goto fail;
794 if (dbg_print)
RDEBUG2(
"%s.%pP", list_name,
vp);
807 I32 key_len, len, i, j;
810 for (i = hv_iterinit(my_hv); i > 0; i--) {
811 res_sv = hv_iternextsv(my_hv,&key,&key_len);
812 if (SvROK(res_sv) && (SvTYPE(SvRV(res_sv)) == SVt_PVAV)) {
813 av = (AV*)SvRV(res_sv);
815 for (j = 0; j <= len; j++) {
816 av_sv = av_fetch(av, j, 0);
817 if (
pairadd_sv(ctx, request, vps, key, *av_sv, list_name,
parent, dbg_print) < 0)
continue;
821 if (
pairadd_sv(ctx, request, vps, key, res_sv, list_name,
parent, dbg_print) < 0)
continue;
837 PerlInterpreter *interp,
char const *function_name)
858 PERL_SET_CONTEXT(interp);
867 rad_reply_hv = get_hv(
"RAD_REPLY", 1);
868 rad_config_hv = get_hv(
"RAD_CONFIG", 1);
869 rad_request_hv = get_hv(
"RAD_REQUEST", 1);
870 rad_state_hv = get_hv(
"RAD_STATE", 1);
872 perl_store_vps(request, &request->request_pairs, rad_request_hv,
"RAD_REQUEST",
true);
873 perl_store_vps(request, &request->reply_pairs, rad_reply_hv,
"RAD_REPLY",
true);
874 perl_store_vps(request, &request->control_pairs, rad_config_hv,
"RAD_CONFIG",
true);
875 perl_store_vps(request, &request->session_state_pairs, rad_state_hv,
"RAD_STATE",
true);
891 count = call_pv(function_name, G_SCALAR | G_EVAL | G_NOARGS);
898 REDEBUG(
"perl_embed:: module = %s , func = %s exit status= %s\n",
899 inst->module, function_name, SvPV(ERRSV,n_a));
902 }
else if (
count == 1) {
904 if (ret >= 100 || ret < 0) {
915 if (
inst->replace.request &&
916 (
get_hv_content(request->request_ctx, request, rad_request_hv, &vps,
"request",
922 if (
inst->replace.reply &&
923 (
get_hv_content(request->reply_ctx, request, rad_reply_hv, &vps,
"reply",
929 if (
inst->replace.control &&
930 (
get_hv_content(request->control_ctx, request, rad_config_hv, &vps,
"control",
936 if (
inst->replace.session &&
937 (
get_hv_content(request->session_state_ctx, request, rad_state_hv, &vps,
"session-state",
947#define RLM_PERL_FUNC(_x) \
948static unlang_action_t CC_HINT(nonnull) mod_##_x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) \
950 rlm_perl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_perl_t); \
951 return do_perl(p_result, mctx, request, \
952 ((rlm_perl_thread_t *)talloc_get_type_abort(mctx->thread, rlm_perl_thread_t))->perl, \
970 PERL_SET_CONTEXT(perl);
976 PL_perl_destruct_level = 2;
978 PL_origenviron = environ;
984 while (PL_scopestack_ix > 1) LEAVE;
996 PerlInterpreter *interp;
999 PERL_SET_CONTEXT(
inst->perl);
1001 interp = perl_clone(
inst->perl, clone_flags);
1005# if PERL_REVISION >= 5 && PERL_VERSION <8
1006 call_pv(
"CLONE", 0);
1008 ptr_table_free(PL_ptr_table);
1009 PL_ptr_table = NULL;
1011 PERL_SET_CONTEXT(aTHX);
1048 char const **embed_c;
1050 int ret = 0, argc = 0;
1058 MEM(embed_c = talloc_zero_array(
inst,
char const *, 4));
1059 memcpy(&embed, &embed_c,
sizeof(embed));
1061 if (
inst->perl_flags) {
1062 embed_c[1] =
inst->perl_flags;
1063 embed_c[2] =
inst->module;
1067 embed_c[1] =
inst->module;
1075 if ((
inst->perl = perl_alloc()) == NULL) {
1076 ERROR(
"No memory for allocating new perl interpreter!");
1079 perl_construct(
inst->perl);
1081 PL_perl_destruct_level = 2;
1085 PERL_SET_CONTEXT(
inst->perl);
1087#if PERL_REVISION >= 5 && PERL_VERSION >=8
1088 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1091 ret = perl_parse(
inst->perl,
xs_init, argc, embed, NULL);
1094 PL_endav = (AV *)NULL;
1097 ERROR(
"Perl_parse failed: %s not found or has syntax errors",
inst->module);
1104 inst->rad_perlconf_hv = get_hv(
"RAD_PERLCONF", 1);
1108 inst->perl_parsed =
true;
1109 perl_run(
inst->perl);
1123 int ret = 0,
count = 0;
1126 if (
inst->perl_parsed) {
1128 PERL_SET_CONTEXT(
inst->perl);
1129 if (
inst->rad_perlconf_hv != NULL) hv_undef(
inst->rad_perlconf_hv);
1131 if (
inst->func_detach) {
1132 dSP;
ENTER; SAVETMPS;
1135 count = call_pv(
inst->func_detach, G_SCALAR | G_EVAL );
1140 if (ret >= 100 || ret < 0) {
1168 char const **embed_c;
1173#define LOAD_INFO(_fmt, ...) fr_log(LOG_DST, L_INFO, __FILE__, __LINE__, "rlm_perl - " _fmt, ## __VA_ARGS__)
1174#define LOAD_WARN(_fmt, ...) fr_log_perror(LOG_DST, L_WARN, __FILE__, __LINE__, \
1175 &(fr_log_perror_format_t){ \
1176 .first_prefix = "rlm_perl - ", \
1177 .subsq_prefix = "rlm_perl - ", \
1179 _fmt, ## __VA_ARGS__)
1181 LOAD_INFO(
"Perl version: %s", PERL_API_VERSION_STRING);
1195 MEM(embed_c = talloc_zero_array(NULL,
char const *, 1));
1196 memcpy(&embed, &embed_c,
sizeof(embed));
1200 PERL_SYS_INIT3(&argc, &embed, &envp);
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
static int const char char buffer[256]
#define DIAG_UNKNOWN_PRAGMAS
#define CONF_PARSER_TERMINATOR
#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_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Defines a CONF_PAIR to C data type mapping.
Common header for all CONF_* types.
Configuration AVP similar to a fr_pair_t.
A section grouping multiple CONF_PAIR.
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
#define cf_item_next(_ci, _curr)
static int split(char **input, char **output, bool syntax_string)
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_next_peek(fr_dcursor_t *cursor)
Return the next iterator item without advancing the cursor.
int dependency_version_number_add(CONF_SECTION *cs, char const *name, char const *version)
Add a library/server version pair to the main configuration.
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
void * dl_open_by_sym(char const *sym_name, int flags)
Utility function to dlopen the library containing a particular symbol.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
static xlat_action_t perl_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Call perl code using an xlat.
#define REXDENT()
Exdent (unindent) R* messages by one level.
#define RPEDEBUG(fmt,...)
#define RINDENT()
Indent R* messages by one level.
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
module_instance_t const * mi
Instance of the module being instantiated.
void * thread
Thread specific instance data.
void * thread
Thread instance data.
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.
Temporary structure to hold arguments for thread_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)
module_t common
Common fields presented by all modules.
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
int8_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da, and tag.
static const conf_parser_t config[]
#define RETURN_MODULE_RCODE(_rcode)
#define RETURN_MODULE_FAIL
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_FAIL
Module failed, don't reply.
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
static unlang_action_t mod_accounting(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Write accounting data to Couchbase documents.
static unlang_action_t mod_post_auth(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
static int perl_sv_to_vblist(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, SV *sv)
Parse a Perl SV and create value boxes, appending to a list.
static int mod_detach(module_detach_ctx_t const *mctx)
static int mod_load(void)
PerlInterpreter * perl
Thread specific perl interpreter.
#define RLM_PERL_FUNC(_x)
HV * rad_perlconf_hv
holds "config" items (perl RAD_PERLCONF hash).
rlm_perl_replace_t replace
char const *char const * func_authorize
static xlat_arg_parser_t const perl_xlat_args[]
static void ** rlm_perl_get_handles(pTHX)
static const conf_parser_t replace_config[]
char const * func_authenticate
char const * func_preacct
static XS(XS_radiusd_log)
EXTERN_C void boot_DynaLoader(pTHX_ CV *cv)
static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv)
#define RLM_PERL_CONF(_x)
static int mod_bootstrap(module_inst_ctx_t const *mctx)
static void rlm_perl_interp_free(PerlInterpreter *perl)
static unlang_action_t do_perl(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request, PerlInterpreter *interp, char const *function_name)
static void mod_unload(void)
static void xs_init(pTHX)
char const * func_post_auth
#define LOAD_WARN(_fmt,...)
static void rlm_perl_close_handles(void **handles)
static void perl_store_vps(request_t *request, fr_pair_list_t *vps, HV *rad_hv, const char *hash_name, bool dbg_print)
bool session
Should the session list be replaced after module call.
static int pairadd_sv(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *vps, char *key, SV *sv, const char *list_name, fr_dict_attr_t const *parent, bool dbg_print)
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
static void perl_vp_to_svpvn_element(request_t *request, AV *av, fr_pair_t *vp, int *i, const char *hash_name, bool dbg_print)
static int perl_vblist_to_av(AV *av, fr_value_box_list_t *head)
Convert a list of value boxes to a Perl array for passing to subroutines.
static void * perl_dlhandle
To allow us to load perl's symbols into the global symbol table.
#define LOAD_INFO(_fmt,...)
static const conf_parser_t module_config[]
static int get_hv_content(TALLOC_CTX *ctx, request_t *request, HV *my_hv, fr_pair_list_t *vps, const char *list_name, fr_dict_attr_t const *parent, bool dbg_print)
static _Thread_local request_t * rlm_perl_request
bool reply
Should the reply list be replaced after module call.
bool control
Should the control list be replaced after module call.
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
static void rlm_perl_clear_handles(pTHX)
bool request
Should the request list be replaced after module call.
char const * func_accounting
static int instantiate(module_inst_ctx_t const *mctx)
static unlang_action_t mod_preacct(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
CONF_SECTION * conf
Module's instance configuration.
size_t inst_size
Size of the module's instance data.
void * data
Module's instance data.
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Named methods exported by a module.
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
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
bool required
Argument must be present, and non-empty.
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
#define XLAT_ARG_PARSER_TERMINATOR
@ XLAT_ACTION_FAIL
An xlat function failed.
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx))
Definition for a single argument consumend by an xlat function.
#define ATTRIBUTE_EQ(_x, _y)
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
void fr_pair_list_sort(fr_pair_list_t *list, fr_cmp_t cmp)
Sort a doubly linked list of fr_pair_ts using merge sort.
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
#define PAIR_LIST_VERIFY(_x)
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
#define FR_TYPE_STRUCTURAL
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
#define fr_value_box_list_foreach(_list_head, _iter)
static size_t char ** out
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.