The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
foreach.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: f62d81a4386536553d8fe4531ea2b402e86fb741 $
19 *
20 * @file unlang/foreach.c
21 * @brief Unlang "foreach" keyword evaluation.
22 *
23 * @copyright 2006-2019 The FreeRADIUS server project
24 */
25RCSID("$Id: f62d81a4386536553d8fe4531ea2b402e86fb741 $")
26
27#include <freeradius-devel/server/tmpl_dcursor.h>
28#include <freeradius-devel/server/rcode.h>
29#include <freeradius-devel/unlang/action.h>
30#include <freeradius-devel/unlang/unlang_priv.h>
31#include <freeradius-devel/unlang/xlat_func.h>
32
33#include "foreach_priv.h"
34#include "return_priv.h"
35#include "xlat_priv.h"
36
37#define BUFFER_SIZE (256)
38
39/** State of a foreach loop
40 *
41 */
42typedef struct {
43 request_t *request; //!< The current request.
44 fr_dcursor_t cursor; //!< Used to track our place in the list
45 fr_pair_t *key; //!< local variable which contains the key
46 fr_pair_t *value; //!< local variable which contains the value
47 tmpl_t const *vpt; //!< pointer to the vpt
48
49 uint32_t index; //!< for xlat results
50 char *buffer; //!< for key values
51
52 unlang_result_t exp_result; //!< for xlat expansion
53 fr_value_box_list_t list; //!< value box list for looping over xlats
54
55 tmpl_dcursor_ctx_t cc; //!< tmpl cursor state
56
57#ifndef NDEBUG
58 int indent; //!< for catching indentation issues
59#endif
61
62/*
63 * Brute-force things instead of doing it the "right" way.
64 *
65 * We would ideally like to have the local variable be a ref to the current vp from the cursor. However,
66 * that isn't (yet) supported. We do have #FR_TYPE_PAIR_CURSOR, but there is no way to save the cursor,
67 * or address it. See also xlat_expr.c for notes on using '$$' to refer to a cursor. Maybe we need a
68 * new magic "list", which is called "cursor", or "self"? That way we can also address parent cursors?
69 *
70 * In order to support that, we would have to update a lot of things:
71 *
72 * - the foreach code has not just create a local attribute, but mark up that attribute as it's really a cursor".
73 * - maybe we also need to put the cursor into its own stack frame? Or have it as a common field
74 * in every frame?
75 * - the tmpl code has to be updated so that when you reference a "cursor attribute", it finds the cursor,
76 * and edits the pair associated with the cursor
77 * - update tmpl_eval_pair(), because that's what's used in the xlat code. That gets us all
78 * references to the _source_ VP.
79 * - we also have to update the edit.c code, which calls tmpl_dcursor_init() to get pairs from
80 * a tmpl_t of type ATTR.
81 * - for LHS assignment, the edit code has to be updated: apply_edits_to_leaf() and apply_edits_to_list()
82 * which calls fr_edit_list_apply_pair_assignment() to do the actual work. But we could likely just
83 * check current->lhs.vp, and dereference that to get the underlying thing.
84 *
85 * What we ACTUALLY do instead is in the compiler when we call define_local_variable(), we clone the "da"
86 * hierarchy via fr_dict_attr_acopy_local(). That function which should go away when we add refs.
87 *
88 * Then this horrific function copies the pairs by number, which re-parents them to the correct
89 * destination da. It's brute-force and expensive, but it's easy. And for now, it's less work than
90 * re-doing substantial parts of the server core and utility libraries.
91 */
92static int unlang_foreach_pair_copy(fr_pair_t *to, fr_pair_t *from, fr_dict_attr_t const *from_parent)
93{
94 fr_assert(fr_type_is_structural(to->vp_type));
95 fr_assert(fr_type_is_structural(from->vp_type));
96
97 fr_pair_list_foreach(&from->vp_group, vp) {
98 fr_pair_t *child;
99
100 /*
101 * We only copy children of the parent TLV, but we can copy internal attributes, as they
102 * can exist anywhere.
103 */
104 if (vp->da->parent != from_parent) {
105 if (vp->da->flags.internal) {
106 child = fr_pair_copy(to, vp);
107 if (child) fr_pair_append(&to->vp_group, child);
108 }
109 continue;
110 }
111
112 child = fr_pair_afrom_child_num(to, to->da, vp->da->attr);
113 if (!child) continue;
114
115 fr_pair_append(&to->vp_group, child);
116
117 if (fr_type_is_leaf(child->vp_type)) {
118 if (unlikely(fr_value_box_copy(child, &child->data, &vp->data) < 0)) return -1;
119 continue;
120 }
121
123
124 if (unlang_foreach_pair_copy(child, vp, vp->da) < 0) return -1;
125 }
126
127 return 0;
128}
129
130/** Ensure request data is pulled out of the request if the frame is popped
131 *
132 */
134{
135 request_t *request = state->request;
136 fr_pair_t *vp;
137
138 fr_assert(state->value);
139
140 if (tmpl_is_xlat(state->vpt)) return 0;
141
142 tmpl_dcursor_clear(&state->cc);
143
144 /*
145 * Now that we're done, the leaf entries can be changed again.
146 */
147 vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
148 if (!vp) {
149 tmpl_dcursor_clear(&state->cc);
150 return 0;
151 }
152 do {
153 vp->vp_edit = false;
154 } while ((vp = fr_dcursor_next(&state->cursor)) != NULL);
155 tmpl_dcursor_clear(&state->cc);
156
157 return 0;
158}
159
161{
162 fr_value_box_t box;
163
164 if (!state->key) return 0;
165
166 fr_value_box_clear_value(&state->key->data);
167
168 fr_value_box(&box, state->index, false);
169
170 if (fr_value_box_cast(state->key, &state->key->data, state->key->vp_type, state->key->da, &box) < 0) {
171 RDEBUG("Failed casting 'foreach' key variable '%s' from %u", state->key->da->name, state->index);
172 return -1;
173 }
174
175 return 0;
176}
177
178
180{
181 unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
182 fr_value_box_t *box;
183
184next:
185 state->index++;
186
187 box = fr_dcursor_next(&state->cursor);
188 if (!box) return UNLANG_ACTION_EXECUTE_NEXT; /* Don't change the section rcode */
189
190 if (unlang_foreach_xlat_key_update(request, state) < 0) goto next;
191
192 fr_value_box_clear_value(&state->value->data);
193 if (fr_value_box_cast(state->value, &state->value->data, state->value->vp_type, state->value->da, box) < 0) {
194 RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pV", state->value->da->name, box);
195 goto next;
196 }
197
198 repeatable_set(frame);
199
200 /*
201 * Push the child, and yield for a later return.
202 */
204}
205
206
208{
209 unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
210 fr_value_box_t *box;
211
212 if (!XLAT_RESULT_SUCCESS(&state->exp_result)) {
213 RDEBUG("Failed expanding 'foreach' list");
215 }
216
217 box = fr_dcursor_init(&state->cursor, fr_value_box_list_dlist_head(&state->list));
218 if (!box) {
219 done:
221 }
222
223 fr_value_box_clear_value(&state->value->data);
224
225next:
226 if (fr_value_box_cast(state->value, &state->value->data, state->value->vp_type, state->value->da, box) < 0) {
227 RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pV", state->value->da->name, box);
228 box = fr_dcursor_next(&state->cursor);
229 if (!box) goto done;
230
231 goto next;
232 }
233
235
236 /*
237 * Push the child, and yield for a later return.
238 */
240}
241
242
243/*
244 * Loop over an xlat expansion
245 */
248{
249 fr_value_box_list_init(&state->list);
250
251 if (unlang_xlat_push(state, &state->exp_result, &state->list, request, tmpl_xlat(state->vpt), false) < 0) {
252 REDEBUG("Failed starting expansion of %s", state->vpt->name);
254 }
255
256 if (unlang_foreach_xlat_key_update(request, state) < 0) {
258 }
259
261 repeatable_set(frame);
262
264}
265
267{
268 if (!state->key) return;
269
270 switch (state->key->vp_type) {
271 case FR_TYPE_UINT32:
272 state->key->vp_uint32++;
273 break;
274
275 case FR_TYPE_STRING:
276 fr_value_box_clear_value(&state->key->data);
277 if (tmpl_dcursor_print(&FR_SBUFF_OUT(state->buffer, BUFFER_SIZE), &state->cc) > 0) {
278 fr_value_box_strdup(state->key, &state->key->data, NULL, state->buffer, false);
279 }
280 break;
281
282 default:
283 fr_assert(0);
284 break;
285
286 }
287}
288
290{
291 unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
292 fr_pair_t *vp;
293
294 vp = fr_dcursor_current(&state->cursor);
295 fr_assert(vp != NULL);
296
297 /*
298 * If we modified the value, copy it back to the original pair. Note that the copy does NOT
299 * check the "immutable" flag. That flag is for the people using unlang, not for the
300 * interpreter.
301 */
302 if (fr_type_is_leaf(vp->vp_type)) {
303 if (vp->vp_type == state->value->vp_type) {
305 if (unlikely(fr_value_box_copy(vp, &vp->data, &state->value->data) < 0)) {
306 RPEDEBUG("Failed copying value from %s to %s", state->value->da->name, vp->da->name);
307 return UNLANG_ACTION_FAIL;
308 }
309 } else {
310 /*
311 * @todo - this shouldn't happen?
312 */
313 }
314 } else {
316
317 /*
318 * @todo - copy the pairs back?
319 */
320 }
321
322next:
323 vp = fr_dcursor_next(&state->cursor);
324 if (!vp) {
325#ifndef NDEBUG
326 fr_assert(state->indent == request->log.indent.unlang);
327#endif
329 }
330
331 unlang_foreach_attr_key_update(request, state);
332
333 /*
334 * Copy the data.
335 */
336 if (vp->vp_type == FR_TYPE_GROUP) {
337 fr_assert(state->value->vp_type == FR_TYPE_GROUP);
338
339 fr_pair_list_free(&state->value->vp_group);
340
341 if (fr_pair_list_copy(state->value, &state->value->vp_group, &vp->vp_group) < 0) {
342 REDEBUG("Failed copying members of %s", state->value->da->name);
344 }
345
346 } else if (fr_type_is_structural(vp->vp_type)) {
347 if (state->value->vp_type != vp->vp_type) goto next;
348
349 fr_pair_list_free(&state->value->vp_group);
350
351 if (unlang_foreach_pair_copy(state->value, vp, vp->da) < 0) {
352 REDEBUG("Failed copying children of %s", state->value->da->name);
354 }
355
356 } else {
357 fr_value_box_clear_value(&state->value->data);
358 if (fr_value_box_cast(state->value, &state->value->data, state->value->vp_type, state->value->da, &vp->data) < 0) {
359 RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pP", state->value->da->name, vp);
360 goto next;
361 }
362
363#ifndef NDEBUG
364 RDEBUG2("# looping with: %s = %pV", state->value->da->name, &vp->data);
365#endif
366 }
367
368 repeatable_set(frame);
369
370 /*
371 * Push the child, and yield for a later return.
372 */
374}
375
376/*
377 * Loop over an attribute
378 */
381{
382 fr_pair_t *vp;
383
384 /*
385 * No matching attributes, we can't do anything.
386 */
387 vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
388 if (!vp) {
389 tmpl_dcursor_clear(&state->cc);
391 }
392
393 /*
394 * Before we loop over the variables, ensure that the user can't pull the rug out from
395 * under us.
396 */
397 do {
398 if (vp->vp_edit) {
399 REDEBUG("Cannot do nested 'foreach' loops over the same attribute %pP", vp);
400 fail:
401 tmpl_dcursor_clear(&state->cc);
403 }
404
405 vp->vp_edit = true;
406 } while ((vp = fr_dcursor_next(&state->cursor)) != NULL);
407 tmpl_dcursor_clear(&state->cc);
408
409 vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
410 fr_assert(vp != NULL);
411
412next:
413 /*
414 * Update the key with the current path. Attribute indexes start at zero.
415 */
416 if (state->key && (state->key->vp_type == FR_TYPE_STRING)) unlang_foreach_attr_key_update(request, state);
417
418 if (vp->vp_type == FR_TYPE_GROUP) {
419 fr_assert(state->value->vp_type == FR_TYPE_GROUP);
420
421 if (fr_pair_list_copy(state->value, &state->value->vp_group, &vp->vp_group) < 0) {
422 REDEBUG("Failed copying members of %s", state->value->da->name);
423 goto fail;
424 }
425
426 } else if (fr_type_is_structural(vp->vp_type)) {
427 if (state->value->vp_type != vp->vp_type) {
428 vp = fr_dcursor_next(&state->cursor);
429 if (vp) goto next;
430
431 fr_assert(state->indent == request->log.indent.unlang);
433 }
434
435 if (unlang_foreach_pair_copy(state->value, vp, vp->da) < 0) {
436 REDEBUG("Failed copying children of %s", state->value->da->name);
437 goto fail;
438 }
439
440 } else {
441 fr_value_box_clear_value(&state->value->data);
442 while (vp && (fr_value_box_cast(state->value, &state->value->data, state->value->vp_type, state->value->da, &vp->data) < 0)) {
443 RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pP", state->value->da->name, vp);
444 vp = fr_dcursor_next(&state->cursor);
445 }
446
447 /*
448 * Couldn't cast anything, the loop can't be run.
449 */
450 if (!vp) {
451 tmpl_dcursor_clear(&state->cc);
453 }
454 }
455
457
458 repeatable_set(frame);
459
460 /*
461 * Push the child, and go process it.
462 */
464}
465
466
468{
472
473 MEM(frame->state = state = talloc_zero(request->stack, unlang_frame_state_foreach_t));
474 talloc_set_destructor(state, _free_unlang_frame_state_foreach);
475
476 state->request = request;
477#ifndef NDEBUG
478 state->indent = request->log.indent.unlang;
479#endif
480
481 /*
482 * Get the value.
483 */
484 fr_assert(gext->value);
485
486 state->vpt = gext->vpt;
487
488 fr_assert(fr_pair_find_by_da(&request->local_pairs, NULL, gext->value) == NULL);
489
490 /*
491 * Create the local variable and populate its value.
492 */
493 if (fr_pair_append_by_da(request->local_ctx, &state->value, &request->local_pairs, gext->value) < 0) {
494 REDEBUG("Failed creating %s", gext->value->name);
496 }
497 fr_assert(state->value != NULL);
498
499 if (gext->key) {
500 fr_assert(fr_pair_find_by_da(&request->local_pairs, NULL, gext->key) == NULL);
501
502 if (fr_pair_append_by_da(request->local_ctx, &state->key, &request->local_pairs, gext->key) < 0) {
503 REDEBUG("Failed creating %s", gext->key->name);
505 }
506 fr_assert(state->key != NULL);
507 }
508
509 if (tmpl_is_attr(gext->vpt)) {
510 MEM(state->buffer = talloc_array(state, char, BUFFER_SIZE));
511 return unlang_foreach_attr_init(p_result, request, frame, state);
512 }
513
514 fr_assert(tmpl_is_xlat(gext->vpt));
515
516 return unlang_foreach_xlat_init(p_result, request, frame, state);
517}
518
520{
522 unlang_stack_t *stack = request->stack;
523 unsigned int break_depth;
524
525 RDEBUG2("%s", unlang_ops[frame->instruction->type].name);
526
527 /*
528 * As we're unwinding intermediary frames we
529 * won't be taking their rcodes or priorities
530 * into account. We do however want to record
531 * the current section rcode.
532 */
533 *p_result = frame->section_result;
534
535 /*
536 * Stop at the next break point, or if we hit
537 * the a top frame.
538 */
539 ua = unwind_to_op_flag(&break_depth, request->stack, UNLANG_OP_FLAG_BREAK_POINT);
540 repeatable_clear(&stack->frame[break_depth]);
541 return ua;
542}
543
545{
546 unlang_stack_t *stack = request->stack;
547
548 RDEBUG2("%s", unlang_ops[frame->instruction->type].name);
549
551}
552
554{
556 fr_token_t token;
557 char const *name2;
558 char const *type_name, *variable_name;
560 unlang_t *c;
561
562 fr_type_t key_type;
563 char const *key_name;
564
566 unlang_foreach_t *gext;
567
568 ssize_t slen;
569 tmpl_t *vpt;
570 fr_dict_attr_t const *da = NULL;
571
572 tmpl_rules_t t_rules;
573 unlang_compile_ctx_t unlang_ctx2;
574
575 /*
576 * Ignore empty "foreach" blocks, and don't even sanity check their arguments.
577 */
578 if (!cf_item_next(cs, NULL)) {
579 return UNLANG_IGNORE;
580 }
581
582 /*
583 * We allow unknown attributes here.
584 */
585 t_rules = *(unlang_ctx->rules);
586 t_rules.attr.allow_unknown = true;
587 t_rules.attr.allow_wildcard = true;
588 RULES_VERIFY(&t_rules);
589
590 name2 = cf_section_name2(cs);
591 fr_assert(name2 != NULL); /* checked in cf_file.c */
592
593 /*
594 * Allocate a group for the "foreach" block.
595 */
597 if (!g) return NULL;
598
600
601 /*
602 * Create the template. If we fail, AND it's a bare word
603 * with &Foo-Bar, it MAY be an attribute defined by a
604 * module. Allow it for now. The pass2 checks below
605 * will fix it up.
606 */
607 token = cf_section_name2_quote(cs);
608 if (token != T_BARE_WORD) {
609 cf_log_err(cs, "Data being looped over in 'foreach' must be an attribute reference or dynamic expansion, not a string");
610 print_ref:
611 cf_log_err(ci, DOC_KEYWORD_REF(foreach));
612 error:
613 talloc_free(g);
614 return NULL;
615 }
616
617 slen = tmpl_afrom_substr(g, &vpt,
618 &FR_SBUFF_IN_STR(name2),
619 token,
620 NULL,
621 &t_rules);
622 if (!vpt) {
623 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'foreach'", name2);
624 goto error;
625 }
626
627 /*
628 * If we don't have a negative return code, we must have a vpt
629 * (mostly to quiet coverity).
630 */
631 fr_assert(vpt);
632
633 if (tmpl_is_attr(vpt)) {
635 cf_log_warn(cs, "Attribute reference should be updated to use %s[*]", vpt->name);
637 }
638
640 cf_log_err(cs, "Attribute references must be of the form ...%s[*]", tmpl_attr_tail_da(vpt)->name);
641 goto print_ref;
642 }
643
644 } else if (!tmpl_contains_xlat(vpt)) {
645 cf_log_err(cs, "Invalid content in 'foreach (...)', it must be an attribute reference or a dynamic expansion");
646 goto print_ref;
647 }
648
649 gext = unlang_group_to_foreach(g);
650 gext->vpt = vpt;
651
652 c->name = "foreach";
653 MEM(c->debug_name = talloc_typed_asprintf(c, "foreach %s", name2));
654
655 /*
656 * Copy over the compilation context. This is mostly
657 * just to ensure that retry is handled correctly.
658 * i.e. reset.
659 */
660 unlang_compile_ctx_copy(&unlang_ctx2, unlang_ctx);
661
662 /*
663 * Then over-write the new compilation context.
664 */
665 unlang_ctx2.section_name1 = "foreach";
666 unlang_ctx2.section_name2 = name2;
667 unlang_ctx2.rules = &t_rules;
668 t_rules.parent = unlang_ctx->rules;
669
670 /*
671 * If we have "type name", then define a local variable of that name.
672 */
673 type_name = cf_section_argv(cs, 0); /* AFTER name1, name2 */
674
675 key_name = cf_section_argv(cs, 2);
676 if (key_name) {
677 key_type = fr_table_value_by_str(fr_type_table, key_name, FR_TYPE_VOID);
678 } else {
679 key_type = FR_TYPE_VOID;
680 }
681 key_name = cf_section_argv(cs, 3);
682
683 if (tmpl_is_xlat(vpt)) {
684 if (!type_name) {
685 cf_log_err(cs, "Dynamic expansions MUST specify a data type for the variable");
686 goto print_ref;
687 }
688
690
691 /*
692 * No data type was specified, see if we can get one from the function.
693 */
694 if (type == FR_TYPE_NULL) {
696 if (fr_type_is_leaf(type)) goto get_name;
697
698 cf_log_err(cs, "Unable to determine return data type from dynamic expansion");
699 goto print_ref;
700 }
701
702 if (!fr_type_is_leaf(type)) {
703 cf_log_err(cs, "Dynamic expansions MUST specify a non-structural data type for the variable");
704 goto print_ref;
705 }
706
707 if ((key_type != FR_TYPE_VOID) && !fr_type_is_numeric(key_type)) {
708 cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be numeric", fr_type_to_str(key_type));
709 goto print_ref;
710 }
711
712 goto get_name;
713 } else {
715
716 if ((key_type != FR_TYPE_VOID) && (key_type != FR_TYPE_STRING) && (key_type != FR_TYPE_UINT32)) {
717 cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be 'string' or 'uint32'", fr_type_to_str(key_type));
718 goto print_ref;
719 }
720 }
721
722 if (type_name) {
724
727
728 /*
729 * foreach string foo (&tlv-thing.[*]) { ... }
730 */
732 goto get_name;
733 }
734
736
737 if (type == FR_TYPE_NULL) {
738 type = da->type;
739
740 } else if (fr_type_is_leaf(type) != fr_type_is_leaf(da->type)) {
741 incompatible:
742 cf_log_err(cs, "Incompatible data types in foreach variable (%s), and reference %s being looped over (%s)",
743 fr_type_to_str(type), da->name, fr_type_to_str(da->type));
744 goto print_ref;
745
746 } else if (fr_type_is_structural(type) && (type != da->type)) {
747 goto incompatible;
748 }
749
750 get_name:
751 variable_name = cf_section_argv(cs, 1);
752
753 /*
754 * Define the local variables.
755 */
756 g->variables = var = talloc_zero(g, unlang_variable_t);
757 if (!var) goto error;
758
759 var->dict = fr_dict_protocol_alloc(unlang_ctx->rules->attr.dict_def);
760 if (!var->dict) goto error;
761
762 var->root = fr_dict_root(var->dict);
763
764 var->max_attr = 1;
765
766 if (unlang_define_local_variable(cf_section_to_item(cs), var, &t_rules, type, variable_name, da) < 0) goto error;
767
768 t_rules.attr.dict_def = var->dict;
769 t_rules.attr.namespace = NULL;
770
771 /*
772 * And ensure we have the key.
773 */
774 gext->value = fr_dict_attr_by_name(NULL, var->root, variable_name);
775 fr_assert(gext->value != NULL);
776
777 /*
778 * Define the local key variable. Note that we don't copy any children.
779 */
780 if (key_type != FR_TYPE_VOID) {
781 if (unlang_define_local_variable(cf_section_to_item(cs), var, &t_rules, key_type, key_name, NULL) < 0) goto error;
782
783 gext->key = fr_dict_attr_by_name(NULL, var->root, key_name);
784 fr_assert(gext->key != NULL);
785 }
786 }
787
788 return unlang_compile_children(g, &unlang_ctx2);
789}
790
791
793{
794 unlang_t *unlang;
795
796 for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
797 /*
798 * "break" doesn't go past a return point.
799 */
800 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
801
802 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_BREAK_POINT) != 0) break;
803 }
804
805 if (!unlang) {
806 error:
807 cf_log_err(ci, "Invalid location for 'break' - it can only be used inside 'foreach' or 'switch'");
808 cf_log_err(ci, DOC_KEYWORD_REF(break));
809 return NULL;
810 }
811
812 parent->closed = true;
813
815}
816
818{
819 unlang_t *unlang;
820
821 for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
822 /*
823 * "continue" doesn't go past a return point.
824 */
825 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
826
827 if (unlang->type == UNLANG_TYPE_FOREACH) break;
828 }
829
830 if (!unlang) {
831 error:
832 cf_log_err(ci, "Invalid location for 'continue' - it can only be used inside 'foreach'");
833 cf_log_err(ci, DOC_KEYWORD_REF(break));
834 return NULL;
835 }
836
837 parent->closed = true;
838
840}
841
843{
845 .name = "foreach",
846 .type = UNLANG_TYPE_FOREACH,
848
849 .compile = unlang_compile_foreach,
850 .interpret = unlang_foreach,
851
852 .unlang_size = sizeof(unlang_foreach_t),
853 .unlang_name = "unlang_foreach_t",
854
855 .pool_headers = TMPL_POOL_DEF_HEADERS,
856 .pool_len = TMPL_POOL_DEF_LEN
857 });
858
860 .name = "break",
861 .type = UNLANG_TYPE_BREAK,
863,
864 .compile = unlang_compile_break,
865 .interpret = unlang_break,
866
867 .unlang_size = sizeof(unlang_group_t),
868 .unlang_name = "unlang_group_t",
869 });
870
872 .name = "continue",
873 .type = UNLANG_TYPE_CONTINUE,
875
876 .compile = unlang_compile_continue,
877 .interpret = unlang_continue,
878
879 .unlang_size = sizeof(unlang_group_t),
880 .unlang_name = "unlang_group_t",
881 });
882}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_EXECUTE_NEXT
Execute the next unlang_t.
Definition action.h:38
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
#define RCSID(id)
Definition build.h:485
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define RULES_VERIFY(_cs, _rules)
Definition cf_file.c:180
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:737
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
char const * cf_section_argv(CONF_SECTION const *cs, int argc)
Return variadic argument at the specified index.
Definition cf_util.c:1212
fr_token_t cf_section_name2_quote(CONF_SECTION const *cs)
Return the quoting of the name2 identifier.
Definition cf_util.c:1229
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_item_next(_parent, _curr)
Definition cf_util.h:92
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
unlang_t * unlang_compile_children(unlang_group_t *g, unlang_compile_ctx_t *unlang_ctx_in)
Definition compile.c:1288
unlang_t * unlang_compile_empty(unlang_t *parent, UNUSED unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
Definition compile.c:1196
unlang_group_t * unlang_group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_type_t type)
Definition compile.c:447
int unlang_define_local_variable(CONF_ITEM *ci, unlang_variable_t *var, tmpl_rules_t *t_rules, fr_type_t type, char const *name, fr_dict_attr_t const *ref)
Definition compile.c:1885
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:290
#define fr_dcursor_init(_cursor, _head)
Initialise a cursor.
Definition dcursor.h:710
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
#define MEM(x)
Definition debug.h:36
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3338
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2475
fr_dict_t * fr_dict_protocol_alloc(fr_dict_t const *parent)
Allocate a new local dictionary.
Definition dict_util.c:4077
static int unlang_foreach_pair_copy(fr_pair_t *to, fr_pair_t *from, fr_dict_attr_t const *from_parent)
Definition foreach.c:92
uint32_t index
for xlat results
Definition foreach.c:49
static void unlang_foreach_attr_key_update(UNUSED request_t *request, unlang_frame_state_foreach_t *state)
Definition foreach.c:266
char * buffer
for key values
Definition foreach.c:50
unlang_result_t exp_result
for xlat expansion
Definition foreach.c:52
static unlang_t * unlang_compile_break(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition foreach.c:792
static unlang_action_t unlang_foreach(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:467
fr_dcursor_t cursor
Used to track our place in the list.
Definition foreach.c:44
tmpl_t const * vpt
pointer to the vpt
Definition foreach.c:47
fr_pair_t * key
local variable which contains the key
Definition foreach.c:45
static unlang_action_t unlang_foreach_attr_init(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame, unlang_frame_state_foreach_t *state)
Definition foreach.c:379
static unlang_action_t unlang_foreach_xlat_expanded(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:207
fr_value_box_list_t list
value box list for looping over xlats
Definition foreach.c:53
#define BUFFER_SIZE
Definition foreach.c:37
static unlang_action_t unlang_foreach_xlat_init(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame, unlang_frame_state_foreach_t *state)
Definition foreach.c:246
fr_pair_t * value
local variable which contains the value
Definition foreach.c:46
tmpl_dcursor_ctx_t cc
tmpl cursor state
Definition foreach.c:55
int indent
for catching indentation issues
Definition foreach.c:58
request_t * request
The current request.
Definition foreach.c:43
static unlang_action_t unlang_foreach_xlat_next(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:179
static unlang_action_t unlang_continue(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:544
static unlang_action_t unlang_break(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:519
static unlang_action_t unlang_foreach_attr_next(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:289
static unlang_t * unlang_compile_continue(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition foreach.c:817
static unlang_t * unlang_compile_foreach(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition foreach.c:553
void unlang_foreach_init(void)
Definition foreach.c:842
static int unlang_foreach_xlat_key_update(request_t *request, unlang_frame_state_foreach_t *state)
Definition foreach.c:160
static int _free_unlang_frame_state_foreach(unlang_frame_state_foreach_t *state)
Ensure request data is pulled out of the request if the frame is popped.
Definition foreach.c:133
State of a foreach loop.
Definition foreach.c:42
static unlang_foreach_t * unlang_group_to_foreach(unlang_group_t *g)
Cast a group structure to the foreach keyword extension.
fr_dict_attr_t const * value
value variable in the foreach loop
fr_dict_attr_t const * key
key variable for the foreach loop
unlang_action_t unlang_interpret_push_children(unlang_result_t *p_result, request_t *request, rlm_rcode_t default_rcode, bool do_next_sibling)
Push the children of the current frame onto a new frame onto the stack.
Definition interpret.c:384
#define RPEDEBUG(fmt,...)
Definition log.h:376
unlang_op_t unlang_ops[UNLANG_TYPE_MAX]
Different operations the interpreter can execute.
Definition base.c:31
static TALLOC_CTX * unlang_ctx
Definition base.c:71
void unlang_register(unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:56
talloc_free(reap)
static char * stack[MAX_STACK]
Definition radmin.c:159
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition pair.c:1462
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2319
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:698
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:1343
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:493
fr_pair_t * fr_pair_afrom_child_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
Create a new valuepair.
Definition pair.c:375
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:81
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG(fmt,...)
Definition radclient.h:53
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:41
#define RETURN_UNLANG_NOOP
Definition rcode.h:65
Declarations for the "return" keyword, used to implement other keywords.
static char const * name
#define FR_SBUFF_IN_STR(_start)
#define FR_SBUFF_OUT(_start, _len_or_end)
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:885
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:227
#define tmpl_is_xlat(vpt)
Definition tmpl.h:210
static bool tmpl_attr_tail_is_unspecified(tmpl_t const *vpt)
Return true if the last attribute reference is "unspecified".
Definition tmpl.h:726
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define NUM_ALL
Definition tmpl.h:391
tmpl_rules_t const * parent
for parent / child relationships
Definition tmpl.h:333
#define tmpl_xlat(_tmpl)
Definition tmpl.h:930
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
static fr_slen_t vpt
Definition tmpl.h:1269
#define TMPL_POOL_DEF_HEADERS
Define manipulation functions for the attribute reference list.
Definition tmpl.h:486
#define NUM_UNSPEC
Definition tmpl.h:390
void tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t num)
Rewrite the leaf's instance number.
#define TMPL_POOL_DEF_LEN
How many additional bytes to allocate in a pool for a tmpl_t.
Definition tmpl.h:491
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
fr_aka_sim_id_type_t type
fr_pair_t * vp
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
uint8_t allow_wildcard
Allow the special case of .
Definition tmpl.h:309
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:301
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_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:514
ssize_t tmpl_dcursor_print(fr_sbuff_t *out, tmpl_dcursor_ctx_t const *cc)
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
enum fr_token fr_token_t
@ T_BARE_WORD
Definition token.h:120
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:270
#define XLAT_RESULT_SUCCESS(_p_result)
Definition xlat.h:503
fr_type_t xlat_data_type(xlat_exp_head_t const *head)
unlang_result_t section_result
The aggregate result of executing all siblings in this section.
static unlang_action_t unwind_to_op_flag(unsigned int *depth_p, unlang_stack_t *stack, unlang_op_flag_t flag)
Mark the entire stack as cancelled.
#define UNLANG_NEXT_SIBLING
static void repeatable_clear(unlang_stack_frame_t *frame)
char const * debug_name
Printed in log messages when the node is executed.
void * state
Stack frame specialisations.
tmpl_rules_t const * rules
unlang_t * parent
Previous node.
fr_dict_attr_t const * root
the root of our dictionary
static void unlang_compile_ctx_copy(unlang_compile_ctx_t *dst, unlang_compile_ctx_t const *src)
static unlang_t * unlang_group_to_generic(unlang_group_t const *p)
#define UNLANG_IGNORE
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
char const * section_name1
char const * name
Unknown...
@ UNLANG_TYPE_CONTINUE
Break statement (within a UNLANG_TYPE_FOREACH).
Definition unlang_priv.h:64
@ UNLANG_TYPE_BREAK
Break statement (within a UNLANG_TYPE_FOREACH or UNLANG_TYPE_CASE).
Definition unlang_priv.h:63
@ UNLANG_TYPE_FOREACH
Foreach section.
Definition unlang_priv.h:62
static void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process)
Mark the current stack frame up for repeat, and set a new process function.
unlang_t const * instruction
The unlang node we're evaluating.
unlang_variable_t * variables
rarely used, so we don't usually need it
char const * name
Name of the keyword.
int max_attr
1..N local attributes have been defined
fr_dict_t * dict
our dictionary
@ UNLANG_OP_FLAG_RETURN_POINT
Return point.
@ UNLANG_OP_FLAG_SINGLE_WORD
the operation is parsed and compiled as a single word
@ UNLANG_OP_FLAG_CONTINUE_POINT
Continue point.
@ UNLANG_OP_FLAG_DEBUG_BRACES
Print debug braces.
@ UNLANG_OP_FLAG_BREAK_POINT
Break point.
static void repeatable_set(unlang_stack_frame_t *frame)
unlang_process_t process
function to call for interpreting this stack frame
unlang_type_t type
The specialisation of this node.
char const * section_name2
unlang_op_flag_t flag
Interpreter flags for this operation.
Generic representation of a grouping.
An unlang operation.
A node in a graph of unlang_op_t (s) that we execute.
Our interpreter stack, as distinct from the C stack.
An unlang stack associated with a request.
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:263
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
static fr_slen_t parent
Definition pair.h:841
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
#define fr_type_is_structural(_x)
Definition types.h:390
#define fr_type_is_numeric(_x)
Definition types.h:380
#define fr_type_is_leaf(_x)
Definition types.h:391
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:452
#define DOC_KEYWORD_REF(_x)
Definition version.h:89
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3741
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4156
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4093
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4375
#define fr_value_box(_box, _var, _tainted)
Automagically fill in a box, determining the value type from the type of the C variable.
Definition value.h:897
String expansion ("translation").