The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
27 RCSID("$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 
38 static 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  */
55 static void *_tmpl_cursor_child_next(fr_dlist_head_t *list, void *curr, void *uctx)
56 {
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 
67 static inline CC_HINT(always_inline) void tmpl_cursor_nested_push(tmpl_dcursor_ctx_t *cc, tmpl_dcursor_nested_t *ns)
68 {
69  fr_dlist_insert_tail(&cc->nested, ns);
70 }
71 
72 static 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  */
82 static inline CC_HINT(always_inline)
83 void _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 
94  *ns = (tmpl_dcursor_nested_t){
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  }
112  tmpl_cursor_nested_push(cc, ns);
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  */
124 static 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;
135  vp = fr_dcursor_current(&ns->cursor);
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  }
226  fr_dcursor_next(&ns->cursor);
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 
269 done:
270  if (pop) tmpl_cursor_nested_pop(cc);
271 
272  return vp;
273 }
274 
275 static 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
326 static 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 
337 static 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 
392  TMPL_VERIFY(vpt);
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;
526  vp = fr_pair_afrom_da(parent, da);
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  */
569 int 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 
583  TMPL_VERIFY(vpt);
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) {
606  case FR_TYPE_STRUCTURAL:
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:481
#define UNUSED
Definition: build.h:313
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition: dcursor.h:480
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
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
fr_dcursor_iter_t void * current
Definition: dcursor.h:148
fr_dcursor_iter_t iter
Definition: dcursor.h:147
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:288
#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
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:337
#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
static fr_slen_t err
Definition: dict.h:821
static int8_t fr_dict_attr_cmp(fr_dict_attr_t const *a, fr_dict_attr_t const *b)
Definition: dict.h:603
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
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
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 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 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_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
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.
Definition: merged_model.c:97
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
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
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
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_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.
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
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
#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_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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
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.
Definition: tmpl_dcursor.c:385
static void * _tmpl_cursor_next(UNUSED fr_dlist_head_t *list, void *curr, void *uctx)
Definition: tmpl_dcursor.c:275
ssize_t tmpl_dcursor_print(fr_sbuff_t *out, tmpl_dcursor_ctx_t const *cc)
Definition: tmpl_dcursor.c:781
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.
Definition: tmpl_dcursor.c:83
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.
Definition: tmpl_dcursor.c:469
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.
Definition: tmpl_dcursor.c:125
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.
Definition: tmpl_dcursor.c:695
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.
Definition: tmpl_dcursor.c:569
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.
Definition: tmpl_dcursor.c:523
static void tmpl_cursor_nested_push(tmpl_dcursor_ctx_t *cc, tmpl_dcursor_nested_t *ns)
Definition: tmpl_dcursor.c:67
static void tmpl_cursor_nested_pop(tmpl_dcursor_ctx_t *cc)
Definition: tmpl_dcursor.c:72
static void _tmpl_cursor_pool_init(tmpl_dcursor_ctx_t *cc)
Definition: tmpl_dcursor.c:39
static void * _tmpl_cursor_child_next(fr_dlist_head_t *list, void *curr, void *uctx)
Traverse a list of attributes.
Definition: tmpl_dcursor.c:55
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
Definition: tmpl_dcursor.c:495
void tmpl_extents_debug(fr_dlist_head_t *head)
Definition: tmpl_dcursor.c:745
#define EXTENT_ADD(_out, _ar, _list_ctx, _list)
Definition: tmpl_dcursor.c:531
tmpl_dcursor_nested_t leaf
Pre-allocated leaf state.
Definition: tmpl_dcursor.h:79
fr_pair_list_t * list
List within the request.
Definition: tmpl_dcursor.h:69
fr_dcursor_t cursor
Cursor to track where we are in the list in case we're doing counts.
Definition: tmpl_dcursor.h:54
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.
Definition: tmpl_dcursor.h:39
struct tmpl_dcursor_ctx_s tmpl_dcursor_ctx_t
Definition: tmpl_dcursor.h:28
size_t num
which attribute number we are looking at
Definition: tmpl_dcursor.h:56
fr_dlist_head_t nested
Nested state.
Definition: tmpl_dcursor.h:71
tmpl_t const * vpt
tmpl we're evaluating.
Definition: tmpl_dcursor.h:65
struct tmpl_dcursor_nested_s tmpl_dcursor_nested_t
Definition: tmpl_dcursor.h:29
TALLOC_CTX * pool
Temporary pool.
Definition: tmpl_dcursor.h:64
TALLOC_CTX * list_ctx
Track where we should be allocating attributes.
Definition: tmpl_dcursor.h:48
tmpl_attr_t const * ar
Attribute reference this state entry is associated with.
Definition: tmpl_dcursor.h:46
Maintains state between cursor calls.
Definition: tmpl_dcursor.h:62
State for traversing an attribute reference.
Definition: tmpl_dcursor.h:44
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:622
static fr_slen_t head
Definition: xlat.h:406
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
#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.
Definition: pair_inline.c:162
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
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
FR_SBUFF_SET_RETURN(sbuff, &our_sbuff)
static size_t char ** out
Definition: value.h:997