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