The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
tmpl_dcursor.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
5 * (at 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: 10c829ad1e0f3b97c878f7c0b817bbb44533b35c $
19 *
20 * @brief #fr_pair_t template functions
21 * @file src/lib/server/tmpl_dcursor.c
22 *
23 * @ingroup AVP
24 *
25 * @copyright 2020-2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
26 */
27RCSID("$Id: 10c829ad1e0f3b97c878f7c0b817bbb44533b35c $")
28
29#include <freeradius-devel/server/exec.h>
30#include <freeradius-devel/server/exec_legacy.h>
31#include <freeradius-devel/server/tmpl.h>
32#include <freeradius-devel/server/tmpl_dcursor.h>
33#include <freeradius-devel/util/dlist.h>
34#include <freeradius-devel/util/proto.h>
35#include <freeradius-devel/util/value.h>
36#include <freeradius-devel/util/edit.h>
37
38static inline CC_HINT(always_inline)
40{
41 if (!cc->pool) MEM(cc->pool = talloc_pool(cc->ctx, sizeof(tmpl_dcursor_nested_t) * 5));
42}
43
44/** Traverse a list of attributes
45 *
46 * A dcursor iterator function for matching attributes
47 *
48 * @param[in] list being traversed.
49 * @param[in] curr item in the list to start tests from.
50 * @param[in] uctx Context for evaluation - in this instance a #tmpl_dcursor_nested_t
51 * @return
52 * - the next matching attribute
53 * - NULL if none found
54 */
55static void *_tmpl_cursor_child_next(fr_dlist_head_t *list, void *curr, void *uctx)
56{
57 tmpl_dcursor_nested_t *ns = uctx;
58 fr_pair_t *vp = curr;
59
60 while ((vp = fr_dlist_next(list, vp))) {
61 if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) break;
62 }
63
64 return vp;
65}
66
67static inline CC_HINT(always_inline) void tmpl_cursor_nested_push(tmpl_dcursor_ctx_t *cc, tmpl_dcursor_nested_t *ns)
68{
70}
71
72static inline CC_HINT(always_inline) void tmpl_cursor_nested_pop(tmpl_dcursor_ctx_t *cc)
73{
75
76 if (ns != &cc->leaf) talloc_free(ns);
77}
78
79/** Initialise the evaluation context for traversing a group attribute
80 *
81 */
82static inline CC_HINT(always_inline)
83void _tmpl_cursor_pair_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
84{
86
87 if (tmpl_attr_list_next(&cc->vpt->data.attribute.ar, ar)) {
89 MEM(ns = talloc(cc->pool, tmpl_dcursor_nested_t));
90 } else {
91 ns = &cc->leaf;
92 }
93
95 .ar = ar,
96 .list_ctx = list_ctx
97 };
98
99 /*
100 * Iterates over attributes of a specific type
101 */
102 if (ar_is_normal(ar)) {
104 /*
105 * Iterates over all attributes at this level
106 */
107 } else if (ar_is_unspecified(ar)) {
109 } else {
110 fr_assert_msg(0, "Invalid attr reference type");
111 }
113}
114
115/** Evaluates, then, sometimes, pops evaluation contexts from the tmpl stack
116 *
117 * To pop or not to pop is determined by whether evaluating the context again
118 * would/should/could produce another fr_pair_t.
119 *
120 * @param[in] curr The pair to evaluate.
121 * @param[in] cc Tracks state between cursor calls.
122 * @return the vp evaluated.
123 */
124static inline CC_HINT(always_inline)
126{
127 tmpl_attr_t const *ar;
129 fr_pair_t *iter = curr, *vp;
130 bool pop = false;
131 int16_t num = NUM_ALL;
132
133 ns = fr_dlist_tail(&cc->nested);
134 ar = ns->ar;
136
137 if (!ar) goto all_inst;
138
139 /*
140 * Array indexes can be attribute references. In which case they must be castable to a uint8_t.
141 *
142 * i.e. there's likly no point in allowing the array ref to specify "none", or "any", or "count".
143 *
144 * Arguably it's useful to specify "all", but why? The main utility of the array reference is to
145 * index a particular attribute when looping over a list of attributes.
146 */
147 if (ar_filter_is_tmpl(ar)) {
148 uint8_t ref;
149
151 fr_assert(tmpl_is_attr(ar->ar_tmpl));
152
153 /*
154 * Can't cast it, we're done.
155 */
156 if (tmpl_expand(&ref, NULL, 0, cc->request, ar->ar_tmpl, NULL, NULL) < 0) {
157 vp = NULL;
158 pop = true;
159 goto done;
160 }
161
162 num = ref;
163 goto find_num;
164 }
165
166 /*
167 * @todo - add dynamic evaluation of conditions. But that would work _only_ if the conditions
168 * aren't blocking, AND we somehow have a way for the conditions to reference a "self" attribute.
169 */
170
171 /*
172 * No filter means "first one", unless the "foreach" code called tmpl_attr_rewrite_leaf_num(),
173 * which rewrites are_
174 */
175 if (ar_filter_is_none(ar)) {
176 num = 0;
177
178 } else if (ar_filter_is_expr(ar)) {
179 fr_value_box_t box;
180 request_t *request = cc->request;
181
182 if (unlang_xlat_eval_type(request, &box, FR_TYPE_UINT8, NULL, request, ar->ar_expr) < 0) {
183 RPEDEBUG("Failed evaluating expression");
184 vp = NULL;
185 pop = true;
186 goto done;
187 }
188
189 num = box.vb_uint8;
190
191 } else if (!ar_filter_is_num(ar)) {
192 request_t *request = cc->request;
193
194 RDEBUG("Attribute filter is unsupported");
195 vp = NULL;
196 pop = true;
197 goto done;
198
199 } else {
200 num = ar->ar_num;
201 }
202
203 switch (num) {
204 /*
205 * Get the first instance
206 */
207 case NUM_UNSPEC:
208 pop = true;
209 break;
210
211 /*
212 * Get all instances
213 */
214 case NUM_ALL:
215 case NUM_COUNT:
216 all_inst:
217 /*
218 * @todo - arguably we shouldn't try building things here.
219 */
220 if (!vp) {
221 pop = true; /* pop only when we're done */
222
223 } else if (num != NUM_COUNT) {
224 ns->num++;
225 }
227
228 break;
229
230 /*
231 * Get the last instance
232 */
233 case NUM_LAST:
234 while ((iter = fr_dcursor_next(&ns->cursor))) {
235 vp = iter;
236 }
237 pop = true;
238 break;
239
240 /*
241 * Get the n'th instance
242 */
243 default:
244 find_num:
245 {
246 int16_t i = 0;
247
248 while ((i++ < num) && vp) vp = fr_dcursor_next(&ns->cursor);
249 pop = true;
250 }
251 break;
252 }
253
254 /*
255 * If no pair was found and there is a fill
256 * callback, call that, depending on the suffix
257 */
258 if (!vp && cc->build && ar) switch (num) {
259 case NUM_UNSPEC:
260 case NUM_LAST:
261 case 0:
262 vp = cc->build(ns->list_ctx, &ns->cursor, ar->da, cc->uctx);
263 break;
264
265 default:
266 break;
267 }
268
269done:
270 if (pop) tmpl_cursor_nested_pop(cc);
271
272 return vp;
273}
274
275static void *_tmpl_cursor_next(UNUSED fr_dlist_head_t *list, void *curr, void *uctx)
276{
277 tmpl_dcursor_ctx_t *cc = uctx;
278 tmpl_t const *vpt = cc->vpt;
279
280 fr_pair_t *vp;
281
282 switch (vpt->type) {
283 case TMPL_TYPE_ATTR:
284 {
285 tmpl_attr_t const *ar = NULL;
286 tmpl_dcursor_nested_t *ns = NULL;
287
288 /*
289 * - Continue until there are no evaluation contexts
290 * - Push a evaluation context if evaluating the head of the
291 * stack yields a VP and we're not at the deepest attribute
292 * reference.
293 * - Return if we have a VP and there are no more attribute
294 * references to push, i.e. we're at the deepest attribute
295 * reference.
296 */
297 while ((ns = fr_dlist_tail(&cc->nested))) {
298 ar = ns->ar;
299 vp = _tmpl_cursor_eval(curr, cc);
300 if (!vp) continue;
301
302 ar = tmpl_attr_list_next(&vpt->data.attribute.ar, ar);
303 if (ar) {
304 fr_pair_list_t *list_head;
305
306 list_head = &vp->vp_group;
307 _tmpl_cursor_pair_init(vp, list_head, ar, cc);
308 curr = fr_pair_list_head(list_head);
309 continue;
310 }
311
312 return vp;
313 }
314
315 return NULL;
316 }
317
318 default:
319 fr_assert(0);
320 }
321
322 return NULL;
323}
324
325#ifdef TMPL_DCURSOR_MOD
326static int tmpl_dcursor_insert(UNUSED fr_dlist_head_t *list, void *to_insert, void *uctx)
327{
328 tmpl_dcursor_ctx_t *cc = uctx;
330
331 if (!ns) return 0;
332
333 fr_dcursor_insert(&ns->cursor, to_insert);
334 return 0;
335}
336
337static int tmpl_dcursor_remove(UNUSED fr_dlist_head_t *list, void *to_remove, void *uctx)
338{
339 tmpl_dcursor_ctx_t *cc = uctx;
341 void *current;
342
343 if (!ns) return 0;
344
346 if (current == to_remove) {
348 } else {
349 fr_dcursor_set_current(&ns->cursor, to_remove);
352 }
353 return 0;
354}
355#endif
356
357/** Initialise a #fr_dcursor_t at the specified point in a pair tree
358 *
359 * This makes iterating over the one or more #fr_pair_t specified by a #tmpl_t
360 * significantly easier.
361 *
362 * @param[out] err May be NULL if no error code is required.
363 * Will be set to:
364 * - 0 on success.
365 * - -1 if no matching #fr_pair_t could be found.
366 * - -2 if list could not be found (doesn't exist in current #request_t).
367 * - -3 if context could not be found (no parent #request_t available).
368 * @param[in] ctx to make temporary allocations under.
369 * @param[in] cc to initialise. Tracks evaluation state.
370 * Must be explicitly cleared with tmpl_cursor_state_clear
371 * otherwise we will leak memory.
372 * @param[in] cursor to store iterator position.
373 * @param[in] request the current request.
374 * @param[in] list a nested list to start evaluating from.
375 * May be the child list of a pair in the request's pair tree.
376 * @param[in] vpt specifying the #fr_pair_t type or list to iterate over.
377 * @param[in] build Callback to build missing pairs.
378 * @param[in] uctx to pass to build.
379 * @return
380 * - First #fr_pair_t specified by the #tmpl_t.
381 * - NULL if no matching #fr_pair_t found, and NULL on error.
382 *
383 * @see tmpl_cursor_next
384 */
386 fr_dcursor_t *cursor,
387 request_t *request, fr_pair_t *list, tmpl_t const *vpt,
388 tmpl_dcursor_build_t build, void *uctx)
389{
390 fr_pair_t *vp = NULL;
391
393
394 /*
395 * Initialise the temporary cursor context
396 */
397 *cc = (tmpl_dcursor_ctx_t){
398 .vpt = vpt,
399 .ctx = ctx,
400 .request = request,
401 .list = &list->vp_group,
402 .build = build,
403 .uctx = uctx
404 };
406
407 /*
408 * Prime the stack!
409 */
410 switch (vpt->type) {
411 case TMPL_TYPE_ATTR:
412 _tmpl_cursor_pair_init(list, cc->list, tmpl_attr_list_head(&vpt->data.attribute.ar), cc);
413 break;
414
415 default:
416 fr_assert(0);
417 break;
418 }
419
420 /*
421 * Get the first entry from the tmpl
422 */
423#ifndef TMPL_DCURSOR_MOD
425#else
426 vp = fr_dcursor_iter_mod_init(cursor, fr_pair_list_to_dlist(cc->list), _tmpl_cursor_next, NULL, cc, tmpl_dcursor_insert, tmpl_dcursor_remove, cc);
427#endif
428 if (!vp) {
429 if (err) {
430 *err = -1;
431 if (tmpl_is_list(vpt)) {
432 fr_strerror_printf("List \"%s\" is empty", vpt->name);
433 } else {
434 fr_strerror_printf("No matching \"%s\" pairs found", tmpl_attr_tail_da(vpt)->name);
435 }
436 }
437 return NULL;
438 }
439
440 return vp;
441}
442
443/** Initialise a #fr_dcursor_t to the #fr_pair_t specified by a #tmpl_t
444 *
445 * This makes iterating over the one or more #fr_pair_t specified by a #tmpl_t
446 * significantly easier.
447 *
448 * @param[out] err May be NULL if no error code is required.
449 * Will be set to:
450 * - 0 on success.
451 * - -1 if no matching #fr_pair_t could be found.
452 * - -2 if list could not be found (doesn't exist in current #request_t).
453 * - -3 if context could not be found (no parent #request_t available).
454 * @param[in] ctx to make temporary allocations under.
455 * @param[in] cc to initialise. Tracks evaluation state.
456 * Must be explicitly cleared with tmpl_cursor_state_clear
457 * otherwise we will leak memory.
458 * @param[in] cursor to store iterator position.
459 * @param[in] request The current #request_t.
460 * @param[in] vpt specifying the #fr_pair_t type or list to iterate over.
461 * @param[in] build Callback to build missing pairs.
462 * @param[in] uctx for building new pairs.
463 * @return
464 * - First #fr_pair_t specified by the #tmpl_t.
465 * - NULL if no matching #fr_pair_t found, and NULL on error.
466 *
467 * @see tmpl_cursor_next
468 */
470 fr_dcursor_t *cursor, request_t *request, tmpl_t const *vpt,
471 tmpl_dcursor_build_t build, void *uctx)
472{
473 fr_pair_t *list;
474
476
477 if (err) *err = 0;
478
479 /*
480 * Navigate to the correct request context (parent, outer, current, etc...)
481 */
482 if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) {
483 if (err) *err = -3;
484 memset(cc, 0, sizeof(*cc)); /* so tmpl_dcursor_clear doesn't explode */
485 return NULL;
486 }
487 list = request->pair_root; /* Start navigating from the root of that request */
488
489 return tmpl_dcursor_init_relative(err, ctx, cc, cursor, request, list, vpt, build, uctx);
490}
491
492/** Clear any temporary state allocations
493 *
494 */
496{
497 /*
498 * If the pool wasn't created, nothing was talloc'd which
499 * needs freeing.
500 */
501 if (!cc->pool) return;
502
503 fr_dlist_remove(&cc->nested, &cc->leaf); /* Noop if leaf isn't inserted */
505
506 TALLOC_FREE(cc->pool);
507}
508
509/** Simple pair building callback for use with tmpl_dcursors
510 *
511 * Which always appends the new pair to the tail of the list
512 * since it is only called when no matching pairs were found when
513 * walking the list.
514 *
515 * @param[in] parent to allocate new pair within.
516 * @param[in,out] cursor to append new pair to.
517 * @param[in] da of new pair.
518 * @param[in] uctx unused.
519 * @return
520 * - newly allocated #fr_pair_t.
521 * - NULL on error.
522 */
524{
525 fr_pair_t *vp;
527 if (vp) fr_dcursor_append(cursor, vp);
528 return vp;
529}
530
531#define EXTENT_ADD(_out, _ar, _list_ctx, _list) \
532 do { \
533 tmpl_attr_extent_t *_extent; \
534 MEM(_extent = talloc(ctx, tmpl_attr_extent_t)); \
535 *_extent = (tmpl_attr_extent_t){ \
536 .ar = _ar, \
537 .list_ctx = _list_ctx, \
538 .list = _list \
539 }; \
540 fr_dlist_insert_tail(_out, _extent); \
541 } while (0)
542
543/** Determines points where the reference list extends beyond the current pair tree
544 *
545 * If a particular branch in the VP hierarchy is incomplete, i.e. the chain of attribute
546 * refers to nodes deeper than the nodes currently in the tree, then we return the
547 * deepest point node in the tree which matched, and the ar that we failed to evaluate.
548 *
549 * If the reference list resolves to one or more structural pairs, return those as well.
550 *
551 * This function can be used for a number of different operations, but it's most useful
552 * for determining insertion points for new attributes, or determining which attributes
553 * need to be updated.
554 *
555 * @param[in] ctx to allocate. It's recommended to pass a pool with space
556 * for at least five extent structures.
557 * @param[out] existing List of extents we discovered by evaluating all
558 * attribute references. May be NULL.
559 * @param[out] to_build List of extents that need building out, i.e. references
560 * extend beyond pairs. May be NULL.
561 * @param[in] request The current #request_t.
562 * @param[in] vpt specifying the #fr_pair_t type to retrieve or create.
563 * Must be #TMPL_TYPE_ATTR.
564 * @return
565 * - 0 on success a pair was found.
566 * - -2 if list could not be found (doesn't exist in current #request_t).
567 * - -3 if context could not be found (no parent #request_t available).
568 */
569int tmpl_extents_find(TALLOC_CTX *ctx,
570 fr_dlist_head_t *existing, fr_dlist_head_t *to_build,
571 request_t *request, tmpl_t const *vpt)
572{
573 fr_pair_t *curr = NULL;
574 fr_pair_list_t *list_head;
575
576 TALLOC_CTX *list_ctx = NULL;
577
579 tmpl_dcursor_nested_t *ns = NULL;
580
581 tmpl_attr_t const *ar = NULL;
582
584
586
587 /*
588 * Navigate to the correct request context
589 */
590 if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) return -3;
591
592 list_head = &request->pair_root->vp_group;
593 list_ctx = request->pair_root;
594
595 /*
596 * If it's a leaf skip all the expensive
597 * initialisation and just return the list
598 * it's part of.
599 *
600 * This is only needed because lists are
601 * treated specially. Once lists are groups
602 * this can be removed.
603 */
604 ar = tmpl_attr_list_head(&vpt->data.attribute.ar);
605 switch (ar->ar_da->type) {
607 break;
608
609 default:
610 if (existing) EXTENT_ADD(existing, NULL, list_ctx, list_head);
611 return 0;
612 }
613
614 /*
615 * Initialise the temporary cursor context
616 */
617 cc = (tmpl_dcursor_ctx_t){
618 .vpt = vpt,
619 .ctx = ctx,
620 .request = request,
621 .list = list_head
622 };
624
625 /*
626 * Prime the stack!
627 */
628 _tmpl_cursor_pair_init(list_ctx, cc.list, tmpl_attr_list_head(&vpt->data.attribute.ar), &cc);
629
630 /*
631 * - Continue until there are no evaluation contexts
632 * - Push a evaluation context if evaluating the head of the
633 * stack yields a VP and we're not at the deepest attribute
634 * reference.
635 * - Return if we have a VP and there are no more attribute
636 * references to push, i.e. we're at the deepest attribute
637 * reference.
638 */
639 curr = fr_pair_list_head(list_head);
640 while ((ns = fr_dlist_tail(&cc.nested))) {
641 tmpl_attr_t const *n_ar;
642
643 list_ctx = ns->list_ctx;
644 ar = ns->ar;
645 curr = _tmpl_cursor_eval(curr, &cc);
646 if (!curr) {
647 /*
648 * References extend beyond current
649 * pair tree.
650 */
651 if (!ar->resolve_only && to_build) EXTENT_ADD(to_build, ar, list_ctx, list_head);
652 continue; /* Rely on _tmpl_cursor_eval popping the stack */
653 }
654
655 /*
656 * Evaluate the next reference
657 */
658 n_ar = tmpl_attr_list_next(&vpt->data.attribute.ar, ar);
659 if (n_ar) {
660 ar = n_ar;
661 list_head = &curr->vp_group;
662 list_ctx = curr; /* Allocations are under the group */
663 _tmpl_cursor_pair_init(list_ctx, list_head, ar, &cc);
664 curr = fr_pair_list_head(list_head);
665 continue;
666 }
667
668 /*
669 * Only reached when we can't find an exiting
670 * part of the pair_root to keep walking.
671 *
672 * VP tree may extend beyond the reference.
673 * If the reference was structural, record this
674 * as an extent.
675 */
676 if (existing) EXTENT_ADD(existing, NULL, list_ctx, list_head);
677
678 break;
679 }
680
681 return 0;
682}
683
684/** Allocate interior pairs
685 *
686 * Builds out the pair tree to the point where leaf attributes can be added
687 *
688 * @param[out] existing List to add built out attributes to.
689 * @param[in] to_build List to remove attributes from.
690 * @param[in] vpt We are evaluating.
691 * @return
692 * - 0 on success.
693 * - -1 on failure.
694 */
696{
697 tmpl_attr_extent_t *extent = NULL;
698
699 while ((extent = fr_dlist_head(to_build))) {
700 fr_pair_list_t *list;
701 TALLOC_CTX *list_ctx;
702 fr_pair_t *vp;
703 tmpl_attr_t const *ar;
704
705 fr_assert(extent->ar); /* Interior extents MUST contain an ar */
706
707 /*
708 * Try and allocate VPs for the
709 * rest of the attribute references.
710 */
711 for (ar = extent->ar, list = extent->list, list_ctx = extent->list_ctx;
712 ar;
713 ar = tmpl_attr_list_next(&vpt->data.attribute.ar, ar)) {
714 switch (ar->type) {
717 /*
718 * Don't build leaf attributes
719 */
720 if (!fr_type_is_structural(ar->ar_da->type)) continue;
721
722 MEM(vp = fr_pair_afrom_da(list_ctx, ar->ar_da)); /* Copies unknowns */
723 fr_pair_append(list, vp);
724 list = &vp->vp_group;
725 list_ctx = vp; /* New allocations occur under the VP */
726 break;
727
728 default:
729 fr_assert_fail("references of this type should have been resolved");
730 return -1;
731 }
732 }
733
734 fr_dlist_remove(to_build, extent); /* Do this *before* zeroing the dlist headers */
735 *extent = (tmpl_attr_extent_t){
736 .list = list,
737 .list_ctx = list_ctx
738 };
739 fr_dlist_insert_tail(existing, extent); /* move between in and out */
740 }
741
742 return 0;
743}
744
746{
747 tmpl_attr_extent_t const *extent = NULL;
748 fr_pair_t *vp = NULL;
749
750 for (extent = fr_dlist_head(head);
751 extent;
752 extent = fr_dlist_next(head, extent)) {
753 tmpl_attr_t const *ar = extent->ar;
754 char const *ctx_name;
755
756 if (ar) {
757 FR_FAULT_LOG("extent-interior-attr");
758 tmpl_attr_ref_debug(extent->ar, 0);
759 } else {
760 FR_FAULT_LOG("extent-leaf");
761 }
762
763 ctx_name = talloc_get_name(extent->list_ctx);
764 if (strcmp(ctx_name, "fr_pair_t") == 0) {
765 FR_FAULT_LOG("list_ctx : %p (%s, %s)", extent->list_ctx, ctx_name,
766 ((fr_pair_t *)extent->list_ctx)->da->name);
767 } else {
768 FR_FAULT_LOG("list_ctx : %p (%s)", extent->list_ctx, ctx_name);
769 }
770 FR_FAULT_LOG("list : %p", extent->list);
771 if (fr_pair_list_empty(extent->list)) {
772 FR_FAULT_LOG("list (first) : none (%p)", extent->list);
773 } else {
774 vp = fr_pair_list_head(extent->list);
775 FR_FAULT_LOG("list (first) : %s (%p)", vp->da->name, extent->list);
776 }
777 }
778
779}
780
782{
784 tmpl_request_t *rr = NULL;
785 tmpl_attr_t *ar = NULL;
786 fr_sbuff_t our_out = FR_SBUFF(out);
787
788 /*
789 * Print all the request references
790 */
791 while ((rr = tmpl_request_list_next(&cc->vpt->data.attribute.rr, rr))) {
793 FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
794 }
795
796 ns = fr_dlist_head(&cc->nested);
797
798 /*
799 * This also prints out the things we're looping over in nested?
800 */
801 while ((ar = tmpl_attr_list_next(tmpl_attr(cc->vpt), ar))) {
802 if (ns->ar == ar) break;
803
804 if (ar->ar_da == request_attr_local) continue;
805
806 FR_SBUFF_IN_STRCPY_RETURN(&our_out, ar->da->name);
807 FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
808 }
809
810 /*
811 * Subtract one from the number, because ???
812 *
813 * @todo - for foo.[*], print out the actual da being used, which involves tracking the current
814 * vp, too. Except that we would then have to track _all_ instances of _all_ vps in a list,
815 * which is bad. Perhaps just forbid the use of foo.[*] instead.
816 */
817 while (true) {
818 fr_assert(ns->num > 0);
819
820 FR_SBUFF_IN_STRCPY_RETURN(&our_out, ns->ar->da->name);
821 FR_SBUFF_IN_CHAR_RETURN(&our_out, '[');
822 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zd", ns->num - 1);
823 FR_SBUFF_IN_CHAR_RETURN(&our_out, ']');
824
825 ns = fr_dlist_next(&cc->nested, ns);
826 if (!ns) break;
827
828 FR_SBUFF_IN_CHAR_RETURN(&our_out, ']');
829 }
830
831 FR_SBUFF_SET_RETURN(out, &our_out);
832}
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
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 int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
#define fr_dcursor_init(_cursor, _head)
Initialise a cursor.
Definition dcursor.h:732
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
Definition dcursor.h:353
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 fr_dcursor_iter_mod_init(_cursor, _list, _iter, _peek, _iter_uctx, _insert, _remove, _mod_uctx)
Initialise a cursor with a custom iterator.
Definition dcursor.h:690
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:216
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:49
#define MEM(x)
Definition debug.h:36
static fr_slen_t err
Definition dict.h:824
static int8_t fr_dict_attr_cmp(fr_dict_attr_t const *a, fr_dict_attr_t const *b)
Definition dict.h:606
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:486
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:908
static void * fr_dlist_pop_tail(fr_dlist_head_t *list_head)
Remove the tail item in a list.
Definition dlist.h:688
static void * fr_dlist_tail(fr_dlist_head_t const *list_head)
Return the TAIL item of a list or NULL if the list is empty.
Definition dlist.h:531
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
Head of a doubly linked list.
Definition dlist.h:51
#define RPEDEBUG(fmt,...)
Definition log.h:376
talloc_free(reap)
@ FR_TYPE_UINT8
8 Bit unsigned integer.
long int ssize_t
unsigned char uint8_t
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1345
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.
Definition pair.c:283
#define fr_assert(_expr)
Definition rad_assert.h:38
static rc_request_t * current
static bool done
Definition radclient.c:80
#define RDEBUG(fmt,...)
Definition radclient.h:53
fr_dict_attr_t const * request_attr_local
Definition request.c:49
static char const * name
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define FR_SBUFF(_sbuff_or_marker)
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define TMPL_VERIFY(_vpt)
Definition tmpl.h:969
#define ar_is_unspecified(_ar)
Definition tmpl.h:515
#define ar_filter_is_tmpl(_ar)
Definition tmpl.h:529
#define NUM_LAST
Definition tmpl.h:402
static fr_slen_t vpt
Definition tmpl.h:1272
TALLOC_CTX * list_ctx
Where to allocate new attributes if building out from the current extents of the tree.
Definition tmpl.h:621
fr_table_num_sorted_t const tmpl_request_ref_table[]
Map keywords to tmpl_request_ref_t values.
void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int idx)
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
#define NUM_ALL
Definition tmpl.h:400
#define ar_filter_is_num(_ar)
Definition tmpl.h:527
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:146
#define NUM_COUNT
Definition tmpl.h:401
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:931
#define ar_filter_is_expr(_ar)
Definition tmpl.h:530
#define NUM_UNSPEC
Definition tmpl.h:399
#define tmpl_attr(_tmpl)
Definition tmpl.h:665
int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql)
Resolve a tmpl_request_ref_t to a request_t.
Definition tmpl_eval.c:169
fr_pair_list_t * list
List that we tried to evaluate ar in and failed.
Definition tmpl.h:623
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
#define ar_is_normal(_ar)
Definition tmpl.h:514
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition tmpl.h:1060
@ TMPL_ATTR_TYPE_NORMAL
Normal, resolved, attribute ref.
Definition tmpl.h:386
@ TMPL_ATTR_TYPE_UNKNOWN
We have an attribute number but it doesn't match anything in the dictionary, or isn't a child of the ...
Definition tmpl.h:389
tmpl_attr_t const * ar
Attribute representing the ar after the deepest node that was found in the existing pair tree when ev...
Definition tmpl.h:615
#define ar_filter_is_none(_ar)
Definition tmpl.h:526
Describes the current extents of a pair tree in relation to the tree described by a tmpl_t.
Definition tmpl.h:612
fr_pair_t * vp
An element in a list of nested attribute references.
Definition tmpl.h:439
unsigned int _CONST resolve_only
This reference and those before it.
Definition tmpl.h:462
fr_dict_attr_t const *_CONST da
Resolved dictionary attribute.
Definition tmpl.h:443
tmpl_attr_type_t _CONST type
is a raw reference
Definition tmpl.h:467
Define manipulation functions for the attribute reference list.
Definition tmpl.h:480
tmpl_request_ref_t _CONST request
Definition tmpl.h:484
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 fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
static void * _tmpl_cursor_next(UNUSED fr_dlist_head_t *list, void *curr, void *uctx)
static fr_pair_t * _tmpl_cursor_eval(fr_pair_t *curr, tmpl_dcursor_ctx_t *cc)
Evaluates, then, sometimes, pops evaluation contexts from the tmpl stack.
ssize_t tmpl_dcursor_print(fr_sbuff_t *out, tmpl_dcursor_ctx_t const *cc)
static void _tmpl_cursor_pair_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
Initialise the evaluation context for traversing a group attribute.
fr_pair_t * tmpl_dcursor_pair_build(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, UNUSED void *uctx)
Simple pair building callback for use with tmpl_dcursors.
int tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *existing, fr_dlist_head_t *to_build, tmpl_t const *vpt)
Allocate interior pairs.
static void * _tmpl_cursor_child_next(fr_dlist_head_t *list, void *curr, void *uctx)
Traverse a list of attributes.
int tmpl_extents_find(TALLOC_CTX *ctx, fr_dlist_head_t *existing, fr_dlist_head_t *to_build, request_t *request, tmpl_t const *vpt)
Determines points where the reference list extends beyond the current pair tree.
static void tmpl_cursor_nested_push(tmpl_dcursor_ctx_t *cc, tmpl_dcursor_nested_t *ns)
static void tmpl_cursor_nested_pop(tmpl_dcursor_ctx_t *cc)
static void _tmpl_cursor_pool_init(tmpl_dcursor_ctx_t *cc)
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
fr_pair_t * tmpl_dcursor_init_relative(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc, fr_dcursor_t *cursor, request_t *request, fr_pair_t *list, tmpl_t const *vpt, tmpl_dcursor_build_t build, void *uctx)
Initialise a fr_dcursor_t at the specified point in a pair tree.
void tmpl_extents_debug(fr_dlist_head_t *head)
#define EXTENT_ADD(_out, _ar, _list_ctx, _list)
fr_pair_t * _tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc, fr_dcursor_t *cursor, request_t *request, tmpl_t const *vpt, tmpl_dcursor_build_t build, void *uctx)
Initialise a fr_dcursor_t to the fr_pair_t specified by a tmpl_t.
TALLOC_CTX * ctx
Temporary allocations go here.
tmpl_dcursor_nested_t leaf
Pre-allocated leaf state.
tmpl_dcursor_build_t build
Callback to build missing pairs.
fr_pair_list_t * list
List within the request.
fr_dcursor_t cursor
Cursor to track where we are in the list in case we're doing counts.
struct tmpl_dcursor_ctx_s tmpl_dcursor_ctx_t
size_t num
which attribute number we are looking at
fr_dlist_head_t nested
Nested state.
tmpl_t const * vpt
tmpl we're evaluating.
request_t * request
Result of following the request references.
struct tmpl_dcursor_nested_s tmpl_dcursor_nested_t
TALLOC_CTX * pool
Temporary pool.
void * uctx
Context for building new pairs.
TALLOC_CTX * list_ctx
Track where we should be allocating attributes.
fr_pair_t *(* tmpl_dcursor_build_t)(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, void *uctx)
Callback function for populating missing pair.
tmpl_attr_t const * ar
Attribute reference this state entry is associated with.
Maintains state between cursor calls.
State for traversing an attribute reference.
int unlang_xlat_eval_type(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t type, fr_dict_attr_t const *enumv, request_t *request, xlat_exp_head_t const *xlat)
Evaluate a "pure" (or not impure) xlat.
Definition xlat.c:769
static fr_slen_t head
Definition xlat.h:422
#define fr_pair_dcursor_iter_init(_cursor, _list, _iter, _uctx)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:569
fr_dlist_head_t * fr_pair_list_to_dlist(fr_pair_list_t const *list)
Get the dlist head from a pair list.
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:43
static fr_slen_t parent
Definition pair.h:851
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_type_is_structural(_x)
Definition types.h:371
#define FR_TYPE_STRUCTURAL
Definition types.h:296
static size_t char ** out
Definition value.h:997