Coverage Report

Created: 2026-08-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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