The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 07657d98ee7ae7d73f7337513ee5819d7d71c4b6 $
19  *
20  * @file unlang/foreach.c
21  * @brief Unlang "foreach" keyword evaluation.
22  *
23  * @copyright 2006-2019 The FreeRADIUS server project
24  */
25 RCSID("$Id: 07657d98ee7ae7d73f7337513ee5819d7d71c4b6 $")
26 
27 #include <freeradius-devel/server/request_data.h>
28 #include <freeradius-devel/unlang/xlat_func.h>
29 
30 #include "foreach_priv.h"
31 #include "return_priv.h"
32 #include "xlat_priv.h"
33 
34 static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
35  "Foreach-Variable-1",
36  "Foreach-Variable-2",
37  "Foreach-Variable-3",
38  "Foreach-Variable-4",
39  "Foreach-Variable-5",
40  "Foreach-Variable-6",
41  "Foreach-Variable-7",
42  "Foreach-Variable-8",
43  "Foreach-Variable-9"};
44 
45 static int xlat_foreach_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; /* up to 10 for foreach */
46 
47 /** State of a foreach loop
48  *
49  */
50 typedef struct {
51  request_t *request; //!< The current request.
52  fr_dcursor_t cursor; //!< Used to track our place in the list
53  ///< we're iterating over.
54  fr_pair_list_t vps; //!< List containing the attribute(s) we're
55  ///< iterating over.
56  int depth; //!< Level of nesting of this foreach loop.
57 #ifndef NDEBUG
58  int indent; //!< for catching indentation issues
59 #endif
61 
62 static xlat_action_t unlang_foreach_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
63  xlat_ctx_t const *xctx,
64  request_t *request, UNUSED fr_value_box_list_t *in);
65 
66 #define FOREACH_REQUEST_DATA (void *)unlang_foreach_xlat
67 
68 /** Ensure request data is pulled out of the request if the frame is popped
69  *
70  */
72 {
74 
75  return 0;
76 }
77 
79 {
80  unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
81  fr_pair_t *vp;
82 
84 
85  vp = fr_dcursor_next(&state->cursor);
86 
87  /*
88  * Skip any non-leaf attributes - adds sanity to foreach &request.[*]
89  */
90  while (vp) {
91  switch (vp->vp_type) {
92  case FR_TYPE_LEAF:
93  break;
94  default:
95  vp = fr_dcursor_next(&state->cursor);
96  continue;
97  }
98  break;
99  }
100 
101  if (!vp) {
102  *p_result = frame->result;
103 #ifndef NDEBUG
104  fr_assert(state->indent == request->log.indent.unlang);
105 #endif
107  }
108 
109 #ifndef NDEBUG
110  RDEBUG2("# looping with: Foreach-Variable-%d = %pV", state->depth, &vp->data);
111 #endif
112 
113  repeatable_set(frame);
114 
115  /*
116  * Push the child, and yield for a later return.
117  */
118  return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
119 }
120 
121 
123 {
124  unlang_stack_t *stack = request->stack;
128 
129  int i, depth = 0;
130  fr_pair_list_t vps;
131  fr_pair_t *vp;
132 
133  fr_pair_list_init(&vps);
134 
135  /*
136  * Ensure any breaks terminate here...
137  */
138  break_point_set(frame);
139 
140  /*
141  * Figure out foreach depth by walking back up the stack
142  */
143  if (stack->depth > 0) for (i = (stack->depth - 1); i >= 0; i--) {
144  unlang_t const *our_instruction;
145  our_instruction = stack->frame[i].instruction;
146  if (!our_instruction || (our_instruction->type != UNLANG_TYPE_FOREACH)) continue;
147  depth++;
148  }
149 
150  if (depth >= (int)NUM_ELEMENTS(xlat_foreach_names)) {
151  REDEBUG("foreach Nesting too deep!");
152  *p_result = RLM_MODULE_FAIL;
154  }
155 
156  MEM(frame->state = state = talloc_zero(request->stack, unlang_frame_state_foreach_t));
157  fr_pair_list_init(&state->vps);
158 
159  /*
160  * Copy the VPs from the original request, this ensures deterministic
161  * behaviour if someone decides to add or remove VPs in the set we're
162  * iterating over.
163  */
164  if (tmpl_copy_pairs(frame->state, &vps, request, gext->vpt) < 0) { /* nothing to loop over */
165  *p_result = RLM_MODULE_NOOP;
167  }
168 
170 
171  fr_pair_list_append(&state->vps, &vps);
172  fr_pair_dcursor_init(&state->cursor, &state->vps);
173 
174  /*
175  * Skip any non-leaf attributes at the start of the cursor
176  * Adds sanity to foreach &request.[*]
177  */
178  vp = fr_dcursor_current(&state->cursor);
179  while (vp) {
180  switch (vp->vp_type) {
181  case FR_TYPE_LEAF:
182  break;
183  default:
184  vp = fr_dcursor_next(&state->cursor);
185  continue;
186  }
187  break;
188  }
189 
190  /*
191  * If no non-leaf attributes found clean up
192  */
193  if (!vp) {
194  fr_dcursor_free_list(&state->cursor);
195  *p_result = RLM_MODULE_NOOP;
197  }
198 
199  state->request = request;
200  state->depth = depth;
201 #ifndef NDEBUG
202  state->indent = request->log.indent.unlang;
203 #endif
204 
205  /*
206  * Add a (hopefully) faster lookup to get the state.
207  */
208  request_data_add(request, FOREACH_REQUEST_DATA, state->depth, state, false, false, false);
209 
210  talloc_set_destructor(state, _free_unlang_frame_state_foreach);
211 
212  frame->process = unlang_foreach_next;
213 
214  repeatable_set(frame);
215 
216  /*
217  * Push the child, and go process it.
218  */
219  return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
220 }
221 
223 {
224  RDEBUG2("%s", unlang_ops[frame->instruction->type].name);
225 
226  *p_result = frame->result;
227 
228  /*
229  * Stop at the next break point, or if we hit
230  * the a top frame.
231  */
232  return unwind_to_break(request->stack);
233 }
234 
235 /** Implements the Foreach-Variable-X
236  *
237  * @ingroup xlat_functions
238  */
240  xlat_ctx_t const *xctx,
241  request_t *request, UNUSED fr_value_box_list_t *in)
242 {
243  fr_pair_t *vp;
244  int const *inst = xctx->inst;
245  fr_value_box_t *vb;
247 
249  if (!state) return XLAT_ACTION_FAIL;
250 
251  vp = fr_dcursor_current(&state->cursor);
252  fr_assert(vp != NULL);
253 
254  MEM(vb = fr_value_box_alloc_null(ctx));
255  fr_value_box_copy(vb, vb, &vp->data);
256  fr_dcursor_append(out, vb);
257  return XLAT_ACTION_DONE;
258 }
259 
260 void unlang_foreach_init(TALLOC_CTX *ctx)
261 {
262  size_t i;
263 
264  for (i = 0; i < NUM_ELEMENTS(xlat_foreach_names); i++) {
265  xlat_t *x;
266 
269  fr_assert(x);
271  x->uctx = &xlat_foreach_inst[i];
272  }
273 
275  &(unlang_op_t){
276  .name = "foreach",
277  .interpret = unlang_foreach,
278  .debug_braces = true
279  });
280 
282  &(unlang_op_t){
283  .name = "break",
284  .interpret = unlang_break,
285  });
286 }
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:405
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition: dcursor.h:659
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
static fr_slen_t in
Definition: dict.h:645
static char const *const xlat_foreach_names[]
Definition: foreach.c:34
static int xlat_foreach_inst[]
Definition: foreach.c:45
fr_dcursor_t cursor
Used to track our place in the list we're iterating over.
Definition: foreach.c:52
void unlang_foreach_init(TALLOC_CTX *ctx)
Definition: foreach.c:260
int depth
Level of nesting of this foreach loop.
Definition: foreach.c:56
static unlang_action_t unlang_foreach(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: foreach.c:122
#define FOREACH_REQUEST_DATA
Definition: foreach.c:66
static unlang_action_t unlang_break(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: foreach.c:222
int indent
for catching indentation issues
Definition: foreach.c:58
request_t * request
The current request.
Definition: foreach.c:51
static unlang_action_t unlang_foreach_next(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: foreach.c:78
fr_pair_list_t vps
List containing the attribute(s) we're iterating over.
Definition: foreach.c:54
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:71
State of a foreach loop.
Definition: foreach.c:50
static unlang_foreach_t * unlang_group_to_foreach(unlang_group_t *g)
Cast a group structure to the foreach keyword extension.
Definition: foreach_priv.h:40
static xlat_action_t unlang_foreach_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Implements the Foreach-Variable-X.
Definition: foreach.c:239
unlang_action_t unlang_interpret_push_children(rlm_rcode_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:243
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition: base.c:65
unlang_op_t unlang_ops[UNLANG_TYPE_MAX]
Different operations the interpreter can execute.
Definition: base.c:33
static char * stack[MAX_STACK]
Definition: radmin.c:158
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
Definition: request_data.c:292
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
Definition: request_data.c:339
#define request_data_add(_request, _unique_ptr, _unique_int, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
Definition: request_data.h:59
Declarations for the "return" keyword, used to implement other keywords.
int tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tmpl_t const *vpt))
Copy pairs matching a tmpl_t in the current request_t.
Definition: tmpl_eval.c:796
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
#define UNLANG_NEXT_SIBLING
Definition: unlang_priv.h:103
void * state
Stack frame specialisations.
Definition: unlang_priv.h:304
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
Definition: unlang_priv.h:539
@ UNLANG_TYPE_BREAK
Break statement (within a UNLANG_TYPE_FOREACH).
Definition: unlang_priv.h:70
@ UNLANG_TYPE_FOREACH
Foreach section.
Definition: unlang_priv.h:69
unlang_t const * instruction
The unlang node we're evaluating.
Definition: unlang_priv.h:289
static void break_point_set(unlang_stack_frame_t *frame)
Definition: unlang_priv.h:355
rlm_rcode_t result
The result from executing the instruction.
Definition: unlang_priv.h:308
char const * name
Name of the operation.
Definition: unlang_priv.h:215
static bool is_stack_unwinding_to_break(unlang_stack_t *stack)
Definition: unlang_priv.h:388
static void repeatable_set(unlang_stack_frame_t *frame)
Definition: unlang_priv.h:353
static unlang_action_t unwind_to_break(unlang_stack_t *stack)
Definition: unlang_priv.h:371
unlang_process_t process
function to call for interpreting this stack frame
Definition: unlang_priv.h:292
unlang_type_t type
The specialisation of this node.
Definition: unlang_priv.h:127
Generic representation of a grouping.
Definition: unlang_priv.h:155
An unlang operation.
Definition: unlang_priv.h:214
A node in a graph of unlang_op_t (s) that we execute.
Definition: unlang_priv.h:122
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:288
An unlang stack associated with a request.
Definition: unlang_priv.h:322
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:590
#define FR_TYPE_LEAF
Definition: types.h:297
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:3689
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:619
static size_t char ** out
Definition: value.h:984
void const * inst
xlat instance data.
Definition: xlat_ctx.h:43
An xlat calling ctx.
Definition: xlat_ctx.h:42
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition: xlat_func.c:415
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition: xlat_func.c:195
@ XLAT_FUNC_FLAG_INTERNAL
Definition: xlat_func.h:39
String expansion ("translation").
void * uctx
uctx to pass to instantiation functions.
Definition: xlat_priv.h:73