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: bf6b048af39f577da72633ebd61d57c4979f6416 $
19 *
20 * @file unlang/condition.c
21 * @brief Unlang "condition" keyword evaluation.
22 *
23 * @copyright 2006-2019 The FreeRADIUS server project
24 */
25RCSID("$Id: bf6b048af39f577da72633ebd61d57c4979f6416 $")
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 unlang_t const *unlang;
42
43 /*
44 * Something in the conditional evaluation failed.
45 */
46 if (state->result.rcode == RLM_MODULE_FAIL) {
49
50 RDEBUG2("... failed to evaluate condition ...");
51
52 if (!gext->has_else) RETURN_UNLANG_FAIL;
54 }
55
56 if (!box) {
57 value = false;
58
59 } else if (fr_value_box_list_next(&state->out, box) != NULL) {
60 value = true;
61
62 } else {
64 }
65
66 if (!value) {
67 RDEBUG2("...");
69 }
70
71 /*
72 * Tell the main interpreter to skip over the else /
73 * elsif blocks, as this "if" condition was taken.
74 */
75 for (unlang = frame->next;
76 unlang != NULL;
77 unlang = unlang_list_next(unlang->list, unlang)) {
78 if ((unlang->type == UNLANG_TYPE_ELSE) ||
79 (unlang->type == UNLANG_TYPE_ELSIF)) {
80 continue;
81 }
82
83 break;
84 }
85
86 /*
87 * Do NOT call frame_set_next(), as that will clean up the current frame.
88 */
89 frame->next = unlang;
90
91 /*
92 * We took the "if". Go recurse into its' children.
93 */
94 return unlang_group(p_result, request, frame);
95}
96
98{
101 unlang_frame_state_cond_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_cond_t);
102
103 fr_assert(gext->head != NULL);
104
105 /*
106 * If we always run this condition, then don't bother pushing anything onto the stack.
107 *
108 * We still run this condition, even for "false" values, due to things like
109 *
110 * if (0) { ... } elsif ....
111 */
112 if (gext->is_truthy) {
113 return unlang_group(p_result, request, frame);
114 }
115
117
118 fr_value_box_list_init(&state->out);
119
120 if (unlang_xlat_push(state, &state->result, &state->out,
121 request, gext->head, UNLANG_SUB_FRAME) < 0) return UNLANG_ACTION_FAIL;
122
124}
125
126
128 L(""),
129 L("{"),
130);
131
133{
134 unlang_t *c;
135
137 unlang_cond_t *gext;
138 CONF_ITEM *ci;
139
140 xlat_exp_head_t *head = NULL;
141 bool is_truthy = false, value = false;
142 xlat_res_rules_t xr_rules = {
144 .dict_def = unlang_ctx->rules->attr.dict_def,
145 },
146 };
147
148 if (!cf_section_name2(cs)) {
149 cf_log_err(cs, "'%s' without condition", unlang_ops[type].name);
150 return NULL;
151 }
152
153 /*
154 * Migration support.
155 */
156 {
157 char const *name2 = cf_section_name2(cs);
158 ssize_t slen;
159
160 tmpl_rules_t t_rules = (tmpl_rules_t) {
161 .parent = unlang_ctx->rules->parent,
162 .attr = {
163 .dict_def = xr_rules.tr_rules->dict_def,
164 .list_def = request_attr_request,
165 .allow_unresolved = false,
166 .allow_unknown = false,
167 .allow_wildcard = true,
168 },
169 .literals_safe_for = unlang_ctx->rules->literals_safe_for,
170 };
171
172 fr_sbuff_parse_rules_t p_rules = { };
173
174 p_rules.terminals = &if_terminals;
175
176 slen = xlat_tokenize_condition(cs, &head, &FR_SBUFF_IN_STR(name2), &p_rules, &t_rules);
177 if (slen == 0) {
178 cf_canonicalize_error(cs, slen, "Empty conditions are invalid", name2);
179 return NULL;
180 }
181
182 if (slen < 0) {
183 slen++; /* fr_slen_t vs ssize_t */
184 cf_canonicalize_error(cs, slen, "Failed parsing condition", name2);
185 return NULL;
186 }
187
188 /*
189 * Resolve the xlat first.
190 */
191 if (xlat_resolve(head, &xr_rules) < 0) {
192 cf_log_err(cs, "Failed resolving condition - %s", fr_strerror());
193 return NULL;
194 }
195
197
199
200 /*
201 * If the condition is always false, we don't compile the
202 * children.
203 */
204 if (is_truthy && !value) {
205 cf_log_debug_prefix(cs, "Skipping contents of '%s' as it is always 'false'",
207
208 /*
209 * Free the children, which frees any xlats,
210 * conditions, etc. which were defined, but are
211 * now entirely unused.
212 *
213 * However, we still need to cache the conditions, as they will be accessed at run-time.
214 */
217 } else {
219 }
220 }
221
222 if (!c) return NULL;
224
226 gext = unlang_group_to_cond(g);
227
228 gext->head = head;
229 gext->is_truthy = is_truthy;
230 gext->value = value;
231
232 ci = cf_section_to_item(cs);
233 while ((ci = cf_item_next(parent->ci, ci)) != NULL) {
234 if (cf_item_is_data(ci)) continue;
235
236 break;
237 }
238
239 /*
240 * If there's an 'if' without an 'else', then remember that.
241 */
242 if (ci && cf_item_is_section(ci)) {
243 char const *name;
244
246 fr_assert(name != NULL);
247
248 gext->has_else = (strcmp(name, "else") == 0) || (strcmp(name, "elsif") == 0);
249 }
250
251 return c;
252}
253
258
263
265{
267
268 if (cf_section_name2(cs)) {
269 cf_log_err(cs, "'else' cannot have a condition");
270 return NULL;
271 }
272
274}
275
276
278{
280 .name = "if",
281 .type = UNLANG_TYPE_IF,
283
284 .compile = unlang_compile_if,
285 .interpret = unlang_if,
286
287 .unlang_size = sizeof(unlang_cond_t),
288 .unlang_name = "unlang_cond_t",
289 .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
290 .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2),
291
292 .frame_state_size = sizeof(unlang_frame_state_cond_t),
293 .frame_state_type = "unlang_frame_state_cond_t",
294 });
295
297 .name = "else",
298 .type = UNLANG_TYPE_ELSE,
300
301 .compile = unlang_compile_else,
302 .interpret = unlang_group,
303
304 .unlang_size = sizeof(unlang_group_t),
305 .unlang_name = "unlang_group_t"
306 });
307
309 .name = "elsif",
310 .type = UNLANG_TYPE_ELSIF,
312
313 .compile = unlang_compile_elsif,
314 .interpret = unlang_if,
315
316 .unlang_size = sizeof(unlang_cond_t),
317 .unlang_name = "unlang_cond_t",
318 .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
319 .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2),
320
321 .frame_state_size = sizeof(unlang_frame_state_cond_t),
322 .frame_state_type = "unlang_frame_state_cond_t",
323 });
324}
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:1508
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:1196
static unlang_t * unlang_compile_if(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition condition.c:254
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:259
static unlang_t * unlang_compile_else(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
Definition condition.c:264
void unlang_condition_init(void)
Definition condition.c:277
static const fr_sbuff_term_t if_terminals
Definition condition.c:127
static unlang_action_t unlang_if(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition condition.c:97
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:132
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:59
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:44
fr_dict_attr_t const * request_attr_request
Definition request.c:43
static char const * name
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define FR_SBUFF_IN_STR(_start)
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:270
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:3163
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:3198
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.
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:47
@ UNLANG_TYPE_ELSIF
!Condition && Condition.
Definition unlang_priv.h:58
@ UNLANG_TYPE_ELSE
!Condition.
Definition unlang_priv.h:57
@ UNLANG_TYPE_IF
Condition.
Definition unlang_priv.h:56
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.
unlang_list_t * list
so we have fewer run-time dereferences
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:841
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:6931
static bool is_truthy(xlat_exp_t *node, bool *out)