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: 3dbd687a6a373ce0672dc87d74849dbc8f59bd6c $
19 *
20 * @file unlang/foreach.c
21 * @brief Unlang "foreach" keyword evaluation.
22 *
23 * @copyright 2006-2019 The FreeRADIUS server project
24 */
25RCSID("$Id: 3dbd687a6a373ce0672dc87d74849dbc8f59bd6c $")
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 (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_IN(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 (void) fr_value_box_copy(vp, &vp->data, &state->value->data);
306 } else {
307 /*
308 * @todo - this shouldn't happen?
309 */
310 }
311 } else {
313
314 /*
315 * @todo - copy the pairs back?
316 */
317 }
318
319next:
320 vp = fr_dcursor_next(&state->cursor);
321 if (!vp) {
322#ifndef NDEBUG
323 fr_assert(state->indent == request->log.indent.unlang);
324#endif
326 }
327
328 unlang_foreach_attr_key_update(request, state);
329
330 /*
331 * Copy the data.
332 */
333 if (vp->vp_type == FR_TYPE_GROUP) {
334 fr_assert(state->value->vp_type == FR_TYPE_GROUP);
335
336 fr_pair_list_free(&state->value->vp_group);
337
338 if (fr_pair_list_copy(state->value, &state->value->vp_group, &vp->vp_group) < 0) {
339 REDEBUG("Failed copying members of %s", state->value->da->name);
341 }
342
343 } else if (fr_type_is_structural(vp->vp_type)) {
344 if (state->value->vp_type != vp->vp_type) goto next;
345
346 fr_pair_list_free(&state->value->vp_group);
347
348 if (unlang_foreach_pair_copy(state->value, vp, vp->da) < 0) {
349 REDEBUG("Failed copying children of %s", state->value->da->name);
351 }
352
353 } else {
354 fr_value_box_clear_value(&state->value->data);
355 if (fr_value_box_cast(state->value, &state->value->data, state->value->vp_type, state->value->da, &vp->data) < 0) {
356 RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pP", state->value->da->name, vp);
357 goto next;
358 }
359
360#ifndef NDEBUG
361 RDEBUG2("# looping with: %s = %pV", state->value->da->name, &vp->data);
362#endif
363 }
364
365 repeatable_set(frame);
366
367 /*
368 * Push the child, and yield for a later return.
369 */
371}
372
373/*
374 * Loop over an attribute
375 */
378{
379 fr_pair_t *vp;
380
381 /*
382 * No matching attributes, we can't do anything.
383 */
384 vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
385 if (!vp) {
386 tmpl_dcursor_clear(&state->cc);
388 }
389
390 /*
391 * Before we loop over the variables, ensure that the user can't pull the rug out from
392 * under us.
393 */
394 do {
395 if (vp->vp_edit) {
396 REDEBUG("Cannot do nested 'foreach' loops over the same attribute %pP", vp);
397 fail:
398 tmpl_dcursor_clear(&state->cc);
400 }
401
402 vp->vp_edit = true;
403 } while ((vp = fr_dcursor_next(&state->cursor)) != NULL);
404 tmpl_dcursor_clear(&state->cc);
405
406 vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
407 fr_assert(vp != NULL);
408
409next:
410 /*
411 * Update the key with the current path. Attribute indexes start at zero.
412 */
413 if (state->key && (state->key->vp_type == FR_TYPE_STRING)) unlang_foreach_attr_key_update(request, state);
414
415 if (vp->vp_type == FR_TYPE_GROUP) {
416 fr_assert(state->value->vp_type == FR_TYPE_GROUP);
417
418 if (fr_pair_list_copy(state->value, &state->value->vp_group, &vp->vp_group) < 0) {
419 REDEBUG("Failed copying members of %s", state->value->da->name);
420 goto fail;
421 }
422
423 } else if (fr_type_is_structural(vp->vp_type)) {
424 if (state->value->vp_type != vp->vp_type) {
425 vp = fr_dcursor_next(&state->cursor);
426 if (vp) goto next;
427
428 fr_assert(state->indent == request->log.indent.unlang);
430 }
431
432 if (unlang_foreach_pair_copy(state->value, vp, vp->da) < 0) {
433 REDEBUG("Failed copying children of %s", state->value->da->name);
434 goto fail;
435 }
436
437 } else {
438 fr_value_box_clear_value(&state->value->data);
439 while (vp && (fr_value_box_cast(state->value, &state->value->data, state->value->vp_type, state->value->da, &vp->data) < 0)) {
440 RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pP", state->value->da->name, vp);
441 vp = fr_dcursor_next(&state->cursor);
442 }
443
444 /*
445 * Couldn't cast anything, the loop can't be run.
446 */
447 if (!vp) {
448 tmpl_dcursor_clear(&state->cc);
450 }
451 }
452
454
455 repeatable_set(frame);
456
457 /*
458 * Push the child, and go process it.
459 */
461}
462
463
465{
469
470 MEM(frame->state = state = talloc_zero(request->stack, unlang_frame_state_foreach_t));
471 talloc_set_destructor(state, _free_unlang_frame_state_foreach);
472
473 state->request = request;
474#ifndef NDEBUG
475 state->indent = request->log.indent.unlang;
476#endif
477
478 /*
479 * Get the value.
480 */
481 fr_assert(gext->value);
482
483 state->vpt = gext->vpt;
484
485 fr_assert(fr_pair_find_by_da(&request->local_pairs, NULL, gext->value) == NULL);
486
487 /*
488 * Create the local variable and populate its value.
489 */
490 if (fr_pair_append_by_da(request->local_ctx, &state->value, &request->local_pairs, gext->value) < 0) {
491 REDEBUG("Failed creating %s", gext->value->name);
493 }
494 fr_assert(state->value != NULL);
495
496 if (gext->key) {
497 fr_assert(fr_pair_find_by_da(&request->local_pairs, NULL, gext->key) == NULL);
498
499 if (fr_pair_append_by_da(request->local_ctx, &state->key, &request->local_pairs, gext->key) < 0) {
500 REDEBUG("Failed creating %s", gext->key->name);
502 }
503 fr_assert(state->key != NULL);
504 }
505
506 if (tmpl_is_attr(gext->vpt)) {
507 MEM(state->buffer = talloc_array(state, char, BUFFER_SIZE));
508 return unlang_foreach_attr_init(p_result, request, frame, state);
509 }
510
511 fr_assert(tmpl_is_xlat(gext->vpt));
512
513 return unlang_foreach_xlat_init(p_result, request, frame, state);
514}
515
517{
519 unlang_stack_t *stack = request->stack;
520 unsigned int break_depth;
521
522 RDEBUG2("%s", unlang_ops[frame->instruction->type].name);
523
524 /*
525 * As we're unwinding intermediary frames we
526 * won't be taking their rcodes or priorities
527 * into account. We do however want to record
528 * the current section rcode.
529 */
530 *p_result = frame->section_result;
531
532 /*
533 * Stop at the next break point, or if we hit
534 * the a top frame.
535 */
536 ua = unwind_to_op_flag(&break_depth, request->stack, UNLANG_OP_FLAG_BREAK_POINT);
537 repeatable_clear(&stack->frame[break_depth]);
538 return ua;
539}
540
542{
543 unlang_stack_t *stack = request->stack;
544
545 RDEBUG2("%s", unlang_ops[frame->instruction->type].name);
546
548}
549
551{
553 fr_token_t token;
554 char const *name2;
555 char const *type_name, *variable_name;
557 unlang_t *c;
558
559 fr_type_t key_type;
560 char const *key_name;
561
563 unlang_foreach_t *gext;
564
565 ssize_t slen;
566 tmpl_t *vpt;
567 fr_dict_attr_t const *da = NULL;
568
569 tmpl_rules_t t_rules;
570 unlang_compile_ctx_t unlang_ctx2;
571
572 /*
573 * Ignore empty "foreach" blocks, and don't even sanity check their arguments.
574 */
575 if (!cf_item_next(cs, NULL)) {
576 return UNLANG_IGNORE;
577 }
578
579 /*
580 * We allow unknown attributes here.
581 */
582 t_rules = *(unlang_ctx->rules);
583 t_rules.attr.allow_unknown = true;
584 t_rules.attr.allow_wildcard = true;
585 RULES_VERIFY(&t_rules);
586
587 name2 = cf_section_name2(cs);
588 fr_assert(name2 != NULL); /* checked in cf_file.c */
589
590 /*
591 * Allocate a group for the "foreach" block.
592 */
594 if (!g) return NULL;
595
597
598 /*
599 * Create the template. If we fail, AND it's a bare word
600 * with &Foo-Bar, it MAY be an attribute defined by a
601 * module. Allow it for now. The pass2 checks below
602 * will fix it up.
603 */
604 token = cf_section_name2_quote(cs);
605 if (token != T_BARE_WORD) {
606 cf_log_err(cs, "Data being looped over in 'foreach' must be an attribute reference or dynamic expansion, not a string");
607 print_ref:
608 cf_log_err(ci, DOC_KEYWORD_REF(foreach));
609 error:
610 talloc_free(g);
611 return NULL;
612 }
613
614 slen = tmpl_afrom_substr(g, &vpt,
615 &FR_SBUFF_IN(name2, strlen(name2)),
616 token,
617 NULL,
618 &t_rules);
619 if (!vpt) {
620 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'foreach'", name2);
621 goto error;
622 }
623
624 /*
625 * If we don't have a negative return code, we must have a vpt
626 * (mostly to quiet coverity).
627 */
628 fr_assert(vpt);
629
630 if (tmpl_is_attr(vpt)) {
632 cf_log_warn(cs, "Attribute reference should be updated to use %s[*]", vpt->name);
634 }
635
637 cf_log_err(cs, "Attribute references must be of the form ...%s[*]", tmpl_attr_tail_da(vpt)->name);
638 goto print_ref;
639 }
640
641 } else if (!tmpl_contains_xlat(vpt)) {
642 cf_log_err(cs, "Invalid content in 'foreach (...)', it must be an attribute reference or a dynamic expansion");
643 goto print_ref;
644 }
645
646 gext = unlang_group_to_foreach(g);
647 gext->vpt = vpt;
648
649 c->name = "foreach";
650 MEM(c->debug_name = talloc_typed_asprintf(c, "foreach %s", name2));
651
652 /*
653 * Copy over the compilation context. This is mostly
654 * just to ensure that retry is handled correctly.
655 * i.e. reset.
656 */
657 unlang_compile_ctx_copy(&unlang_ctx2, unlang_ctx);
658
659 /*
660 * Then over-write the new compilation context.
661 */
662 unlang_ctx2.section_name1 = "foreach";
663 unlang_ctx2.section_name2 = name2;
664 unlang_ctx2.rules = &t_rules;
665 t_rules.parent = unlang_ctx->rules;
666
667 /*
668 * If we have "type name", then define a local variable of that name.
669 */
670 type_name = cf_section_argv(cs, 0); /* AFTER name1, name2 */
671
672 key_name = cf_section_argv(cs, 2);
673 if (key_name) {
674 key_type = fr_table_value_by_str(fr_type_table, key_name, FR_TYPE_VOID);
675 } else {
676 key_type = FR_TYPE_VOID;
677 }
678 key_name = cf_section_argv(cs, 3);
679
680 if (tmpl_is_xlat(vpt)) {
681 if (!type_name) {
682 cf_log_err(cs, "Dynamic expansions MUST specify a data type for the variable");
683 goto print_ref;
684 }
685
687
688 /*
689 * No data type was specified, see if we can get one from the function.
690 */
691 if (type == FR_TYPE_NULL) {
693 if (fr_type_is_leaf(type)) goto get_name;
694
695 cf_log_err(cs, "Unable to determine return data type from dynamic expansion");
696 goto print_ref;
697 }
698
699 if (!fr_type_is_leaf(type)) {
700 cf_log_err(cs, "Dynamic expansions MUST specify a non-structural data type for the variable");
701 goto print_ref;
702 }
703
704 if ((key_type != FR_TYPE_VOID) && !fr_type_is_numeric(key_type)) {
705 cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be numeric", fr_type_to_str(key_type));
706 goto print_ref;
707 }
708
709 goto get_name;
710 } else {
712
713 if ((key_type != FR_TYPE_VOID) && (key_type != FR_TYPE_STRING) && (key_type != FR_TYPE_UINT32)) {
714 cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be 'string' or 'uint32'", fr_type_to_str(key_type));
715 goto print_ref;
716 }
717 }
718
719 if (type_name) {
721
724
725 /*
726 * foreach string foo (&tlv-thing.[*]) { ... }
727 */
729 goto get_name;
730 }
731
733
734 if (type == FR_TYPE_NULL) {
735 type = da->type;
736
737 } else if (fr_type_is_leaf(type) != fr_type_is_leaf(da->type)) {
738 incompatible:
739 cf_log_err(cs, "Incompatible data types in foreach variable (%s), and reference %s being looped over (%s)",
740 fr_type_to_str(type), da->name, fr_type_to_str(da->type));
741 goto print_ref;
742
743 } else if (fr_type_is_structural(type) && (type != da->type)) {
744 goto incompatible;
745 }
746
747 get_name:
748 variable_name = cf_section_argv(cs, 1);
749
750 /*
751 * Define the local variables.
752 */
753 g->variables = var = talloc_zero(g, unlang_variable_t);
754 if (!var) goto error;
755
756 var->dict = fr_dict_protocol_alloc(unlang_ctx->rules->attr.dict_def);
757 if (!var->dict) goto error;
758
759 var->root = fr_dict_root(var->dict);
760
761 var->max_attr = 1;
762
763 if (unlang_define_local_variable(cf_section_to_item(cs), var, &t_rules, type, variable_name, da) < 0) goto error;
764
765 t_rules.attr.dict_def = var->dict;
766 t_rules.attr.namespace = NULL;
767
768 /*
769 * And ensure we have the key.
770 */
771 gext->value = fr_dict_attr_by_name(NULL, var->root, variable_name);
772 fr_assert(gext->value != NULL);
773
774 /*
775 * Define the local key variable. Note that we don't copy any children.
776 */
777 if (key_type != FR_TYPE_VOID) {
778 if (unlang_define_local_variable(cf_section_to_item(cs), var, &t_rules, key_type, key_name, NULL) < 0) goto error;
779
780 gext->key = fr_dict_attr_by_name(NULL, var->root, key_name);
781 fr_assert(gext->key != NULL);
782 }
783 }
784
785 return unlang_compile_children(g, &unlang_ctx2);
786}
787
788
790{
791 unlang_t *unlang;
792
793 for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
794 /*
795 * "break" doesn't go past a return point.
796 */
797 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
798
799 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_BREAK_POINT) != 0) break;
800 }
801
802 if (!unlang) {
803 error:
804 cf_log_err(ci, "Invalid location for 'break' - it can only be used inside 'foreach' or 'switch'");
805 cf_log_err(ci, DOC_KEYWORD_REF(break));
806 return NULL;
807 }
808
809 parent->closed = true;
810
812}
813
815{
816 unlang_t *unlang;
817
818 for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
819 /*
820 * "continue" doesn't go past a return point.
821 */
822 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
823
824 if (unlang->type == UNLANG_TYPE_FOREACH) break;
825 }
826
827 if (!unlang) {
828 error:
829 cf_log_err(ci, "Invalid location for 'continue' - it can only be used inside 'foreach'");
830 cf_log_err(ci, DOC_KEYWORD_REF(break));
831 return NULL;
832 }
833
834 parent->closed = true;
835
837}
838
840{
842 .name = "foreach",
843 .type = UNLANG_TYPE_FOREACH,
845
846 .compile = unlang_compile_foreach,
847 .interpret = unlang_foreach,
848
849 .unlang_size = sizeof(unlang_foreach_t),
850 .unlang_name = "unlang_foreach_t",
851
852 .pool_headers = TMPL_POOL_DEF_HEADERS,
853 .pool_len = TMPL_POOL_DEF_LEN
854 });
855
857 .name = "break",
858 .type = UNLANG_TYPE_BREAK,
860,
861 .compile = unlang_compile_break,
862 .interpret = unlang_break,
863
864 .unlang_size = sizeof(unlang_group_t),
865 .unlang_name = "unlang_group_t",
866 });
867
869 .name = "continue",
870 .type = UNLANG_TYPE_CONTINUE,
872
873 .compile = unlang_compile_continue,
874 .interpret = unlang_continue,
875
876 .unlang_size = sizeof(unlang_group_t),
877 .unlang_name = "unlang_group_t",
878 });
879}
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
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
#define RULES_VERIFY(_cs, _rules)
Definition cf_file.c:177
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:1324
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:1233
unlang_group_t * unlang_group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_type_t type)
Definition compile.c:456
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:1929
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:3266
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2403
fr_dict_t * fr_dict_protocol_alloc(fr_dict_t const *parent)
Allocate a new local dictionary.
Definition dict_util.c:4005
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:789
static unlang_action_t unlang_foreach(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:464
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:376
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:541
static unlang_action_t unlang_break(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition foreach.c:516
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:814
static unlang_t * unlang_compile_foreach(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition foreach.c:550
void unlang_foreach_init(void)
Definition foreach.c:839
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:389
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:1463
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:2320
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:697
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:1342
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:57
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:52
#define RETURN_UNLANG_NOOP
Definition rcode.h:63
Declarations for the "return" keyword, used to implement other keywords.
static char const * name
#define FR_SBUFF_IN(_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:282
#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
Definition unlang_priv.h:98
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:62
@ UNLANG_TYPE_BREAK
Break statement (within a UNLANG_TYPE_FOREACH or UNLANG_TYPE_CASE).
Definition unlang_priv.h:61
@ UNLANG_TYPE_FOREACH
Foreach section.
Definition unlang_priv.h:60
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:261
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:839
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:388
#define fr_type_is_numeric(_x)
Definition types.h:378
#define fr_type_is_leaf(_x)
Definition types.h:389
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:450
#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:3574
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:3962
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3899
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:4158
#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:894
String expansion ("translation").