The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
condition.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: bccda50f6ba70de66f8134cb3e50dce15be0d34a $
19 *
20 * @file unlang/condition.c
21 * @brief Unlang "condition" keyword evaluation.
22 *
23 * @copyright 2006-2019 The FreeRADIUS server project
24 */
25RCSID("$Id: bccda50f6ba70de66f8134cb3e50dce15be0d34a $")
26
27#include "condition_priv.h"
28#include "group_priv.h"
29
30typedef struct {
31 fr_value_box_list_t out; //!< Head of the result of a nested
32 ///< expansion.
33 unlang_result_t result; //!< Store the result of unlang expressions.
35
37{
38 unlang_frame_state_cond_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_cond_t);
39 fr_value_box_t *box = fr_value_box_list_head(&state->out);
40 bool value;
41
42 /*
43 * Something in the conditional evaluation failed.
44 */
45 if (state->result.rcode == RLM_MODULE_FAIL) {
48
49 RDEBUG2("... failed to evaluate condition ...");
50
51 if (!gext->has_else) RETURN_UNLANG_FAIL;
53 }
54
55 if (!box) {
56 value = false;
57
58 } else if (fr_value_box_list_next(&state->out, box) != NULL) {
59 value = true;
60
61 } else {
63 }
64
65 if (!value) {
66 RDEBUG2("...");
68 }
69
70 /*
71 * Tell the main interpreter to skip over the else /
72 * elsif blocks, as this "if" condition was taken.
73 */
74 while (frame->next &&
75 ((frame->next->type == UNLANG_TYPE_ELSE) ||
76 (frame->next->type == UNLANG_TYPE_ELSIF))) {
77 frame->next = frame->next->next;
78 }
79
80 /*
81 * We took the "if". Go recurse into its' children.
82 */
83 return unlang_group(p_result, request, frame);
84}
85
87{
90 unlang_frame_state_cond_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_cond_t);
91
92 fr_assert(gext->head != NULL);
93
94 /*
95 * If we always run this condition, then don't bother pushing anything onto the stack.
96 *
97 * We still run this condition, even for "false" values, due to things like
98 *
99 * if (0) { ... } elsif ....
100 */
101 if (gext->is_truthy) {
102 return unlang_group(p_result, request, frame);
103 }
104
106
107 fr_value_box_list_init(&state->out);
108
109 if (unlang_xlat_push(state, &state->result, &state->out,
110 request, gext->head, UNLANG_SUB_FRAME) < 0) return UNLANG_ACTION_FAIL;
111
113}
114
115
117 L(""),
118 L("{"),
119);
120
122{
123 unlang_t *c;
124
126 unlang_cond_t *gext;
127 CONF_ITEM *ci;
128
129 xlat_exp_head_t *head = NULL;
130 bool is_truthy = false, value = false;
131 xlat_res_rules_t xr_rules = {
133 .dict_def = unlang_ctx->rules->attr.dict_def,
134 },
135 };
136
137 if (!cf_section_name2(cs)) {
138 cf_log_err(cs, "'%s' without condition", unlang_ops[type].name);
139 return NULL;
140 }
141
142 /*
143 * Migration support.
144 */
145 {
146 char const *name2 = cf_section_name2(cs);
147 ssize_t slen;
148
149 tmpl_rules_t t_rules = (tmpl_rules_t) {
150 .parent = unlang_ctx->rules->parent,
151 .attr = {
152 .dict_def = xr_rules.tr_rules->dict_def,
153 .list_def = request_attr_request,
154 .allow_unresolved = false,
155 .allow_unknown = false,
156 .allow_wildcard = true,
157 },
158 .literals_safe_for = unlang_ctx->rules->literals_safe_for,
159 };
160
161 fr_sbuff_parse_rules_t p_rules = { };
162
163 p_rules.terminals = &if_terminals;
164
165 slen = xlat_tokenize_condition(cs, &head, &FR_SBUFF_IN(name2, strlen(name2)), &p_rules, &t_rules);
166 if (slen == 0) {
167 cf_canonicalize_error(cs, slen, "Empty conditions are invalid", name2);
168 return NULL;
169 }
170
171 if (slen < 0) {
172 slen++; /* fr_slen_t vs ssize_t */
173 cf_canonicalize_error(cs, slen, "Failed parsing condition", name2);
174 return NULL;
175 }
176
177 /*
178 * Resolve the xlat first.
179 */
180 if (xlat_resolve(head, &xr_rules) < 0) {
181 cf_log_err(cs, "Failed resolving condition - %s", fr_strerror());
182 return NULL;
183 }
184
186
188
189 /*
190 * If the condition is always false, we don't compile the
191 * children.
192 */
193 if (is_truthy && !value) {
194 cf_log_debug_prefix(cs, "Skipping contents of '%s' as it is always 'false'",
196
197 /*
198 * Free the children, which frees any xlats,
199 * conditions, etc. which were defined, but are
200 * now entirely unused.
201 *
202 * However, we still need to cache the conditions, as they will be accessed at run-time.
203 */
206 } else {
208 }
209 }
210
211 if (!c) return NULL;
213
215 gext = unlang_group_to_cond(g);
216
217 gext->head = head;
218 gext->is_truthy = is_truthy;
219 gext->value = value;
220
221 ci = cf_section_to_item(cs);
222 while ((ci = cf_item_next(parent->ci, ci)) != NULL) {
223 if (cf_item_is_data(ci)) continue;
224
225 break;
226 }
227
228 /*
229 * If there's an 'if' without an 'else', then remember that.
230 */
231 if (ci && cf_item_is_section(ci)) {
232 char const *name;
233
235 fr_assert(name != NULL);
236
237 gext->has_else = (strcmp(name, "else") == 0) || (strcmp(name, "elsif") == 0);
238 }
239
240 return c;
241}
242
247
252
254{
256
257 if (cf_section_name2(cs)) {
258 cf_log_err(cs, "'else' cannot have a condition");
259 return NULL;
260 }
261
263}
264
265
267{
269 .name = "if",
270 .type = UNLANG_TYPE_IF,
272
273 .compile = unlang_compile_if,
274 .interpret = unlang_if,
275
276 .unlang_size = sizeof(unlang_cond_t),
277 .unlang_name = "unlang_cond_t",
278 .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
279 .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2),
280
281 .frame_state_size = sizeof(unlang_frame_state_cond_t),
282 .frame_state_type = "unlang_frame_state_cond_t",
283 });
284
286 .name = "else",
287 .type = UNLANG_TYPE_ELSE,
289
290 .compile = unlang_compile_else,
291 .interpret = unlang_group,
292
293 .unlang_size = sizeof(unlang_group_t),
294 .unlang_name = "unlang_group_t"
295 });
296
298 .name = "elsif",
299 .type = UNLANG_TYPE_ELSIF,
301
302 .compile = unlang_compile_elsif,
303 .interpret = unlang_if,
304
305 .unlang_size = sizeof(unlang_cond_t),
306 .unlang_name = "unlang_cond_t",
307 .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
308 .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2),
309
310 .frame_state_size = sizeof(unlang_frame_state_cond_t),
311 .frame_state_type = "unlang_frame_state_cond_t",
312 });
313}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_EXECUTE_NEXT
Execute the next unlang_t.
Definition action.h:38
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:737
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
bool cf_item_is_data(CONF_ITEM const *ci)
Determine if CONF_ITEM is CONF_DATA.
Definition cf_util.c:645
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:617
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_log_debug_prefix(_cf, _fmt,...)
Definition cf_util.h:306
#define cf_section_free_children(_x)
Definition cf_util.h:196
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_item_next(_parent, _curr)
Definition cf_util.h:92
unlang_t * unlang_compile_section(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
Definition compile.c:1547
unlang_t * unlang_compile_empty(unlang_t *parent, UNUSED unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
Definition compile.c:1233
static unlang_t * unlang_compile_if(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition condition.c:243
unlang_result_t result
Store the result of unlang expressions.
Definition condition.c:33
static unlang_t * unlang_compile_elsif(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition condition.c:248
static unlang_t * unlang_compile_else(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition condition.c:253
void unlang_condition_init(void)
Definition condition.c:266
static const fr_sbuff_term_t if_terminals
Definition condition.c:116
static unlang_action_t unlang_if(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition condition.c:86
fr_value_box_list_t out
Head of the result of a nested expansion.
Definition condition.c:31
static unlang_action_t unlang_if_resume(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition condition.c:36
static unlang_t * compile_if_subsection(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
Definition condition.c:121
xlat_exp_head_t * head
static unlang_cond_t * unlang_group_to_cond(unlang_group_t *g)
Cast a group structure to the cond keyword extension.
Test enumeration values.
Definition dict_test.h:92
unlang_action_t unlang_group(UNUSED unlang_result_t *p_result, request_t *request, UNUSED unlang_stack_frame_t *frame)
Definition group.c:31
Declarations for the "group" keyword.
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
unlang_op_t unlang_ops[UNLANG_TYPE_MAX]
Different operations the interpreter can execute.
Definition base.c:31
static TALLOC_CTX * unlang_ctx
Definition base.c:71
void unlang_register(unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:56
long int ssize_t
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RETURN_UNLANG_FAIL
Definition rcode.h:57
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
fr_dict_attr_t const * request_attr_request
Definition request.c:43
static char const * name
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
Set of terminal elements.
struct map_s map_t
Definition map.h:33
tmpl_rules_t const * parent
for parent / child relationships
Definition tmpl.h:333
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:365
#define TMPL_POOL_DEF_HEADERS
Define manipulation functions for the attribute reference list.
Definition tmpl.h:486
#define TMPL_POOL_DEF_LEN
How many additional bytes to allocate in a pool for a tmpl_t.
Definition tmpl.h:491
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
struct tmpl_res_rules_s tmpl_res_rules_t
Definition tmpl.h:237
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
fr_aka_sim_id_type_t type
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:282
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition xlat.h:165
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3152
static fr_slen_t e_rules bool xlat_is_truthy(xlat_exp_head_t const *head, bool *out)
Allow callers to see if an xlat is truthy.
Definition xlat_expr.c:3187
static fr_slen_t head
Definition xlat.h:420
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
unlang_t * next
Next node (executed on UNLANG_ACTION_EXECUTE_NEXT et al).
void * state
Stack frame specialisations.
#define UNLANG_IGNORE
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
unlang_type_t
Types of unlang_t nodes.
Definition unlang_priv.h:45
@ UNLANG_TYPE_ELSIF
!Condition && Condition.
Definition unlang_priv.h:56
@ UNLANG_TYPE_ELSE
!Condition.
Definition unlang_priv.h:55
@ UNLANG_TYPE_IF
Condition.
Definition unlang_priv.h:54
static void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process)
Mark the current stack frame up for repeat, and set a new process function.
unlang_t const * instruction
The unlang node we're evaluating.
unlang_t const * next
The next unlang node we will evaluate.
@ UNLANG_OP_FLAG_DEBUG_BRACES
Print debug braces.
unlang_type_t type
The specialisation of this node.
Generic representation of a grouping.
An unlang operation.
A node in a graph of unlang_op_t (s) that we execute.
Our interpreter stack, as distinct from the C stack.
static fr_slen_t parent
Definition pair.h:839
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
bool fr_value_box_is_truthy(fr_value_box_t const *in)
Check truthiness of values.
Definition value.c:6587
static bool is_truthy(xlat_exp_t *node, bool *out)