blob: 5fb7069128acccd9203a526b0ac0728338dd7170 [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[] = {
Remi Tricot-Le Bretonb01179a2021-10-11 15:34:12 +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) },
29 { "ssllib_name_startswith", CFG_PRED_SSLLIB_NAME_STARTSWITH, ARG1(1, STR) },
Willy Tarreau66243b42021-07-16 15:39:28 +020030 { NULL, CFG_PRED_NONE, 0 }
31};
32
33/* looks up a cond predicate matching the keyword in <str>, possibly followed
34 * by a parenthesis. Returns a pointer to it or NULL if not found.
35 */
36const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
37{
38 const struct cond_pred_kw *ret;
39 int len = strcspn(str, " (");
40
41 for (ret = &cond_predicates[0]; ret->word; ret++) {
42 if (len != strlen(ret->word))
43 continue;
44 if (strncmp(str, ret->word, len) != 0)
45 continue;
46 return ret;
47 }
48 return NULL;
49}
50
Willy Tarreau087b2d02021-07-16 14:27:20 +020051/* Frees <term> and its args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +020052void cfg_free_cond_term(struct cfg_cond_term *term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020053{
Willy Tarreauf1db20c2021-07-17 18:46:30 +020054 if (!term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020055 return;
56
Willy Tarreauf1db20c2021-07-17 18:46:30 +020057 if (term->type == CCTT_PAREN) {
58 cfg_free_cond_expr(term->expr);
59 term->expr = NULL;
60 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +020061
Willy Tarreauf1db20c2021-07-17 18:46:30 +020062 free_args(term->args);
63 free(term->args);
64 free(term);
Willy Tarreau087b2d02021-07-16 14:27:20 +020065}
66
Willy Tarreauf8690952021-07-16 12:12:00 +020067/* Parse an indirect input text as a possible config condition term.
68 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
Willy Tarreau087b2d02021-07-16 14:27:20 +020069 * success. <term> is allocated and filled with the parsed info, and <text>
70 * is updated on success to point to the first unparsed character, or is left
71 * untouched on failure. On success, the caller must free <term> using
72 * cfg_free_cond_term(). An error will be set in <err> on error, and only
Willy Tarreauf8690952021-07-16 12:12:00 +020073 * in this case. In this case the first bad character will be reported in
Willy Tarreaudc70c182021-07-20 17:58:34 +020074 * <errptr>. <maxdepth> corresponds to the maximum recursion depth permitted,
75 * it is decremented on each recursive call and the parsing will fail one
76 * reaching <= 0.
Willy Tarreau66243b42021-07-16 15:39:28 +020077 */
Willy Tarreaudc70c182021-07-20 17:58:34 +020078int 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 +020079{
Willy Tarreau087b2d02021-07-16 14:27:20 +020080 struct cfg_cond_term *t;
Willy Tarreauf8690952021-07-16 12:12:00 +020081 const char *in = *text;
Willy Tarreau66243b42021-07-16 15:39:28 +020082 const char *end_ptr;
Willy Tarreau66243b42021-07-16 15:39:28 +020083 int err_arg;
84 int nbargs;
Willy Tarreau66243b42021-07-16 15:39:28 +020085 char *end;
86 long val;
87
Willy Tarreauf8690952021-07-16 12:12:00 +020088 while (*in == ' ' || *in == '\t')
89 in++;
90
91 if (!*in) /* empty term does not parse */
Willy Tarreau66243b42021-07-16 15:39:28 +020092 return 0;
93
Willy Tarreaudc70c182021-07-20 17:58:34 +020094 *term = NULL;
95 if (maxdepth <= 0)
96 goto fail0;
97
Willy Tarreau087b2d02021-07-16 14:27:20 +020098 t = *term = calloc(1, sizeof(**term));
99 if (!t) {
100 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
101 goto fail1;
102 }
103
104 t->type = CCTT_NONE;
105 t->args = NULL;
106 t->neg = 0;
107
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200108 /* !<term> negates the term. White spaces permitted */
109 while (*in == '!') {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200110 t->neg = !t->neg;
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200111 do { in++; } while (*in == ' ' || *in == '\t');
112 }
113
Willy Tarreauf8690952021-07-16 12:12:00 +0200114 val = strtol(in, &end, 0);
115 if (end != in) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200116 t->type = val ? CCTT_TRUE : CCTT_FALSE;
Willy Tarreauf8690952021-07-16 12:12:00 +0200117 *text = end;
118 return 1;
119 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200120
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200121 /* Try to parse '(' EXPR ')' */
122 if (*in == '(') {
123 int ret;
124
125 t->type = CCTT_PAREN;
126 t->args = NULL;
127
128 do { in++; } while (*in == ' ' || *in == '\t');
Willy Tarreaudc70c182021-07-20 17:58:34 +0200129 ret = cfg_parse_cond_expr(&in, &t->expr, err, errptr, maxdepth - 1);
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200130 if (ret == -1)
131 goto fail2;
132 if (ret == 0)
133 goto fail0;
134
135 /* find the closing ')' */
136 while (*in == ' ' || *in == '\t')
137 in++;
138 if (*in != ')') {
139 memprintf(err, "expected ')' after conditional expression '%s'", *text);
140 goto fail1;
141 }
142 do { in++; } while (*in == ' ' || *in == '\t');
143 *text = in;
144 return 1;
145 }
146
Willy Tarreau66243b42021-07-16 15:39:28 +0200147 /* below we'll likely all make_arg_list() so we must return only via
148 * the <done> label which frees the arg list.
149 */
Willy Tarreau087b2d02021-07-16 14:27:20 +0200150 t->pred = cfg_lookup_cond_pred(in);
151 if (t->pred) {
152 t->type = CCTT_PRED;
153 nbargs = make_arg_list(in + strlen(t->pred->word), -1,
154 t->pred->arg_mask, &t->args, err,
Willy Tarreau66243b42021-07-16 15:39:28 +0200155 &end_ptr, &err_arg, NULL);
Willy Tarreau66243b42021-07-16 15:39:28 +0200156 if (nbargs < 0) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200157 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 +0200158 if (errptr)
159 *errptr = end_ptr;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200160 goto fail2;
Willy Tarreau66243b42021-07-16 15:39:28 +0200161 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200162 *text = end_ptr;
163 return 1;
164 }
165
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200166 fail0:
Willy Tarreauf8690952021-07-16 12:12:00 +0200167 memprintf(err, "unparsable conditional expression '%s'", *text);
Willy Tarreau087b2d02021-07-16 14:27:20 +0200168 fail1:
Willy Tarreauf8690952021-07-16 12:12:00 +0200169 if (errptr)
170 *errptr = *text;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200171 fail2:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200172 cfg_free_cond_term(*term);
173 *term = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200174 return -1;
175}
176
177/* evaluate a condition term on a .if/.elif line. The condition was already
178 * parsed in <term>. Returns -1 on error (in which case err is filled with a
179 * message, and only in this case), 0 if the condition is false, 1 if it's
180 * true.
181 */
182int cfg_eval_cond_term(const struct cfg_cond_term *term, char **err)
183{
184 int ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200185
Willy Tarreauf8690952021-07-16 12:12:00 +0200186 if (term->type == CCTT_FALSE)
187 ret = 0;
188 else if (term->type == CCTT_TRUE)
189 ret = 1;
190 else if (term->type == CCTT_PRED) {
191 /* here we know we have a valid predicate with valid arguments
192 * placed in term->args (which the caller will free).
Willy Tarreau66243b42021-07-16 15:39:28 +0200193 */
Willy Tarreauf8690952021-07-16 12:12:00 +0200194 switch (term->pred->prd) {
Willy Tarreau66243b42021-07-16 15:39:28 +0200195 case CFG_PRED_DEFINED: // checks if arg exists as an environment variable
Willy Tarreauf8690952021-07-16 12:12:00 +0200196 ret = getenv(term->args[0].data.str.area) != NULL;
197 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200198
199 case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
200 const char *p;
201
Willy Tarreauf8690952021-07-16 12:12:00 +0200202 ret = 0; // assume feature not found
203 for (p = build_features; (p = strstr(p, term->args[0].data.str.area)); p++) {
204 if (p > build_features &&
205 (p[term->args[0].data.str.data] == ' ' ||
206 p[term->args[0].data.str.data] == 0)) {
207 if (*(p-1) == '+') { // e.g. "+OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200208 ret = 1;
Willy Tarreauf8690952021-07-16 12:12:00 +0200209 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200210 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200211 else if (*(p-1) == '-') { // e.g. "-OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200212 ret = 0;
Willy Tarreauf8690952021-07-16 12:12:00 +0200213 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200214 }
215 /* it was a sub-word, let's restart from next place */
216 }
217 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200218 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200219 }
220 case CFG_PRED_STREQ: // checks if the two arg are equal
Willy Tarreauf8690952021-07-16 12:12:00 +0200221 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) == 0;
222 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200223
224 case CFG_PRED_STRNEQ: // checks if the two arg are different
Willy Tarreauf8690952021-07-16 12:12:00 +0200225 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) != 0;
226 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200227
228 case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200229 ret = compare_current_version(term->args[0].data.str.area) <= 0;
230 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200231
232 case CFG_PRED_VERSION_BEFORE: // checks if the current version is older than this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200233 ret = compare_current_version(term->args[0].data.str.area) > 0;
234 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200235
William Lallemand3aeb3f92021-08-21 23:59:56 +0200236 case CFG_PRED_OSSL_VERSION_ATLEAST: { // checks if the current openssl version is at least this one
237 int opensslret = openssl_compare_current_version(term->args[0].data.str.area);
238
239 if (opensslret < -1) /* can't parse the string or no openssl available */
240 ret = -1;
241 else
242 ret = opensslret <= 0;
243 break;
244 }
245 case CFG_PRED_OSSL_VERSION_BEFORE: { // checks if the current openssl version is older than this one
246 int opensslret = openssl_compare_current_version(term->args[0].data.str.area);
247
248 if (opensslret < -1) /* can't parse the string or no openssl available */
249 ret = -1;
250 else
251 ret = opensslret > 0;
252 break;
253 }
Remi Tricot-Le Bretonb01179a2021-10-11 15:34:12 +0200254 case CFG_PRED_SSLLIB_NAME_STARTSWITH: { // checks if the current SSL library's name starts with a specified string (can be used to distinguish OpenSSL from LibreSSL or BoringSSL)
255 ret = openssl_compare_current_name(term->args[0].data.str.area) == 0;
256 break;
257 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200258 default:
Willy Tarreauf8690952021-07-16 12:12:00 +0200259 memprintf(err, "internal error: unhandled conditional expression predicate '%s'", term->pred->word);
260 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200261 }
262 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200263 else if (term->type == CCTT_PAREN) {
264 ret = cfg_eval_cond_expr(term->expr, err);
265 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200266 else {
267 memprintf(err, "internal error: unhandled condition term type %d", (int)term->type);
268 }
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200269
270 if (ret >= 0 && term->neg)
271 ret = !ret;
Willy Tarreauf8690952021-07-16 12:12:00 +0200272 return ret;
273}
274
275
Willy Tarreauca818872021-07-16 14:46:09 +0200276/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200277void cfg_free_cond_and(struct cfg_cond_and *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200278{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200279 struct cfg_cond_and *prev;
280
281 while (expr) {
282 cfg_free_cond_term(expr->left);
283 prev = expr;
284 expr = expr->right;
285 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200286 }
287}
288
289/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200290void cfg_free_cond_expr(struct cfg_cond_expr *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200291{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200292 struct cfg_cond_expr *prev;
293
294 while (expr) {
295 cfg_free_cond_and(expr->left);
296 prev = expr;
297 expr = expr->right;
298 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200299 }
300}
301
302/* Parse an indirect input text as a possible config condition sub-expr.
303 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
304 * success. <expr> is filled with the parsed info, and <text> is updated on
305 * success to point to the first unparsed character, or is left untouched
306 * on failure. On success, the caller will have to free all lower-level
307 * allocated structs using cfg_free_cond_expr(). An error will be set in
308 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200309 * character will be reported in <errptr>. <maxdepth> corresponds to the
310 * maximum recursion depth permitted, it is decremented on each recursive
311 * call and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200312 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200313int 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 +0200314{
315 struct cfg_cond_and *e;
316 const char *in = *text;
317 int ret = -1;
318
319 if (!*in) /* empty expr does not parse */
320 return 0;
321
Willy Tarreaudc70c182021-07-20 17:58:34 +0200322 *expr = NULL;
323 if (maxdepth <= 0) {
324 memprintf(err, "unparsable conditional sub-expression '%s'", in);
325 if (errptr)
326 *errptr = in;
327 goto done;
328 }
329
Willy Tarreauca818872021-07-16 14:46:09 +0200330 e = *expr = calloc(1, sizeof(**expr));
331 if (!e) {
332 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
333 goto done;
334 }
335
Willy Tarreaudc70c182021-07-20 17:58:34 +0200336 ret = cfg_parse_cond_term(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200337 if (ret == -1) // parse error, error already reported
338 goto done;
339
340 if (ret == 0) {
341 /* ret == 0, no other way to parse this */
342 memprintf(err, "unparsable conditional sub-expression '%s'", in);
343 if (errptr)
344 *errptr = in;
345 ret = -1;
346 goto done;
347 }
348
349 /* ret=1, we have a term in the left hand set */
350
Ilya Shipitsin01881082021-08-07 14:41:56 +0500351 /* find an optional '&&' */
Willy Tarreauca818872021-07-16 14:46:09 +0200352 while (*in == ' ' || *in == '\t')
353 in++;
354
355 *text = in;
356 if (in[0] != '&' || in[1] != '&')
357 goto done;
358
359 /* we have a '&&', let's parse the right handset's subexp */
360 in += 2;
361 while (*in == ' ' || *in == '\t')
362 in++;
363
Willy Tarreaudc70c182021-07-20 17:58:34 +0200364 ret = cfg_parse_cond_and(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200365 if (ret > 0)
366 *text = in;
367 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200368 if (ret < 0) {
369 cfg_free_cond_and(*expr);
370 *expr = NULL;
371 }
Willy Tarreauca818872021-07-16 14:46:09 +0200372 return ret;
373}
374
375/* Parse an indirect input text as a possible config condition term.
376 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
377 * success. <expr> is filled with the parsed info, and <text> is updated on
378 * success to point to the first unparsed character, or is left untouched
379 * on failure. On success, the caller will have to free all lower-level
380 * allocated structs using cfg_free_cond_expr(). An error will be set in
381 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200382 * character will be reported in <errptr>. <maxdepth> corresponds to the
383 * maximum recursion depth permitted, it is decremented on each recursive call
384 * and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200385 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200386int 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 +0200387{
388 struct cfg_cond_expr *e;
389 const char *in = *text;
390 int ret = -1;
391
392 if (!*in) /* empty expr does not parse */
393 return 0;
394
Willy Tarreaudc70c182021-07-20 17:58:34 +0200395 *expr = NULL;
396 if (maxdepth <= 0) {
397 memprintf(err, "unparsable conditional expression '%s'", in);
398 if (errptr)
399 *errptr = in;
400 goto done;
401 }
402
Willy Tarreauca818872021-07-16 14:46:09 +0200403 e = *expr = calloc(1, sizeof(**expr));
404 if (!e) {
405 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
406 goto done;
407 }
408
Willy Tarreaudc70c182021-07-20 17:58:34 +0200409 ret = cfg_parse_cond_and(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200410 if (ret == -1) // parse error, error already reported
411 goto done;
412
413 if (ret == 0) {
414 /* ret == 0, no other way to parse this */
415 memprintf(err, "unparsable conditional expression '%s'", in);
416 if (errptr)
417 *errptr = in;
418 ret = -1;
419 goto done;
420 }
421
422 /* ret=1, we have a sub-expr in the left hand set */
423
Ilya Shipitsin01881082021-08-07 14:41:56 +0500424 /* find an optional '||' */
Willy Tarreauca818872021-07-16 14:46:09 +0200425 while (*in == ' ' || *in == '\t')
426 in++;
427
428 *text = in;
429 if (in[0] != '|' || in[1] != '|')
430 goto done;
431
432 /* we have a '||', let's parse the right handset's subexp */
433 in += 2;
434 while (*in == ' ' || *in == '\t')
435 in++;
436
Willy Tarreaudc70c182021-07-20 17:58:34 +0200437 ret = cfg_parse_cond_expr(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200438 if (ret > 0)
439 *text = in;
440 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200441 if (ret < 0) {
442 cfg_free_cond_expr(*expr);
443 *expr = NULL;
444 }
Willy Tarreauca818872021-07-16 14:46:09 +0200445 return ret;
446}
447
448/* evaluate an sub-expression on a .if/.elif line. The expression is valid and
449 * was already parsed in <expr>. Returns -1 on error (in which case err is
450 * filled with a message, and only in this case), 0 if the condition is false,
451 * 1 if it's true.
452 */
453int cfg_eval_cond_and(struct cfg_cond_and *expr, char **err)
454{
455 int ret;
456
457 /* AND: loop on terms and sub-exp's terms as long as they're TRUE
458 * (stop on FALSE and ERROR).
459 */
460 while ((ret = cfg_eval_cond_term(expr->left, err)) > 0 && expr->right)
461 expr = expr->right;
462 return ret;
463}
464
465/* evaluate an expression on a .if/.elif line. The expression is valid and was
466 * already parsed in <expr>. Returns -1 on error (in which case err is filled
467 * with a message, and only in this case), 0 if the condition is false, 1 if
468 * it's true.
469 */
470int cfg_eval_cond_expr(struct cfg_cond_expr *expr, char **err)
471{
472 int ret;
473
474 /* OR: loop on sub-exps as long as they're FALSE (stop on TRUE and ERROR) */
475 while ((ret = cfg_eval_cond_and(expr->left, err)) == 0 && expr->right)
476 expr = expr->right;
477 return ret;
478}
479
Willy Tarreauf8690952021-07-16 12:12:00 +0200480/* evaluate a condition on a .if/.elif line. The condition is already tokenized
481 * in <err>. Returns -1 on error (in which case err is filled with a message,
482 * and only in this case), 0 if the condition is false, 1 if it's true. If
483 * <errptr> is not NULL, it's set to the first invalid character on error.
484 */
485int cfg_eval_condition(char **args, char **err, const char **errptr)
486{
Willy Tarreauca818872021-07-16 14:46:09 +0200487 struct cfg_cond_expr *expr = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200488 const char *text = args[0];
489 int ret = -1;
490
491 if (!*text) /* note: empty = false */
492 return 0;
493
Willy Tarreaudc70c182021-07-20 17:58:34 +0200494 ret = cfg_parse_cond_expr(&text, &expr, err, errptr, MAX_CFG_RECURSION);
Willy Tarreauf8690952021-07-16 12:12:00 +0200495 if (ret != 0) {
496 if (ret == -1) // parse error, error already reported
497 goto done;
Willy Tarreau379ceea2021-07-16 16:18:03 +0200498 while (*text == ' ' || *text == '\t')
499 text++;
500
501 if (*text) {
502 ret = -1;
503 memprintf(err, "unexpected character '%c' at the end of conditional expression '%s'",
504 *text, args[0]);
505 goto fail;
506 }
507
Willy Tarreauca818872021-07-16 14:46:09 +0200508 ret = cfg_eval_cond_expr(expr, err);
Willy Tarreauf8690952021-07-16 12:12:00 +0200509 goto done;
510 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200511
Willy Tarreauf8690952021-07-16 12:12:00 +0200512 /* ret == 0, no other way to parse this */
513 ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200514 memprintf(err, "unparsable conditional expression '%s'", args[0]);
Willy Tarreau379ceea2021-07-16 16:18:03 +0200515 fail:
Willy Tarreau66243b42021-07-16 15:39:28 +0200516 if (errptr)
Willy Tarreauf8690952021-07-16 12:12:00 +0200517 *errptr = text;
Willy Tarreau66243b42021-07-16 15:39:28 +0200518 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200519 cfg_free_cond_expr(expr);
Willy Tarreau66243b42021-07-16 15:39:28 +0200520 return ret;
521}