blob: 4aaa92d9d43f077b7c2b36e399b7ab9bd206be4f [file] [log] [blame]
Willy Tarreau66243b42021-07-16 15:39:28 +02001/*
2 * Configuration condition preprocessor
3 *
4 * Copyright 2000-2021 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <haproxy/api.h>
14#include <haproxy/arg.h>
15#include <haproxy/cfgcond.h>
16#include <haproxy/global.h>
17#include <haproxy/tools.h>
18
19/* supported condition predicates */
20const struct cond_pred_kw cond_predicates[] = {
William Lallemand3aeb3f92021-08-21 23:59:56 +020021 { "defined", CFG_PRED_DEFINED, ARG1(1, STR) },
22 { "feature", CFG_PRED_FEATURE, ARG1(1, STR) },
23 { "streq", CFG_PRED_STREQ, ARG2(2, STR, STR) },
24 { "strneq", CFG_PRED_STRNEQ, ARG2(2, STR, STR) },
25 { "version_atleast", CFG_PRED_VERSION_ATLEAST, ARG1(1, STR) },
26 { "version_before", CFG_PRED_VERSION_BEFORE, ARG1(1, STR) },
27 { "openssl_version_atleast", CFG_PRED_OSSL_VERSION_ATLEAST, ARG1(1, STR) },
28 { "openssl_version_before", CFG_PRED_OSSL_VERSION_BEFORE, ARG1(1, STR) },
Willy Tarreau66243b42021-07-16 15:39:28 +020029 { NULL, CFG_PRED_NONE, 0 }
30};
31
32/* looks up a cond predicate matching the keyword in <str>, possibly followed
33 * by a parenthesis. Returns a pointer to it or NULL if not found.
34 */
35const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
36{
37 const struct cond_pred_kw *ret;
38 int len = strcspn(str, " (");
39
40 for (ret = &cond_predicates[0]; ret->word; ret++) {
41 if (len != strlen(ret->word))
42 continue;
43 if (strncmp(str, ret->word, len) != 0)
44 continue;
45 return ret;
46 }
47 return NULL;
48}
49
Willy Tarreau087b2d02021-07-16 14:27:20 +020050/* Frees <term> and its args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +020051void cfg_free_cond_term(struct cfg_cond_term *term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020052{
Willy Tarreauf1db20c2021-07-17 18:46:30 +020053 if (!term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020054 return;
55
Willy Tarreauf1db20c2021-07-17 18:46:30 +020056 if (term->type == CCTT_PAREN) {
57 cfg_free_cond_expr(term->expr);
58 term->expr = NULL;
59 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +020060
Willy Tarreauf1db20c2021-07-17 18:46:30 +020061 free_args(term->args);
62 free(term->args);
63 free(term);
Willy Tarreau087b2d02021-07-16 14:27:20 +020064}
65
Willy Tarreauf8690952021-07-16 12:12:00 +020066/* Parse an indirect input text as a possible config condition term.
67 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
Willy Tarreau087b2d02021-07-16 14:27:20 +020068 * success. <term> is allocated and filled with the parsed info, and <text>
69 * is updated on success to point to the first unparsed character, or is left
70 * untouched on failure. On success, the caller must free <term> using
71 * cfg_free_cond_term(). An error will be set in <err> on error, and only
Willy Tarreauf8690952021-07-16 12:12:00 +020072 * in this case. In this case the first bad character will be reported in
Willy Tarreaudc70c182021-07-20 17:58:34 +020073 * <errptr>. <maxdepth> corresponds to the maximum recursion depth permitted,
74 * it is decremented on each recursive call and the parsing will fail one
75 * reaching <= 0.
Willy Tarreau66243b42021-07-16 15:39:28 +020076 */
Willy Tarreaudc70c182021-07-20 17:58:34 +020077int cfg_parse_cond_term(const char **text, struct cfg_cond_term **term, char **err, const char **errptr, int maxdepth)
Willy Tarreau66243b42021-07-16 15:39:28 +020078{
Willy Tarreau087b2d02021-07-16 14:27:20 +020079 struct cfg_cond_term *t;
Willy Tarreauf8690952021-07-16 12:12:00 +020080 const char *in = *text;
Willy Tarreau66243b42021-07-16 15:39:28 +020081 const char *end_ptr;
Willy Tarreau66243b42021-07-16 15:39:28 +020082 int err_arg;
83 int nbargs;
Willy Tarreau66243b42021-07-16 15:39:28 +020084 char *end;
85 long val;
86
Willy Tarreauf8690952021-07-16 12:12:00 +020087 while (*in == ' ' || *in == '\t')
88 in++;
89
90 if (!*in) /* empty term does not parse */
Willy Tarreau66243b42021-07-16 15:39:28 +020091 return 0;
92
Willy Tarreaudc70c182021-07-20 17:58:34 +020093 *term = NULL;
94 if (maxdepth <= 0)
95 goto fail0;
96
Willy Tarreau087b2d02021-07-16 14:27:20 +020097 t = *term = calloc(1, sizeof(**term));
98 if (!t) {
99 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
100 goto fail1;
101 }
102
103 t->type = CCTT_NONE;
104 t->args = NULL;
105 t->neg = 0;
106
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200107 /* !<term> negates the term. White spaces permitted */
108 while (*in == '!') {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200109 t->neg = !t->neg;
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200110 do { in++; } while (*in == ' ' || *in == '\t');
111 }
112
Willy Tarreauf8690952021-07-16 12:12:00 +0200113 val = strtol(in, &end, 0);
114 if (end != in) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200115 t->type = val ? CCTT_TRUE : CCTT_FALSE;
Willy Tarreauf8690952021-07-16 12:12:00 +0200116 *text = end;
117 return 1;
118 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200119
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200120 /* Try to parse '(' EXPR ')' */
121 if (*in == '(') {
122 int ret;
123
124 t->type = CCTT_PAREN;
125 t->args = NULL;
126
127 do { in++; } while (*in == ' ' || *in == '\t');
Willy Tarreaudc70c182021-07-20 17:58:34 +0200128 ret = cfg_parse_cond_expr(&in, &t->expr, err, errptr, maxdepth - 1);
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200129 if (ret == -1)
130 goto fail2;
131 if (ret == 0)
132 goto fail0;
133
134 /* find the closing ')' */
135 while (*in == ' ' || *in == '\t')
136 in++;
137 if (*in != ')') {
138 memprintf(err, "expected ')' after conditional expression '%s'", *text);
139 goto fail1;
140 }
141 do { in++; } while (*in == ' ' || *in == '\t');
142 *text = in;
143 return 1;
144 }
145
Willy Tarreau66243b42021-07-16 15:39:28 +0200146 /* below we'll likely all make_arg_list() so we must return only via
147 * the <done> label which frees the arg list.
148 */
Willy Tarreau087b2d02021-07-16 14:27:20 +0200149 t->pred = cfg_lookup_cond_pred(in);
150 if (t->pred) {
151 t->type = CCTT_PRED;
152 nbargs = make_arg_list(in + strlen(t->pred->word), -1,
153 t->pred->arg_mask, &t->args, err,
Willy Tarreau66243b42021-07-16 15:39:28 +0200154 &end_ptr, &err_arg, NULL);
Willy Tarreau66243b42021-07-16 15:39:28 +0200155 if (nbargs < 0) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200156 memprintf(err, "%s in argument %d of predicate '%s' used in conditional expression", *err, err_arg, t->pred->word);
Willy Tarreau66243b42021-07-16 15:39:28 +0200157 if (errptr)
158 *errptr = end_ptr;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200159 goto fail2;
Willy Tarreau66243b42021-07-16 15:39:28 +0200160 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200161 *text = end_ptr;
162 return 1;
163 }
164
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200165 fail0:
Willy Tarreauf8690952021-07-16 12:12:00 +0200166 memprintf(err, "unparsable conditional expression '%s'", *text);
Willy Tarreau087b2d02021-07-16 14:27:20 +0200167 fail1:
Willy Tarreauf8690952021-07-16 12:12:00 +0200168 if (errptr)
169 *errptr = *text;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200170 fail2:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200171 cfg_free_cond_term(*term);
172 *term = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200173 return -1;
174}
175
176/* evaluate a condition term on a .if/.elif line. The condition was already
177 * parsed in <term>. Returns -1 on error (in which case err is filled with a
178 * message, and only in this case), 0 if the condition is false, 1 if it's
179 * true.
180 */
181int cfg_eval_cond_term(const struct cfg_cond_term *term, char **err)
182{
183 int ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200184
Willy Tarreauf8690952021-07-16 12:12:00 +0200185 if (term->type == CCTT_FALSE)
186 ret = 0;
187 else if (term->type == CCTT_TRUE)
188 ret = 1;
189 else if (term->type == CCTT_PRED) {
190 /* here we know we have a valid predicate with valid arguments
191 * placed in term->args (which the caller will free).
Willy Tarreau66243b42021-07-16 15:39:28 +0200192 */
Willy Tarreauf8690952021-07-16 12:12:00 +0200193 switch (term->pred->prd) {
Willy Tarreau66243b42021-07-16 15:39:28 +0200194 case CFG_PRED_DEFINED: // checks if arg exists as an environment variable
Willy Tarreauf8690952021-07-16 12:12:00 +0200195 ret = getenv(term->args[0].data.str.area) != NULL;
196 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200197
198 case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
199 const char *p;
200
Willy Tarreauf8690952021-07-16 12:12:00 +0200201 ret = 0; // assume feature not found
202 for (p = build_features; (p = strstr(p, term->args[0].data.str.area)); p++) {
203 if (p > build_features &&
204 (p[term->args[0].data.str.data] == ' ' ||
205 p[term->args[0].data.str.data] == 0)) {
206 if (*(p-1) == '+') { // e.g. "+OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200207 ret = 1;
Willy Tarreauf8690952021-07-16 12:12:00 +0200208 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200209 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200210 else if (*(p-1) == '-') { // e.g. "-OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200211 ret = 0;
Willy Tarreauf8690952021-07-16 12:12:00 +0200212 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200213 }
214 /* it was a sub-word, let's restart from next place */
215 }
216 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200217 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200218 }
219 case CFG_PRED_STREQ: // checks if the two arg are equal
Willy Tarreauf8690952021-07-16 12:12:00 +0200220 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) == 0;
221 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200222
223 case CFG_PRED_STRNEQ: // checks if the two arg are different
Willy Tarreauf8690952021-07-16 12:12:00 +0200224 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) != 0;
225 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200226
227 case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200228 ret = compare_current_version(term->args[0].data.str.area) <= 0;
229 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200230
231 case CFG_PRED_VERSION_BEFORE: // checks if the current version is older than this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200232 ret = compare_current_version(term->args[0].data.str.area) > 0;
233 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200234
William Lallemand3aeb3f92021-08-21 23:59:56 +0200235 case CFG_PRED_OSSL_VERSION_ATLEAST: { // checks if the current openssl version is at least this one
236 int opensslret = openssl_compare_current_version(term->args[0].data.str.area);
237
238 if (opensslret < -1) /* can't parse the string or no openssl available */
239 ret = -1;
240 else
241 ret = opensslret <= 0;
242 break;
243 }
244 case CFG_PRED_OSSL_VERSION_BEFORE: { // checks if the current openssl version is older than this one
245 int opensslret = openssl_compare_current_version(term->args[0].data.str.area);
246
247 if (opensslret < -1) /* can't parse the string or no openssl available */
248 ret = -1;
249 else
250 ret = opensslret > 0;
251 break;
252 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200253 default:
Willy Tarreauf8690952021-07-16 12:12:00 +0200254 memprintf(err, "internal error: unhandled conditional expression predicate '%s'", term->pred->word);
255 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200256 }
257 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200258 else if (term->type == CCTT_PAREN) {
259 ret = cfg_eval_cond_expr(term->expr, err);
260 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200261 else {
262 memprintf(err, "internal error: unhandled condition term type %d", (int)term->type);
263 }
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200264
265 if (ret >= 0 && term->neg)
266 ret = !ret;
Willy Tarreauf8690952021-07-16 12:12:00 +0200267 return ret;
268}
269
270
Willy Tarreauca818872021-07-16 14:46:09 +0200271/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200272void cfg_free_cond_and(struct cfg_cond_and *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200273{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200274 struct cfg_cond_and *prev;
275
276 while (expr) {
277 cfg_free_cond_term(expr->left);
278 prev = expr;
279 expr = expr->right;
280 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200281 }
282}
283
284/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200285void cfg_free_cond_expr(struct cfg_cond_expr *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200286{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200287 struct cfg_cond_expr *prev;
288
289 while (expr) {
290 cfg_free_cond_and(expr->left);
291 prev = expr;
292 expr = expr->right;
293 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200294 }
295}
296
297/* Parse an indirect input text as a possible config condition sub-expr.
298 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
299 * success. <expr> is filled with the parsed info, and <text> is updated on
300 * success to point to the first unparsed character, or is left untouched
301 * on failure. On success, the caller will have to free all lower-level
302 * allocated structs using cfg_free_cond_expr(). An error will be set in
303 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200304 * character will be reported in <errptr>. <maxdepth> corresponds to the
305 * maximum recursion depth permitted, it is decremented on each recursive
306 * call and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200307 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200308int cfg_parse_cond_and(const char **text, struct cfg_cond_and **expr, char **err, const char **errptr, int maxdepth)
Willy Tarreauca818872021-07-16 14:46:09 +0200309{
310 struct cfg_cond_and *e;
311 const char *in = *text;
312 int ret = -1;
313
314 if (!*in) /* empty expr does not parse */
315 return 0;
316
Willy Tarreaudc70c182021-07-20 17:58:34 +0200317 *expr = NULL;
318 if (maxdepth <= 0) {
319 memprintf(err, "unparsable conditional sub-expression '%s'", in);
320 if (errptr)
321 *errptr = in;
322 goto done;
323 }
324
Willy Tarreauca818872021-07-16 14:46:09 +0200325 e = *expr = calloc(1, sizeof(**expr));
326 if (!e) {
327 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
328 goto done;
329 }
330
Willy Tarreaudc70c182021-07-20 17:58:34 +0200331 ret = cfg_parse_cond_term(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200332 if (ret == -1) // parse error, error already reported
333 goto done;
334
335 if (ret == 0) {
336 /* ret == 0, no other way to parse this */
337 memprintf(err, "unparsable conditional sub-expression '%s'", in);
338 if (errptr)
339 *errptr = in;
340 ret = -1;
341 goto done;
342 }
343
344 /* ret=1, we have a term in the left hand set */
345
Ilya Shipitsin01881082021-08-07 14:41:56 +0500346 /* find an optional '&&' */
Willy Tarreauca818872021-07-16 14:46:09 +0200347 while (*in == ' ' || *in == '\t')
348 in++;
349
350 *text = in;
351 if (in[0] != '&' || in[1] != '&')
352 goto done;
353
354 /* we have a '&&', let's parse the right handset's subexp */
355 in += 2;
356 while (*in == ' ' || *in == '\t')
357 in++;
358
Willy Tarreaudc70c182021-07-20 17:58:34 +0200359 ret = cfg_parse_cond_and(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200360 if (ret > 0)
361 *text = in;
362 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200363 if (ret < 0) {
364 cfg_free_cond_and(*expr);
365 *expr = NULL;
366 }
Willy Tarreauca818872021-07-16 14:46:09 +0200367 return ret;
368}
369
370/* Parse an indirect input text as a possible config condition term.
371 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
372 * success. <expr> is filled with the parsed info, and <text> is updated on
373 * success to point to the first unparsed character, or is left untouched
374 * on failure. On success, the caller will have to free all lower-level
375 * allocated structs using cfg_free_cond_expr(). An error will be set in
376 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200377 * character will be reported in <errptr>. <maxdepth> corresponds to the
378 * maximum recursion depth permitted, it is decremented on each recursive call
379 * and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200380 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200381int cfg_parse_cond_expr(const char **text, struct cfg_cond_expr **expr, char **err, const char **errptr, int maxdepth)
Willy Tarreauca818872021-07-16 14:46:09 +0200382{
383 struct cfg_cond_expr *e;
384 const char *in = *text;
385 int ret = -1;
386
387 if (!*in) /* empty expr does not parse */
388 return 0;
389
Willy Tarreaudc70c182021-07-20 17:58:34 +0200390 *expr = NULL;
391 if (maxdepth <= 0) {
392 memprintf(err, "unparsable conditional expression '%s'", in);
393 if (errptr)
394 *errptr = in;
395 goto done;
396 }
397
Willy Tarreauca818872021-07-16 14:46:09 +0200398 e = *expr = calloc(1, sizeof(**expr));
399 if (!e) {
400 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
401 goto done;
402 }
403
Willy Tarreaudc70c182021-07-20 17:58:34 +0200404 ret = cfg_parse_cond_and(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200405 if (ret == -1) // parse error, error already reported
406 goto done;
407
408 if (ret == 0) {
409 /* ret == 0, no other way to parse this */
410 memprintf(err, "unparsable conditional expression '%s'", in);
411 if (errptr)
412 *errptr = in;
413 ret = -1;
414 goto done;
415 }
416
417 /* ret=1, we have a sub-expr in the left hand set */
418
Ilya Shipitsin01881082021-08-07 14:41:56 +0500419 /* find an optional '||' */
Willy Tarreauca818872021-07-16 14:46:09 +0200420 while (*in == ' ' || *in == '\t')
421 in++;
422
423 *text = in;
424 if (in[0] != '|' || in[1] != '|')
425 goto done;
426
427 /* we have a '||', let's parse the right handset's subexp */
428 in += 2;
429 while (*in == ' ' || *in == '\t')
430 in++;
431
Willy Tarreaudc70c182021-07-20 17:58:34 +0200432 ret = cfg_parse_cond_expr(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200433 if (ret > 0)
434 *text = in;
435 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200436 if (ret < 0) {
437 cfg_free_cond_expr(*expr);
438 *expr = NULL;
439 }
Willy Tarreauca818872021-07-16 14:46:09 +0200440 return ret;
441}
442
443/* evaluate an sub-expression on a .if/.elif line. The expression is valid and
444 * was already parsed in <expr>. Returns -1 on error (in which case err is
445 * filled with a message, and only in this case), 0 if the condition is false,
446 * 1 if it's true.
447 */
448int cfg_eval_cond_and(struct cfg_cond_and *expr, char **err)
449{
450 int ret;
451
452 /* AND: loop on terms and sub-exp's terms as long as they're TRUE
453 * (stop on FALSE and ERROR).
454 */
455 while ((ret = cfg_eval_cond_term(expr->left, err)) > 0 && expr->right)
456 expr = expr->right;
457 return ret;
458}
459
460/* evaluate an expression on a .if/.elif line. The expression is valid and was
461 * already parsed in <expr>. Returns -1 on error (in which case err is filled
462 * with a message, and only in this case), 0 if the condition is false, 1 if
463 * it's true.
464 */
465int cfg_eval_cond_expr(struct cfg_cond_expr *expr, char **err)
466{
467 int ret;
468
469 /* OR: loop on sub-exps as long as they're FALSE (stop on TRUE and ERROR) */
470 while ((ret = cfg_eval_cond_and(expr->left, err)) == 0 && expr->right)
471 expr = expr->right;
472 return ret;
473}
474
Willy Tarreauf8690952021-07-16 12:12:00 +0200475/* evaluate a condition on a .if/.elif line. The condition is already tokenized
476 * in <err>. Returns -1 on error (in which case err is filled with a message,
477 * and only in this case), 0 if the condition is false, 1 if it's true. If
478 * <errptr> is not NULL, it's set to the first invalid character on error.
479 */
480int cfg_eval_condition(char **args, char **err, const char **errptr)
481{
Willy Tarreauca818872021-07-16 14:46:09 +0200482 struct cfg_cond_expr *expr = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200483 const char *text = args[0];
484 int ret = -1;
485
486 if (!*text) /* note: empty = false */
487 return 0;
488
Willy Tarreaudc70c182021-07-20 17:58:34 +0200489 ret = cfg_parse_cond_expr(&text, &expr, err, errptr, MAX_CFG_RECURSION);
Willy Tarreauf8690952021-07-16 12:12:00 +0200490 if (ret != 0) {
491 if (ret == -1) // parse error, error already reported
492 goto done;
Willy Tarreau379ceea2021-07-16 16:18:03 +0200493 while (*text == ' ' || *text == '\t')
494 text++;
495
496 if (*text) {
497 ret = -1;
498 memprintf(err, "unexpected character '%c' at the end of conditional expression '%s'",
499 *text, args[0]);
500 goto fail;
501 }
502
Willy Tarreauca818872021-07-16 14:46:09 +0200503 ret = cfg_eval_cond_expr(expr, err);
Willy Tarreauf8690952021-07-16 12:12:00 +0200504 goto done;
505 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200506
Willy Tarreauf8690952021-07-16 12:12:00 +0200507 /* ret == 0, no other way to parse this */
508 ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200509 memprintf(err, "unparsable conditional expression '%s'", args[0]);
Willy Tarreau379ceea2021-07-16 16:18:03 +0200510 fail:
Willy Tarreau66243b42021-07-16 15:39:28 +0200511 if (errptr)
Willy Tarreauf8690952021-07-16 12:12:00 +0200512 *errptr = text;
Willy Tarreau66243b42021-07-16 15:39:28 +0200513 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200514 cfg_free_cond_expr(expr);
Willy Tarreau66243b42021-07-16 15:39:28 +0200515 return ret;
516}