/src/freeradius-server/src/freeradius-devel/unlang/unlang_priv.h
Line | Count | Source |
1 | | #pragma once |
2 | | /* |
3 | | * This program is free software; you can redistribute it and/or modify |
4 | | * it under the terms of the GNU General Public License as published by |
5 | | * the Free Software Foundation; either version 2, or (at your option) |
6 | | * any later version. |
7 | | * |
8 | | * This program is distributed in the hope that it will be useful, |
9 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | | * GNU General Public License for more details. |
12 | | * |
13 | | * You should have received a copy of the GNU General Public License |
14 | | * along with this program; if not, write to the Free Software Foundation, |
15 | | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * $Id: 705cd70647da95d7f4b682bc9096911833b06371 $ |
20 | | * |
21 | | * @file unlang/unlang_priv.h |
22 | | * @brief Private interpreter structures and functions |
23 | | * |
24 | | * @author Alan DeKok (aland@freeradius.org) |
25 | | * |
26 | | * @copyright 2016-2019 The FreeRADIUS server project |
27 | | */ |
28 | | #include <freeradius-devel/server/cf_util.h> /* Need CONF_* definitions */ |
29 | | #include <freeradius-devel/server/map_proc.h> |
30 | | #include <freeradius-devel/server/modpriv.h> |
31 | | #include <freeradius-devel/server/time_tracking.h> |
32 | | #include <freeradius-devel/util/debug.h> |
33 | | #include <freeradius-devel/util/dlist.h> |
34 | | #include <freeradius-devel/unlang/base.h> |
35 | | #include <freeradius-devel/unlang/map.h> |
36 | | #include <freeradius-devel/io/listen.h> |
37 | | |
38 | | #ifdef __cplusplus |
39 | | extern "C" { |
40 | | #endif |
41 | | |
42 | | /** Types of unlang_t nodes |
43 | | * |
44 | | * Here are our basic types: unlang_t, unlang_group_t, and unlang_module_t. For an |
45 | | * explanation of what they are all about, see doc/unlang/configurable_failover.adoc |
46 | | */ |
47 | | typedef enum { |
48 | | UNLANG_TYPE_NULL = 0, //!< unlang type not set. |
49 | | UNLANG_TYPE_MODULE = 1, //!< Module method. |
50 | | UNLANG_TYPE_FUNCTION, //!< Internal call to a function or submodule. |
51 | | UNLANG_TYPE_GROUP, //!< Grouping section. |
52 | | UNLANG_TYPE_REDUNDANT, //!< exactly like group, but with different default return codes |
53 | | UNLANG_TYPE_LOAD_BALANCE, //!< Load balance section. |
54 | | UNLANG_TYPE_REDUNDANT_LOAD_BALANCE, //!< Redundant load balance section. |
55 | | UNLANG_TYPE_PARALLEL, //!< execute statements in parallel |
56 | | UNLANG_TYPE_IF, //!< Condition. |
57 | | UNLANG_TYPE_ELSE, //!< !Condition. |
58 | | UNLANG_TYPE_ELSIF, //!< !Condition && Condition. |
59 | | UNLANG_TYPE_SWITCH, //!< Switch section. |
60 | | UNLANG_TYPE_CASE, //!< Case section (within a #UNLANG_TYPE_SWITCH). |
61 | | UNLANG_TYPE_FOREACH, //!< Foreach section. |
62 | | UNLANG_TYPE_BREAK, //!< Break statement (within a #UNLANG_TYPE_FOREACH or #UNLANG_TYPE_CASE). |
63 | | UNLANG_TYPE_CONTINUE, //!< Break statement (within a #UNLANG_TYPE_FOREACH). |
64 | | UNLANG_TYPE_RETURN, //!< Return statement. |
65 | | UNLANG_TYPE_MAP, //!< Mapping section (like #UNLANG_TYPE_UPDATE, but uses |
66 | | //!< values from a #map_proc_t call). |
67 | | UNLANG_TYPE_SUBREQUEST, //!< create a child subrequest |
68 | | UNLANG_TYPE_CHILD_REQUEST, //!< a frame at the top of a child's request stack used to signal the |
69 | | ///< parent when the child is complete. |
70 | | UNLANG_TYPE_DETACH, //!< detach a child |
71 | | UNLANG_TYPE_CALL, //!< call another virtual server |
72 | | UNLANG_TYPE_CALLER, //!< conditionally check parent dictionary type |
73 | | UNLANG_TYPE_TIMEOUT, //!< time-based timeouts. |
74 | | UNLANG_TYPE_LIMIT, //!< limit number of requests in a section |
75 | | UNLANG_TYPE_TRANSACTION, //!< transactions for editing lists |
76 | | UNLANG_TYPE_TRY, //!< try / catch blocks |
77 | | UNLANG_TYPE_CATCH, //!< catch a previous try |
78 | | UNLANG_TYPE_FINALLY, //!< run at the end of a virtual server. |
79 | | UNLANG_TYPE_POLICY, //!< Policy section. |
80 | | UNLANG_TYPE_XLAT, //!< Represents one level of an xlat expansion. |
81 | | UNLANG_TYPE_TMPL, //!< asynchronously expand a tmpl_t |
82 | | UNLANG_TYPE_EDIT, //!< edit VPs in place. After 20 years! |
83 | | UNLANG_TYPE_MAX |
84 | | } unlang_type_t; |
85 | | |
86 | | /** Allows the frame evaluator to signal the interpreter |
87 | | * |
88 | | */ |
89 | | typedef enum { |
90 | | UNLANG_FRAME_ACTION_POP = 1, //!< Pop the current frame, and check the next one further |
91 | | ///< up in the stack for what to do next. |
92 | | UNLANG_FRAME_ACTION_RETRY, //!< retry the current frame |
93 | | UNLANG_FRAME_ACTION_NEXT, //!< Process the next instruction at this level. |
94 | | UNLANG_FRAME_ACTION_YIELD //!< Temporarily return control back to the caller on the C |
95 | | ///< stack. |
96 | | } unlang_frame_action_t; |
97 | | |
98 | 0 | #define UNLANG_NEXT_STOP (false) |
99 | 0 | #define UNLANG_NEXT_SIBLING (true) |
100 | | |
101 | | #define UNLANG_DETACHABLE (true) |
102 | | #define UNLANG_NORMAL_CHILD (false) |
103 | | |
104 | | DIAG_OFF(attributes) |
105 | | typedef enum CC_HINT(flag_enum) { |
106 | | UNLANG_FRAME_FLAG_NONE = 0x00, //!< No flags. |
107 | | UNLANG_FRAME_FLAG_REPEAT = 0x01, //!< Repeat the frame on the way up the stack. |
108 | | UNLANG_FRAME_FLAG_TOP_FRAME = 0x02, //!< are we the top frame of the stack? |
109 | | ///< If true, causes the interpreter to stop |
110 | | ///< interpreting and return, control then passes |
111 | | ///< to whatever called the interpreter. |
112 | | UNLANG_FRAME_FLAG_YIELDED = 0x04, //!< frame has yielded |
113 | | UNLANG_FRAME_FLAG_UNWIND = 0x08, //!< This frame should be unwound without evaluation. |
114 | | } unlang_frame_flag_t; |
115 | | DIAG_ON(attributes) |
116 | | |
117 | | typedef struct unlang_s unlang_t; |
118 | | typedef struct unlang_stack_frame_s unlang_stack_frame_t; |
119 | | |
120 | | FR_DLIST_TYPES(unlang_list) |
121 | | FR_DLIST_TYPEDEFS(unlang_list, unlang_list_t, unlang_entry_t) |
122 | | |
123 | | /** A node in a graph of #unlang_op_t (s) that we execute |
124 | | * |
125 | | * The interpreter acts like a turing machine, with #unlang_t nodes forming the tape |
126 | | * and the #unlang_action_t the instructions. |
127 | | * |
128 | | * This is the parent 'class' for multiple #unlang_t node specialisations. |
129 | | * The #unlang_t struct is listed first in the specialisation so that we can cast between |
130 | | * parent/child classes without knowledge of the layout of the structures. |
131 | | * |
132 | | * The specialisations of the nodes describe additional details of the operation to be performed. |
133 | | */ |
134 | | struct unlang_s { |
135 | | unlang_t *parent; //!< Previous node. |
136 | | unlang_list_t *list; //!< so we have fewer run-time dereferences |
137 | | unlang_entry_t entry; //!< next / prev entries |
138 | | char const *name; //!< Unknown... |
139 | | char const *debug_name; //!< Printed in log messages when the node is executed. |
140 | | unlang_type_t type; //!< The specialisation of this node. |
141 | | bool closed; //!< whether or not this section is closed to new statements |
142 | | bool add_filename; //!< add the filename when printing in debug mode |
143 | | CONF_ITEM *ci; //!< used to generate this item |
144 | | unsigned int number; //!< unique node number |
145 | | unlang_mod_actions_t actions; //!< Priorities, etc. for the various return codes. |
146 | | }; |
147 | | |
148 | | FR_DLIST_FUNCS(unlang_list, unlang_t, entry) |
149 | | #define unlang_list_foreach(_list_head, _iter) fr_dlist_foreach(unlang_list_dlist_head(_list_head), unlang_t, _iter) |
150 | | |
151 | | typedef struct { |
152 | | fr_dict_t *dict; //!< our dictionary |
153 | | fr_dict_attr_t const *root; //!< the root of our dictionary |
154 | | int max_attr; //!< 1..N local attributes have been defined |
155 | | } unlang_variable_t; |
156 | | |
157 | | /** Generic representation of a grouping |
158 | | * |
159 | | * Can represent IF statements, maps, update sections etc... |
160 | | */ |
161 | | typedef struct { |
162 | | unlang_t self; |
163 | | |
164 | | CONF_SECTION *cs; |
165 | | unlang_list_t children; |
166 | | |
167 | | unlang_variable_t *variables; //!< rarely used, so we don't usually need it |
168 | | } unlang_group_t; |
169 | | |
170 | | /** A naked xlat |
171 | | * |
172 | | * @note These are vestigial and may be removed in future. |
173 | | */ |
174 | | typedef struct { |
175 | | unlang_t self; |
176 | | tmpl_t const *tmpl; |
177 | | } unlang_tmpl_t; |
178 | | |
179 | | /** Function to call when interpreting a frame |
180 | | * |
181 | | * @param[in,out] p_result Pointer to the current rcode, may be modified by the function. |
182 | | * @param[in] request The current request. |
183 | | * @param[in] frame being executed. |
184 | | * |
185 | | * @return an action for the interpreter to perform. |
186 | | */ |
187 | | typedef unlang_action_t (*unlang_process_t)(unlang_result_t *p_result, request_t *request, |
188 | | unlang_stack_frame_t *frame); |
189 | | |
190 | | /** Function to call if the request was signalled |
191 | | * |
192 | | * This is the instruction specific cancellation function. |
193 | | * This function will usually either call a more specialised cancellation function |
194 | | * set when something like a module yielded, or just cleanup the state of the original |
195 | | * #unlang_process_t. |
196 | | * |
197 | | * @param[in] request The current request. |
198 | | * @param[in] frame being signalled. |
199 | | * @param[in] action We're being signalled with. |
200 | | */ |
201 | | typedef void (*unlang_signal_t)(request_t *request, |
202 | | unlang_stack_frame_t *frame, fr_signal_t action); |
203 | | |
204 | | /** Custom callback for dumping information about frame state |
205 | | * |
206 | | * @param[in] request The current request. |
207 | | * @param[in] frame to provide additional information for. |
208 | | */ |
209 | | typedef void (*unlang_dump_t)(request_t *request, unlang_stack_frame_t *frame); |
210 | | |
211 | | typedef int (*unlang_thread_instantiate_t)(unlang_t const *instruction, void *thread_inst); |
212 | | |
213 | | typedef struct { |
214 | | virtual_server_t const *vs; //!< Virtual server we're compiling in the context of. |
215 | | ///< This shouldn't change during the compilation of |
216 | | ///< a single unlang section. |
217 | | char const *section_name1; |
218 | | char const *section_name2; |
219 | | unlang_mod_actions_t actions; |
220 | | tmpl_rules_t const *rules; |
221 | | } unlang_compile_ctx_t; |
222 | | |
223 | | typedef unlang_t *(*unlang_compile_t)(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci); |
224 | | |
225 | 0 | #define UNLANG_IGNORE ((unlang_t *) -1) |
226 | | |
227 | | unlang_t *unlang_compile_empty(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type); |
228 | | |
229 | | unlang_t *unlang_compile_section(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type); |
230 | | |
231 | | unlang_t *unlang_compile_children(unlang_group_t *g, unlang_compile_ctx_t *unlang_ctx); |
232 | | |
233 | | unlang_group_t *unlang_group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_type_t type); |
234 | | |
235 | | int unlang_define_local_variable(CONF_ITEM *ci, unlang_variable_t *var, tmpl_rules_t *t_rules, fr_type_t type, char const *name, |
236 | | fr_dict_attr_t const *ref); |
237 | | |
238 | | bool unlang_compile_limit_subsection(CONF_SECTION *cs, char const *name); |
239 | | |
240 | | /* |
241 | | * @todo - These functions should be made private once all of they keywords have been moved to foo(args) syntax. |
242 | | */ |
243 | | bool pass2_fixup_tmpl(UNUSED TALLOC_CTX *ctx, tmpl_t **vpt_p, CONF_ITEM const *ci, fr_dict_t const *dict); |
244 | | bool pass2_fixup_map(map_t *map, tmpl_rules_t const *rules, fr_dict_attr_t const *parent); |
245 | | bool pass2_fixup_update(unlang_group_t *g, tmpl_rules_t const *rules); |
246 | | bool pass2_fixup_map_rhs(unlang_group_t *g, tmpl_rules_t const *rules); |
247 | | |
248 | | /* |
249 | | * When we switch to a new unlang ctx, we use the new component |
250 | | * name and number, but we use the CURRENT actions. |
251 | | */ |
252 | | static inline CC_HINT(always_inline) |
253 | | void unlang_compile_ctx_copy(unlang_compile_ctx_t *dst, unlang_compile_ctx_t const *src) |
254 | 0 | { |
255 | 0 | #ifndef NDEBUG |
256 | 0 | int i; |
257 | 0 | #endif |
258 | |
|
259 | 0 | *dst = *src; |
260 | |
|
261 | 0 | #ifndef NDEBUG |
262 | | /* |
263 | | * Ensure that none of the actions are RETRY. The actions { ... } section is applied to the |
264 | | * instruction, and not to the unlang_compile_ctx_t |
265 | | */ |
266 | 0 | for (i = 0; i < RLM_MODULE_NUMCODES; i++) { |
267 | 0 | fr_assert(dst->actions.actions[i] != MOD_ACTION_RETRY); |
268 | 0 | fr_assert(MOD_ACTION_VALID(dst->actions.actions[i])); |
269 | 0 | } |
270 | 0 | #endif |
271 | 0 | } Unexecuted instantiation: edit.c:unlang_compile_ctx_copy Unexecuted instantiation: finally.c:unlang_compile_ctx_copy Unexecuted instantiation: foreach.c:unlang_compile_ctx_copy |
272 | | |
273 | | |
274 | | #ifndef NDEBUG |
275 | | static inline CC_HINT(always_inline) int unlang_attr_rules_verify(tmpl_attr_rules_t const *rules) |
276 | 0 | { |
277 | 0 | if (!fr_cond_assert_msg(rules->dict_def, "No protocol dictionary set")) return -1; |
278 | 0 | if (!fr_cond_assert_msg(rules->dict_def != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1; |
279 | 0 | if (!fr_cond_assert_msg(!rules->allow_foreign, "rules->attr.allow_foreign must be false")) return -1; |
280 | 0 |
|
281 | 0 | return 0; |
282 | 0 | } Unexecuted instantiation: edit.c:unlang_attr_rules_verify Unexecuted instantiation: finally.c:unlang_attr_rules_verify |
283 | | |
284 | | static inline CC_HINT(always_inline) int unlang_rules_verify(tmpl_rules_t const *rules) |
285 | 0 | { |
286 | 0 | if (!fr_cond_assert_msg(!rules->at_runtime, "rules->at_runtime must be false")) return -1; |
287 | 0 | return unlang_attr_rules_verify(&rules->attr); |
288 | 0 | } Unexecuted instantiation: edit.c:unlang_rules_verify Unexecuted instantiation: finally.c:unlang_rules_verify |
289 | | |
290 | 0 | #define RULES_VERIFY(_rules) do { if (unlang_rules_verify(_rules) < 0) return NULL; } while (0) |
291 | | #else |
292 | | #define RULES_VERIFY(_rules) |
293 | | #endif |
294 | | |
295 | | DIAG_OFF(attributes) |
296 | | typedef enum CC_HINT(flag_enum) { |
297 | | UNLANG_OP_FLAG_NONE = 0x00, //!< No flags. |
298 | | UNLANG_OP_FLAG_DEBUG_BRACES = 0x01, //!< Print debug braces. |
299 | | UNLANG_OP_FLAG_RCODE_SET = 0x02, //!< Set request->rcode to the result of this operation. |
300 | | UNLANG_OP_FLAG_NO_FORCE_UNWIND = 0x04, //!< Must not be cancelled. |
301 | | ///< @Note Slightly confusingly, a cancellation signal |
302 | | ///< can still be delivered to a frame that is not |
303 | | ///< cancellable, but the frame won't be automatically |
304 | | ///< unwound. This lets the frame know that cancellation |
305 | | ///< is desired, but can be ignored. |
306 | | UNLANG_OP_FLAG_BREAK_POINT = 0x08, //!< Break point. |
307 | | UNLANG_OP_FLAG_RETURN_POINT = 0x10, //!< Return point. |
308 | | UNLANG_OP_FLAG_CONTINUE_POINT = 0x20, //!< Continue point. |
309 | | |
310 | | UNLANG_OP_FLAG_SINGLE_WORD = 0x1000, //!< the operation is parsed and compiled as a single word |
311 | | UNLANG_OP_FLAG_INTERNAL = 0x2000, //!< it's not a real keyword |
312 | | |
313 | | } unlang_op_flag_t; |
314 | | DIAG_ON(attributes) |
315 | | |
316 | | /** An unlang operation |
317 | | * |
318 | | * These are like the opcodes in other interpreters. Each operation, when executed |
319 | | * will return an #unlang_action_t, which determines what the interpreter does next. |
320 | | */ |
321 | | typedef struct { |
322 | | char const *name; //!< Name of the keyword |
323 | | unlang_type_t type; //!< enum value for the keyword |
324 | | |
325 | | unlang_compile_t compile; //!< compile the keyword |
326 | | |
327 | | unlang_process_t interpret; //!< Function to interpret the keyword |
328 | | |
329 | | unlang_signal_t signal; //!< Function to signal stop / dup / whatever |
330 | | |
331 | | unlang_dump_t dump; //!< Dump additional information about the frame state. |
332 | | |
333 | | size_t unlang_size; //!< Total length of the unlang_t + specialisation struct. |
334 | | char const *unlang_name; //!< Talloc type name for the unlang_t |
335 | | |
336 | | unsigned pool_headers; //!< How much additional space to allocate for chunk headers. |
337 | | size_t pool_len; //!< How much additional space to allocate for chunks |
338 | | |
339 | | |
340 | | unlang_thread_instantiate_t thread_instantiate; //!< per-thread instantiation function |
341 | | size_t thread_inst_size; |
342 | | char const *thread_inst_type; |
343 | | |
344 | | unlang_op_flag_t flag; //!< Interpreter flags for this operation. |
345 | | |
346 | | size_t frame_state_size; //!< size of instance data in the stack frame |
347 | | |
348 | | char const *frame_state_type; //!< talloc name of the frame instance data |
349 | | |
350 | | size_t frame_state_pool_objects; //!< How many sub-allocations we expect. |
351 | | |
352 | | size_t frame_state_pool_size; //!< The total size of the pool to alloc. |
353 | | } unlang_op_t; |
354 | | |
355 | | typedef struct { |
356 | | unlang_t const *instruction; //!< instruction which we're executing |
357 | | void *thread_inst; //!< thread-specific instance data |
358 | | |
359 | | bool use_forced_result; //!< Do we force a result for this module? |
360 | | unlang_result_t forced_result; //!< the result to force |
361 | | fr_timer_t *ev; //!< run on expiry |
362 | | |
363 | | #ifdef WITH_PERF |
364 | | uint64_t uses; //!< how many packets it has processed |
365 | | uint64_t active; //!< currently active in this instruction |
366 | | uint64_t yielded; //!< currently yielded |
367 | | fr_time_tracking_t tracking; //!< tracking cpu time |
368 | | #endif |
369 | | } unlang_thread_t; |
370 | | |
371 | | void *unlang_thread_instance(unlang_t const *instruction); |
372 | | |
373 | | #ifdef WITH_PERF |
374 | | void unlang_frame_perf_init(unlang_stack_frame_t *frame); |
375 | | void unlang_frame_perf_yield(unlang_stack_frame_t *frame); |
376 | | void unlang_frame_perf_resume(unlang_stack_frame_t *frame); |
377 | | void unlang_frame_perf_cleanup(unlang_stack_frame_t *frame); |
378 | | #else |
379 | | #define unlang_frame_perf_init(_x) |
380 | | #define unlang_frame_perf_yield(_x) |
381 | | #define unlang_frame_perf_resume(_x) |
382 | | #define unlang_frame_perf_cleanup(_x) |
383 | | #endif |
384 | | |
385 | | unlang_thread_t const *unlang_thread_stats(unlang_t const *instruction) CC_HINT(nonnull); |
386 | | |
387 | | #define debug_braces(_type) (unlang_ops[_type].flag & UNLANG_OP_FLAG_DEBUG_BRACES) |
388 | | |
389 | | void unlang_stack_signal(request_t *request, fr_signal_t action, int limit); |
390 | | |
391 | | typedef struct { |
392 | | request_t *request; |
393 | | int depth; //!< of this retry structure |
394 | | fr_retry_state_t state; |
395 | | uint32_t count; |
396 | | fr_timer_t *ev; |
397 | | } unlang_retry_t; |
398 | | |
399 | | /** Our interpreter stack, as distinct from the C stack |
400 | | * |
401 | | * We don't call the modules recursively. Instead we iterate over a list of #unlang_t and |
402 | | * and manage the call stack ourselves. |
403 | | * |
404 | | * After looking at various green thread implementations, it was decided that using the existing |
405 | | * unlang interpreter stack was the best way to perform async I/O. |
406 | | * |
407 | | * Each request as an unlang interpreter stack associated with it, which represents its progress |
408 | | * through the server. Because the interpreter stack is distinct from the C stack, we can have |
409 | | * a single system thread with many thousands of pending requests. |
410 | | */ |
411 | | struct unlang_stack_frame_s { |
412 | | unlang_t const *instruction; //!< The unlang node we're evaluating. |
413 | | unlang_t const *next; //!< The next unlang node we will evaluate |
414 | | |
415 | | unlang_process_t process; //!< function to call for interpreting this stack frame |
416 | | unlang_signal_t signal; //!< function to call when signalling this stack frame |
417 | | |
418 | | /** Stack frame specialisations |
419 | | * |
420 | | * These store extra (mutable) state data, for the immutable (#unlang_t) |
421 | | * instruction. Instructions can't be used to store data because they |
422 | | * might be shared between multiple threads. |
423 | | * |
424 | | * Which stack_entry specialisation to use is determined by the |
425 | | * instruction->type. |
426 | | */ |
427 | | void *state; |
428 | | |
429 | | unlang_result_t section_result; //!< The aggregate result of executing all siblings |
430 | | ///< in this section. This will be merged with the |
431 | | ///< higher stack frame's rcode when the frame is popped. |
432 | | ///< If the rcode is set to RLM_MODULE_NOT_SET when |
433 | | ///< the frame is popped, then the rcode of the frame |
434 | | ///< does not modify the rcode of the frame above it. |
435 | | |
436 | | unlang_result_t scratch_result; //!< The result of executing the current instruction. |
437 | | ///< This will be set to RLM_MODULE_NOT_SET, and |
438 | | ///< MOD_ACTION_NOT_SET when a new instruction is set |
439 | | ///< for the frame. If p_result does not point to this |
440 | | ///< field, the rcode and priority returned will be |
441 | | ///< left as NOT_SET and will be ignored. |
442 | | ///< This values here will persist between yields. |
443 | | |
444 | | unlang_result_t *p_result; //!< Where to write the result of executing the current |
445 | | ///< instruction. Will either point to `scratch_result`, |
446 | | ///< OR if the parent does not want its rcode to be updated |
447 | | ///< by a child it pushed for evaluation, it will point to |
448 | | ///< memory in the parent's frame state, so that the parent |
449 | | ///< can manually process the rcode. |
450 | | |
451 | | unlang_retry_t *retry; //!< if the frame is being retried. |
452 | | |
453 | | |
454 | | rindent_t indent; //!< Indent level of the request when the frame was |
455 | | ///< created. This is used to restore the indent |
456 | | ///< level when the stack is being forcefully unwound. |
457 | | |
458 | | unlang_frame_flag_t flag; //!< Flags that mark up the frame for various things |
459 | | ///< such as being the point where break, return or |
460 | | ///< continue stop, or for forced unwinding. |
461 | | |
462 | | struct { //!< reference to a previous frame |
463 | | uint8_t frame_break; //!< previous "break" frame |
464 | | uint8_t frame_continue; //!< previous "continue" frame |
465 | | uint8_t frame_return; //!< previous "return" frame |
466 | | uint8_t frame_load_balance; //!< previous "load-balance" frame |
467 | | uint8_t frame_call; //!< previous "call" frame |
468 | | } prev; |
469 | | |
470 | | #ifdef WITH_PERF |
471 | | fr_time_tracking_t tracking; //!< track this instance of this instruction |
472 | | #endif |
473 | | }; |
474 | | |
475 | | /** An unlang stack associated with a request |
476 | | * |
477 | | */ |
478 | | typedef struct { |
479 | | unlang_interpret_t *intp; //!< Interpreter that the request is currently |
480 | | ///< associated with. |
481 | | |
482 | | int depth; //!< Current depth we're executing at. |
483 | | uint8_t unwind; //!< Unwind to this frame if it exists. |
484 | | ///< This is used for break and return. |
485 | | unlang_stack_frame_t frame[UNLANG_STACK_MAX]; //!< The stack... |
486 | | } unlang_stack_t; |
487 | | |
488 | | /** Different operations the interpreter can execute |
489 | | */ |
490 | | extern unlang_op_t unlang_ops[]; |
491 | | extern fr_hash_table_t *unlang_op_table; |
492 | | |
493 | | #define MOD_NUM_TYPES (UNLANG_TYPE_XLAT + 1) |
494 | | |
495 | | extern fr_table_num_sorted_t const mod_rcode_table[]; |
496 | | extern size_t mod_rcode_table_len; |
497 | | |
498 | 0 | static inline void repeatable_set(unlang_stack_frame_t *frame) { frame->flag |= UNLANG_FRAME_FLAG_REPEAT; }Unexecuted instantiation: edit.c:repeatable_set Unexecuted instantiation: finally.c:repeatable_set Unexecuted instantiation: foreach.c:repeatable_set |
499 | 0 | static inline void top_frame_set(unlang_stack_frame_t *frame) { frame->flag |= UNLANG_FRAME_FLAG_TOP_FRAME; }Unexecuted instantiation: edit.c:top_frame_set Unexecuted instantiation: finally.c:top_frame_set Unexecuted instantiation: foreach.c:top_frame_set |
500 | 0 | static inline void yielded_set(unlang_stack_frame_t *frame) { frame->flag |= UNLANG_FRAME_FLAG_YIELDED; }Unexecuted instantiation: edit.c:yielded_set Unexecuted instantiation: finally.c:yielded_set Unexecuted instantiation: foreach.c:yielded_set |
501 | 0 | static inline void unwind_set(unlang_stack_frame_t *frame) { frame->flag |= UNLANG_FRAME_FLAG_UNWIND; }Unexecuted instantiation: edit.c:unwind_set Unexecuted instantiation: finally.c:unwind_set Unexecuted instantiation: foreach.c:unwind_set |
502 | | |
503 | 0 | static inline void repeatable_clear(unlang_stack_frame_t *frame) { frame->flag &= ~UNLANG_FRAME_FLAG_REPEAT; }Unexecuted instantiation: edit.c:repeatable_clear Unexecuted instantiation: finally.c:repeatable_clear Unexecuted instantiation: foreach.c:repeatable_clear |
504 | 0 | static inline void top_frame_clear(unlang_stack_frame_t *frame) { frame->flag &= ~UNLANG_FRAME_FLAG_TOP_FRAME; }Unexecuted instantiation: edit.c:top_frame_clear Unexecuted instantiation: finally.c:top_frame_clear Unexecuted instantiation: foreach.c:top_frame_clear |
505 | 0 | static inline void yielded_clear(unlang_stack_frame_t *frame) { frame->flag &= ~UNLANG_FRAME_FLAG_YIELDED; }Unexecuted instantiation: edit.c:yielded_clear Unexecuted instantiation: finally.c:yielded_clear Unexecuted instantiation: foreach.c:yielded_clear |
506 | 0 | static inline void unwind_clear(unlang_stack_frame_t *frame) { frame->flag &= ~UNLANG_FRAME_FLAG_UNWIND; }Unexecuted instantiation: edit.c:unwind_clear Unexecuted instantiation: finally.c:unwind_clear Unexecuted instantiation: foreach.c:unwind_clear |
507 | | |
508 | 0 | static inline bool is_repeatable(unlang_stack_frame_t const *frame) { return frame->flag & UNLANG_FRAME_FLAG_REPEAT; }Unexecuted instantiation: edit.c:is_repeatable Unexecuted instantiation: finally.c:is_repeatable Unexecuted instantiation: foreach.c:is_repeatable |
509 | 0 | static inline bool is_top_frame(unlang_stack_frame_t const *frame) { return frame->flag & UNLANG_FRAME_FLAG_TOP_FRAME; }Unexecuted instantiation: edit.c:is_top_frame Unexecuted instantiation: finally.c:is_top_frame Unexecuted instantiation: foreach.c:is_top_frame |
510 | 0 | static inline bool is_yielded(unlang_stack_frame_t const *frame) { return frame->flag & UNLANG_FRAME_FLAG_YIELDED; }Unexecuted instantiation: edit.c:is_yielded Unexecuted instantiation: finally.c:is_yielded Unexecuted instantiation: foreach.c:is_yielded |
511 | 0 | static inline bool is_unwinding(unlang_stack_frame_t const *frame) { return frame->flag & UNLANG_FRAME_FLAG_UNWIND; }Unexecuted instantiation: edit.c:is_unwinding Unexecuted instantiation: finally.c:is_unwinding Unexecuted instantiation: foreach.c:is_unwinding |
512 | 0 | static inline bool is_private_result(unlang_stack_frame_t const *frame) { return !(frame->p_result == &frame->section_result); }Unexecuted instantiation: edit.c:is_private_result Unexecuted instantiation: finally.c:is_private_result Unexecuted instantiation: foreach.c:is_private_result |
513 | | |
514 | 0 | static inline bool _instruction_has_debug_braces(unlang_t const *instruction) { return unlang_ops[instruction->type].flag & UNLANG_OP_FLAG_DEBUG_BRACES; }Unexecuted instantiation: edit.c:_instruction_has_debug_braces Unexecuted instantiation: finally.c:_instruction_has_debug_braces Unexecuted instantiation: foreach.c:_instruction_has_debug_braces |
515 | 0 | static inline bool _frame_has_debug_braces(unlang_stack_frame_t const *frame) { return unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_DEBUG_BRACES; }Unexecuted instantiation: edit.c:_frame_has_debug_braces Unexecuted instantiation: finally.c:_frame_has_debug_braces Unexecuted instantiation: foreach.c:_frame_has_debug_braces |
516 | | #define has_debug_braces(_thing) \ |
517 | | _Generic((_thing), \ |
518 | | unlang_t *: _instruction_has_debug_braces((unlang_t const *)(_thing)), \ |
519 | | unlang_t const *: _instruction_has_debug_braces((unlang_t const *)(_thing)), \ |
520 | | unlang_stack_frame_t *: _frame_has_debug_braces((unlang_stack_frame_t const *)(_thing)), \ |
521 | | unlang_stack_frame_t const *: _frame_has_debug_braces((unlang_stack_frame_t const *)(_thing)) \ |
522 | | ) |
523 | 0 | static inline bool is_rcode_set(unlang_stack_frame_t const *frame) { return unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_RCODE_SET; }Unexecuted instantiation: edit.c:is_rcode_set Unexecuted instantiation: finally.c:is_rcode_set Unexecuted instantiation: foreach.c:is_rcode_set |
524 | 0 | static inline bool is_cancellable(unlang_stack_frame_t const *frame) { return !(unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_NO_FORCE_UNWIND); }Unexecuted instantiation: edit.c:is_cancellable Unexecuted instantiation: finally.c:is_cancellable Unexecuted instantiation: foreach.c:is_cancellable |
525 | 0 | static inline bool is_break_point(unlang_stack_frame_t const *frame) { return unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_BREAK_POINT; }Unexecuted instantiation: edit.c:is_break_point Unexecuted instantiation: finally.c:is_break_point Unexecuted instantiation: foreach.c:is_break_point |
526 | 0 | static inline bool is_return_point(unlang_stack_frame_t const *frame) { return unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_RETURN_POINT; }Unexecuted instantiation: edit.c:is_return_point Unexecuted instantiation: finally.c:is_return_point Unexecuted instantiation: foreach.c:is_return_point |
527 | 0 | static inline bool is_continue_point(unlang_stack_frame_t const *frame) { return unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_CONTINUE_POINT; }Unexecuted instantiation: edit.c:is_continue_point Unexecuted instantiation: finally.c:is_continue_point Unexecuted instantiation: foreach.c:is_continue_point |
528 | | |
529 | | /** @name Debug functions |
530 | | * |
531 | | * @{ |
532 | | */ |
533 | | void stack_dump(request_t *request); |
534 | | void stack_dump_with_actions(request_t *request); |
535 | | /** @} */ |
536 | | |
537 | | /** Find the first frame with a given flag |
538 | | * |
539 | | * @return |
540 | | * - 0 if no frame has the flag. |
541 | | * - The index of the first frame with the flag. |
542 | | */ |
543 | | static inline unsigned int unlang_frame_by_flag(unlang_stack_t *stack, unlang_frame_flag_t flag) |
544 | 0 | { |
545 | 0 | unsigned int i; |
546 | 0 |
|
547 | 0 | for (i = stack->depth; i > 0; i--) { |
548 | 0 | unlang_stack_frame_t *frame = &stack->frame[i]; |
549 | 0 |
|
550 | 0 | if (frame->flag & flag) return i; |
551 | 0 | } |
552 | 0 | return 0; |
553 | 0 | } Unexecuted instantiation: edit.c:unlang_frame_by_flag Unexecuted instantiation: finally.c:unlang_frame_by_flag Unexecuted instantiation: foreach.c:unlang_frame_by_flag |
554 | | |
555 | | /** Find the first frame with a given flag |
556 | | * |
557 | | * @return |
558 | | * - 0 if no frame has the flag. |
559 | | * - The index of the first frame with the flag. |
560 | | */ |
561 | | static inline unsigned int unlang_frame_by_op_flag(unlang_stack_t *stack, unlang_op_flag_t flag) |
562 | 0 | { |
563 | 0 | unlang_stack_frame_t *frame; |
564 | |
|
565 | 0 | #ifndef NDEBUG |
566 | 0 | unlang_stack_frame_t *current = &stack->frame[stack->depth]; |
567 | 0 | unsigned int i; |
568 | |
|
569 | 0 | for (i = stack->depth; i > 0; i--) { |
570 | 0 | frame = &stack->frame[i]; |
571 | |
|
572 | 0 | if (unlang_ops[frame->instruction->type].flag & flag) { |
573 | 0 | switch (flag) { |
574 | 0 | default: |
575 | 0 | fr_assert(0); |
576 | 0 | break; |
577 | | |
578 | 0 | case UNLANG_OP_FLAG_BREAK_POINT: |
579 | 0 | fr_assert(frame->prev.frame_break == i); |
580 | 0 | fr_assert(current->prev.frame_break == i); |
581 | 0 | break; |
582 | | |
583 | 0 | case UNLANG_OP_FLAG_RETURN_POINT: |
584 | 0 | fr_assert(frame->prev.frame_return == i); |
585 | 0 | fr_assert(current->prev.frame_return == i); |
586 | 0 | break; |
587 | | |
588 | 0 | case UNLANG_OP_FLAG_CONTINUE_POINT: |
589 | 0 | fr_assert(frame->prev.frame_continue == i); |
590 | 0 | fr_assert(current->prev.frame_continue == i); |
591 | 0 | break; |
592 | 0 | } |
593 | | |
594 | 0 | return i; |
595 | 0 | } |
596 | | |
597 | | /* |
598 | | * Do not unwind past a top frame. |
599 | | */ |
600 | 0 | if (is_top_frame(frame)) return 0; |
601 | 0 | } |
602 | | #else |
603 | | frame = &stack->frame[stack->depth]; |
604 | | |
605 | | /* |
606 | | * No asserts in NDEBUG mode. Just return the frame, and stop looping up the stack. |
607 | | */ |
608 | | switch (flag) { |
609 | | default: |
610 | | break; |
611 | | |
612 | | case UNLANG_OP_FLAG_BREAK_POINT: |
613 | | return frame->prev.frame_break; |
614 | | |
615 | | case UNLANG_OP_FLAG_RETURN_POINT: |
616 | | return frame->prev.frame_return; |
617 | | |
618 | | case UNLANG_OP_FLAG_CONTINUE_POINT: |
619 | | return frame->prev.frame_continue; |
620 | | } |
621 | | #endif |
622 | | |
623 | 0 | return 0; |
624 | 0 | } Unexecuted instantiation: edit.c:unlang_frame_by_op_flag Unexecuted instantiation: finally.c:unlang_frame_by_op_flag Unexecuted instantiation: foreach.c:unlang_frame_by_op_flag |
625 | | |
626 | | /** Mark up frames as cancelled so they're immediately popped by the interpreter |
627 | | * |
628 | | * @note We used to do this asynchronously, but now we may need to execute timeout sections |
629 | | * which means it's not enough to pop and cleanup the stack, we need continue executing |
630 | | * the request. |
631 | | * |
632 | | * @param[in] stack The current stack. |
633 | | * @param[in] to_depth mark all frames up to and including this depth as cancelled. |
634 | | */ |
635 | | static inline unlang_action_t unwind_to_depth(unlang_stack_t *stack, unsigned int to_depth) |
636 | 0 | { |
637 | 0 | unlang_stack_frame_t *frame; |
638 | 0 | unsigned int i, depth = stack->depth; |
639 | |
|
640 | 0 | if (!fr_cond_assert(to_depth >= 1)) return UNLANG_ACTION_FAIL; |
641 | | |
642 | 0 | for (i = depth; i >= to_depth; i--) { |
643 | 0 | frame = &stack->frame[i]; |
644 | 0 | if (!is_cancellable(frame)) continue; |
645 | 0 | unwind_set(frame); |
646 | 0 | } |
647 | |
|
648 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
649 | 0 | } Unexecuted instantiation: edit.c:unwind_to_depth Unexecuted instantiation: finally.c:unwind_to_depth Unexecuted instantiation: foreach.c:unwind_to_depth |
650 | | |
651 | | /** Mark the entire stack as cancelled |
652 | | * |
653 | | * This cancels all frames up to the next "break" frame. |
654 | | * |
655 | | * @param[out] depth_p Depth of the break || return || continue point. |
656 | | * @param[in] stack The current stack. |
657 | | * @param[in] flag Flag to search for. One of: |
658 | | * - UNLANG_OP_FLAG_BREAK_POINT |
659 | | * - UNLANG_OP_FLAG_RETURN_POINT |
660 | | * - UNLANG_OP_FLAG_CONTINUE_POINT |
661 | | * @return UNLANG_ACTION_CALCULATE_RESULT |
662 | | */ |
663 | | static inline unlang_action_t unwind_to_op_flag(unsigned int *depth_p, unlang_stack_t *stack, unlang_op_flag_t flag) |
664 | 0 | { |
665 | 0 | unsigned int depth; |
666 | |
|
667 | 0 | depth = unlang_frame_by_op_flag(stack, flag); |
668 | 0 | if (depth == 0) { |
669 | 0 | if (depth_p) *depth_p = stack->depth + 1; /* Don't cancel any frames! */ |
670 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
671 | 0 | } |
672 | | |
673 | 0 | unwind_to_depth(stack, depth + 1); /* cancel UP TO the break point */ |
674 | |
|
675 | 0 | if (depth_p) *depth_p = depth; |
676 | |
|
677 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
678 | 0 | } Unexecuted instantiation: edit.c:unwind_to_op_flag Unexecuted instantiation: finally.c:unwind_to_op_flag Unexecuted instantiation: foreach.c:unwind_to_op_flag |
679 | | |
680 | | static inline unlang_stack_frame_t *frame_current(request_t *request) |
681 | 0 | { |
682 | 0 | unlang_stack_t *stack = request->stack; |
683 | 0 |
|
684 | 0 | return &stack->frame[stack->depth]; |
685 | 0 | } Unexecuted instantiation: edit.c:frame_current Unexecuted instantiation: finally.c:frame_current Unexecuted instantiation: foreach.c:frame_current |
686 | | |
687 | | static inline int stack_depth_current(request_t *request) |
688 | 0 | { |
689 | 0 | unlang_stack_t *stack = request->stack; |
690 | 0 |
|
691 | 0 | return stack->depth; |
692 | 0 | } Unexecuted instantiation: edit.c:stack_depth_current Unexecuted instantiation: finally.c:stack_depth_current Unexecuted instantiation: foreach.c:stack_depth_current |
693 | | |
694 | | /** Initialise memory and instruction for a frame when a new instruction is to be evaluated |
695 | | * |
696 | | * @note We don't change frame->p_result here, we only reset the scratch values. This is because |
697 | | * Whatever pushed the frame onto the stack generally wants the aggregate result of |
698 | | * the complete section, not just the first instruction. |
699 | | * |
700 | | * @param[in] stack the current request stack. |
701 | | * @param[in] frame frame to initialise |
702 | | */ |
703 | | static inline void frame_state_init(unlang_stack_t *stack, unlang_stack_frame_t *frame) |
704 | 0 | { |
705 | 0 | unlang_t const *instruction = frame->instruction; |
706 | 0 | unlang_op_t *op; |
707 | 0 | char const *name; |
708 | 0 |
|
709 | 0 | unlang_frame_perf_init(frame); |
710 | 0 |
|
711 | 0 | op = &unlang_ops[instruction->type]; |
712 | 0 | name = op->frame_state_type ? op->frame_state_type : __location__; |
713 | 0 |
|
714 | 0 | /* |
715 | 0 | * Reset for each instruction |
716 | 0 | */ |
717 | 0 | frame->scratch_result = UNLANG_RESULT_NOT_SET; |
718 | 0 |
|
719 | 0 | frame->process = op->interpret; |
720 | 0 | frame->signal = op->signal; |
721 | 0 |
|
722 | 0 | #ifdef HAVE_TALLOC_ZERO_POOLED_OBJECT |
723 | 0 | /* |
724 | 0 | * Pooled object |
725 | 0 | */ |
726 | 0 | if (op->frame_state_pool_size && op->frame_state_size) { |
727 | 0 | MEM(frame->state = _talloc_zero_pooled_object(stack, |
728 | 0 | op->frame_state_size, name, |
729 | 0 | op->frame_state_pool_objects, |
730 | 0 | op->frame_state_pool_size)); |
731 | 0 | } else |
732 | 0 | #endif |
733 | 0 | /* |
734 | 0 | * Pool |
735 | 0 | */ |
736 | 0 | if (op->frame_state_pool_size && !op->frame_state_size) { |
737 | 0 | MEM(frame->state = talloc_pool(stack, |
738 | 0 | op->frame_state_pool_size + |
739 | 0 | ((20 + 68 + 15) * op->frame_state_pool_objects))); /* from samba talloc.c */ |
740 | 0 | talloc_set_name_const(frame->state, name); |
741 | 0 | /* |
742 | 0 | * Object |
743 | 0 | */ |
744 | 0 | } else if (op->frame_state_size) { |
745 | 0 | MEM(frame->state = _talloc_zero(stack, op->frame_state_size, name)); |
746 | 0 | } |
747 | 0 |
|
748 | 0 | /* |
749 | 0 | * Don't change frame->retry, it may be left over from a previous retry. |
750 | 0 | */ |
751 | 0 |
|
752 | 0 | /* |
753 | 0 | * Normal frames get their "prev" pointers copied from the previous frame, and then get the break |
754 | 0 | * / continue / etc. point update, if the current instruction warrants it. |
755 | 0 | */ |
756 | 0 | if (!is_top_frame(frame)) { |
757 | 0 | /* |
758 | 0 | * A sub-frame / xlat can be the first item pushed on a stack. In that case, stack frame 0 is all zeros. |
759 | 0 | */ |
760 | 0 | fr_assert(stack->depth >= 1); |
761 | 0 | frame->prev = (frame - 1)->prev; |
762 | 0 |
|
763 | 0 | if (unlang_ops[frame->instruction->type].flag) { |
764 | 0 | /* |
765 | 0 | * TBD: A return point should (or should not?) erase any previous frame settings. |
766 | 0 | */ |
767 | 0 | if (unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_RETURN_POINT) { |
768 | 0 | frame->prev.frame_return = stack->depth; |
769 | 0 | } |
770 | 0 |
|
771 | 0 | if (unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_BREAK_POINT) { |
772 | 0 | frame->prev.frame_break = stack->depth; |
773 | 0 | } |
774 | 0 |
|
775 | 0 | if (unlang_ops[frame->instruction->type].flag & UNLANG_OP_FLAG_CONTINUE_POINT) { |
776 | 0 | frame->prev.frame_continue = stack->depth; |
777 | 0 | } |
778 | 0 |
|
779 | 0 | if ((frame->instruction->type == UNLANG_TYPE_LOAD_BALANCE) || |
780 | 0 | (frame->instruction->type == UNLANG_TYPE_REDUNDANT_LOAD_BALANCE)) { |
781 | 0 | frame->prev.frame_load_balance = stack->depth; |
782 | 0 | } |
783 | 0 | } |
784 | 0 | } else { |
785 | 0 | /* |
786 | 0 | * TBD: Top frames are also return points? |
787 | 0 | */ |
788 | 0 | frame->prev.frame_return = stack->depth; |
789 | 0 | } |
790 | 0 | } Unexecuted instantiation: edit.c:frame_state_init Unexecuted instantiation: finally.c:frame_state_init Unexecuted instantiation: foreach.c:frame_state_init |
791 | | |
792 | | /** Cleanup any lingering frame state |
793 | | * |
794 | | */ |
795 | | static inline void frame_cleanup(unlang_stack_frame_t *frame) |
796 | 0 | { |
797 | 0 | unlang_frame_perf_cleanup(frame); |
798 | 0 |
|
799 | 0 | /* |
800 | 0 | * Don't clear top_frame flag, bad things happen... |
801 | 0 | */ |
802 | 0 | frame->flag &= UNLANG_FRAME_FLAG_TOP_FRAME; |
803 | 0 | TALLOC_FREE(frame->retry); |
804 | 0 | if (frame->state) { |
805 | 0 | talloc_free_children(frame->state); /* *(ev->parent) = NULL in event.c */ |
806 | 0 | TALLOC_FREE(frame->state); |
807 | 0 | } |
808 | 0 | } Unexecuted instantiation: edit.c:frame_cleanup Unexecuted instantiation: finally.c:frame_cleanup Unexecuted instantiation: foreach.c:frame_cleanup |
809 | | |
810 | | /** Advance to the next sibling instruction |
811 | | * |
812 | | */ |
813 | | static inline void frame_next(unlang_stack_t *stack, unlang_stack_frame_t *frame) |
814 | 0 | { |
815 | 0 | frame_cleanup(frame); |
816 | 0 | frame->instruction = frame->next; |
817 | 0 |
|
818 | 0 | if (!frame->instruction) return; /* No siblings, need to pop instead */ |
819 | 0 |
|
820 | 0 | frame->next = unlang_list_next(frame->instruction->list, frame->instruction); |
821 | 0 |
|
822 | 0 | /* |
823 | 0 | * We _may_ want to take a new frame->p_result value in future but |
824 | 0 | * for now default to the scratch result. Generally the thing |
825 | 0 | * advancing the frame is within this library, and doesn't |
826 | 0 | * need custom behaviour for rcodes. |
827 | 0 | */ |
828 | 0 | frame_state_init(stack, frame); |
829 | 0 | } Unexecuted instantiation: edit.c:frame_next Unexecuted instantiation: finally.c:frame_next Unexecuted instantiation: foreach.c:frame_next |
830 | | |
831 | | /** Pop a stack frame, removing any associated dynamically allocated state |
832 | | * |
833 | | * @param[in] request The current request. |
834 | | * @param[in] stack frame to pop. |
835 | | */ |
836 | | static inline void frame_pop(request_t *request, unlang_stack_t *stack) |
837 | 0 | { |
838 | 0 | unlang_stack_frame_t *frame; |
839 | 0 |
|
840 | 0 | fr_assert(stack->depth >= 1); |
841 | 0 |
|
842 | 0 | frame = &stack->frame[stack->depth]; |
843 | 0 |
|
844 | 0 | /* |
845 | 0 | * Signal the frame to get it back into a consistent state |
846 | 0 | * as we won't be calling the resume function. |
847 | 0 | * |
848 | 0 | * If the frame was cancelled, the signal function will |
849 | 0 | * have already been called. |
850 | 0 | */ |
851 | 0 | if (!is_unwinding(frame) && is_repeatable(frame)) { |
852 | 0 | if (frame->signal) frame->signal(request, frame, FR_SIGNAL_CANCEL); |
853 | 0 | repeatable_clear(frame); |
854 | 0 | } |
855 | 0 |
|
856 | 0 | /* |
857 | 0 | * We clean up the retries when we pop the frame, not |
858 | 0 | * when we do a frame_cleanup(). That's because |
859 | 0 | * frame_cleanup() is called from the signal handler, and |
860 | 0 | * we need to keep frame->retry around to ensure that we |
861 | 0 | * know how to _stop_ the retries after they've hit a timeout. |
862 | 0 | */ |
863 | 0 | TALLOC_FREE(frame->retry); |
864 | 0 |
|
865 | 0 | /* |
866 | 0 | * Ensure log indent is at the same level as it was when |
867 | 0 | * the frame was pushed. This is important when we're |
868 | 0 | * unwinding the stack and forcefully cancelling calls. |
869 | 0 | */ |
870 | 0 | request->log.indent = frame->indent; |
871 | 0 |
|
872 | 0 | frame_cleanup(frame); |
873 | 0 |
|
874 | 0 | stack->depth--; |
875 | 0 | } Unexecuted instantiation: edit.c:frame_pop Unexecuted instantiation: finally.c:frame_pop Unexecuted instantiation: foreach.c:frame_pop |
876 | | |
877 | | /** Mark the current stack frame up for repeat, and set a new process function |
878 | | * |
879 | | */ |
880 | | static inline void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process) |
881 | 0 | { |
882 | 0 | repeatable_set(frame); |
883 | 0 | frame->process = process; |
884 | 0 | } Unexecuted instantiation: edit.c:frame_repeat Unexecuted instantiation: finally.c:frame_repeat Unexecuted instantiation: foreach.c:frame_repeat |
885 | | |
886 | | static inline unlang_action_t frame_set_next(unlang_stack_frame_t *frame, unlang_t const *unlang) |
887 | 0 | { |
888 | 0 | /* |
889 | 0 | * We're skipping the remaining siblings, stop the |
890 | 0 | * interpreter from continuing and have it pop |
891 | 0 | * this frame, running cleanups normally. |
892 | 0 | * |
893 | 0 | * We don't explicitly cleanup here, otherwise we |
894 | 0 | * end up doing it twice and bad things happen. |
895 | 0 | */ |
896 | 0 | if (!unlang) { |
897 | 0 | frame->next = NULL; |
898 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
899 | 0 | } |
900 | 0 |
|
901 | 0 | /* |
902 | 0 | * Clean up this frame now, so that stats, etc. will be |
903 | 0 | * processed using the correct frame. |
904 | 0 | */ |
905 | 0 | frame_cleanup(frame); |
906 | 0 |
|
907 | 0 | /* |
908 | 0 | * frame_next() will call cleanup *before* resetting the frame->instruction. |
909 | 0 | * but since the instruction is NULL, no duplicate cleanups will happen. |
910 | 0 | * |
911 | 0 | * frame_next() will then set frame->instruction = frame->next, and everything will be OK. |
912 | 0 | */ |
913 | 0 | frame->instruction = NULL; |
914 | 0 | frame->next = unlang; |
915 | 0 | return UNLANG_ACTION_EXECUTE_NEXT; |
916 | 0 | } Unexecuted instantiation: edit.c:frame_set_next Unexecuted instantiation: finally.c:frame_set_next Unexecuted instantiation: foreach.c:frame_set_next |
917 | | |
918 | | /** @name Conversion functions for converting #unlang_t to its specialisations |
919 | | * |
920 | | * Simple conversions: #unlang_module_t and #unlang_group_t are subclasses of #unlang_t, |
921 | | * so we often want to go back and forth between them. |
922 | | * |
923 | | * @{ |
924 | | */ |
925 | | static inline unlang_group_t *unlang_generic_to_group(unlang_t const *p) |
926 | 0 | { |
927 | 0 | fr_assert((p->type > UNLANG_TYPE_MODULE) && (p->type <= UNLANG_TYPE_POLICY)); |
928 | |
|
929 | 0 | return UNCONST(unlang_group_t *, p); |
930 | 0 | } Unexecuted instantiation: edit.c:unlang_generic_to_group Unexecuted instantiation: finally.c:unlang_generic_to_group Unexecuted instantiation: foreach.c:unlang_generic_to_group |
931 | | |
932 | | static inline unlang_t *unlang_group_to_generic(unlang_group_t const *p) |
933 | 0 | { |
934 | 0 | return UNCONST(unlang_t *, p); |
935 | 0 | } Unexecuted instantiation: edit.c:unlang_group_to_generic Unexecuted instantiation: finally.c:unlang_group_to_generic Unexecuted instantiation: foreach.c:unlang_group_to_generic |
936 | | |
937 | | static inline CC_HINT(always_inline) unlang_list_t *unlang_list(unlang_t *unlang) |
938 | 0 | { |
939 | 0 | return &unlang_generic_to_group(unlang)->children; |
940 | 0 | } Unexecuted instantiation: edit.c:unlang_list Unexecuted instantiation: finally.c:unlang_list Unexecuted instantiation: foreach.c:unlang_list |
941 | | |
942 | | static inline CC_HINT(always_inline) void unlang_type_init(unlang_t *unlang, unlang_t *parent, unlang_type_t type) |
943 | 0 | { |
944 | 0 | unlang->type = type; |
945 | 0 | unlang->parent = parent; |
946 | 0 | if (parent) unlang->list = unlang_list(parent); |
947 | 0 | unlang_list_entry_init(unlang); |
948 | 0 | } Unexecuted instantiation: edit.c:unlang_type_init Unexecuted instantiation: finally.c:unlang_type_init Unexecuted instantiation: foreach.c:unlang_type_init |
949 | | |
950 | | static inline CC_HINT(always_inline) void unlang_group_type_init(unlang_t *unlang, unlang_t *parent, unlang_type_t type) |
951 | 0 | { |
952 | 0 | unlang_type_init(unlang, parent, type); |
953 | 0 | unlang_list_init(&((unlang_group_t *) unlang)->children); |
954 | 0 | } Unexecuted instantiation: edit.c:unlang_group_type_init Unexecuted instantiation: finally.c:unlang_group_type_init Unexecuted instantiation: foreach.c:unlang_group_type_init |
955 | | |
956 | | static inline unlang_tmpl_t *unlang_generic_to_tmpl(unlang_t const *p) |
957 | 0 | { |
958 | 0 | fr_assert(p->type == UNLANG_TYPE_TMPL); |
959 | 0 | return UNCONST(unlang_tmpl_t *, talloc_get_type_abort_const(p, unlang_tmpl_t)); |
960 | 0 | } Unexecuted instantiation: edit.c:unlang_generic_to_tmpl Unexecuted instantiation: finally.c:unlang_generic_to_tmpl Unexecuted instantiation: foreach.c:unlang_generic_to_tmpl |
961 | | |
962 | | static inline unlang_t *unlang_tmpl_to_generic(unlang_tmpl_t const *p) |
963 | 0 | { |
964 | 0 | return UNCONST(unlang_t *, p); |
965 | 0 | } Unexecuted instantiation: edit.c:unlang_tmpl_to_generic Unexecuted instantiation: finally.c:unlang_tmpl_to_generic Unexecuted instantiation: foreach.c:unlang_tmpl_to_generic |
966 | | /** @} */ |
967 | | |
968 | | /** @name Internal interpreter functions needed by ops |
969 | | * |
970 | | * @{ |
971 | | */ |
972 | | int unlang_interpret_push(unlang_result_t *p_result, request_t *request, unlang_t const *instruction, |
973 | | unlang_frame_conf_t const *conf, bool do_next_sibling) |
974 | | CC_HINT(warn_unused_result); |
975 | | |
976 | | unlang_action_t unlang_interpret_push_children(unlang_result_t *p_result, request_t *request, |
977 | | rlm_rcode_t default_rcode, bool do_next_sibling) |
978 | | CC_HINT(warn_unused_result); |
979 | | |
980 | | int unlang_op_init(void); |
981 | | |
982 | | void unlang_op_free(void); |
983 | | |
984 | | /** @} */ |
985 | | |
986 | | /** @name io shims |
987 | | * |
988 | | * Functions to simulate a 'proto' module when we're running 'fake' |
989 | | * requests. i.e. those created by parallel and subrequest. |
990 | | * |
991 | | * @{ |
992 | | */ |
993 | | request_t *unlang_io_subrequest_alloc(request_t *parent, fr_dict_t const *namespace, bool detachable); |
994 | | |
995 | | /** @} */ |
996 | | |
997 | | /** @name op init functions |
998 | | * |
999 | | * Functions to trigger registration of the various unlang ops. |
1000 | | * |
1001 | | * @{ |
1002 | | */ |
1003 | | void unlang_register(unlang_op_t *op) CC_HINT(nonnull); |
1004 | | |
1005 | | void unlang_call_init(void); |
1006 | | |
1007 | | void unlang_caller_init(void); |
1008 | | |
1009 | | void unlang_condition_init(void); |
1010 | | |
1011 | | void unlang_finally_init(void); |
1012 | | |
1013 | | void unlang_foreach_init(void); |
1014 | | |
1015 | | void unlang_function_init(void); |
1016 | | |
1017 | | void unlang_group_init(void); |
1018 | | |
1019 | | void unlang_load_balance_init(void); |
1020 | | |
1021 | | void unlang_map_init(void); |
1022 | | |
1023 | | void unlang_module_init(void); |
1024 | | |
1025 | | void unlang_return_init(void); |
1026 | | |
1027 | | void unlang_parallel_init(void); |
1028 | | |
1029 | | int unlang_subrequest_op_init(void); |
1030 | | |
1031 | | void unlang_detach_init(void); |
1032 | | |
1033 | | void unlang_switch_init(void); |
1034 | | |
1035 | | void unlang_tmpl_init(void); |
1036 | | |
1037 | | void unlang_edit_init(void); |
1038 | | |
1039 | | void unlang_timeout_init(void); |
1040 | | |
1041 | | void unlang_transaction_init(void); |
1042 | | |
1043 | | void unlang_limit_init(void); |
1044 | | |
1045 | | void unlang_try_init(void); |
1046 | | |
1047 | | void unlang_catch_init(void); |
1048 | | |
1049 | | /** @} */ |
1050 | | |
1051 | | #ifdef __cplusplus |
1052 | | } |
1053 | | #endif |