MINOR: cfgcond: insert an expression between the condition and the term
Now evaluating a condition will rely on an expression (or an empty string),
and this expression will support ORing a sub-expression with another
optional expression. The sub-expressions ANDs a term with another optional
sub-expression. With this alone precedence between && and || is respected,
and the following expression:
A && B && C || D || E && F || G
will naturally evaluate as:
(A && B && C) || D || (E && F) || G
diff --git a/doc/configuration.txt b/doc/configuration.txt
index ccb77a5..a66350f 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -805,14 +805,21 @@
Conditions can also be evaluated on startup with the -cc parameter.
See "3. Starting HAProxy" in the management doc.
-The conditions are currently limited to:
+The conditions are either an empty string (which then returns false), or an
+expression made of any combination of:
- - an empty string, always returns "false"
- the integer zero ('0'), always returns "false"
- a non-nul integer (e.g. '1'), always returns "true".
- a predicate optionally followed by argument(s) in parenthesis.
- a question mark ('!') preceeding any of the non-empty elements above, and
which will negate its status.
+ - expressions combined with a logical AND ('&&'), which will be evaluated
+ from left to right until one returns false
+ - expressions combined with a logical OR ('||'), which will be evaluated
+ from right to left until one returns true
+
+Note that like in other languages, the AND operator has precedence over the OR
+operator, so that "A && B || C && D" evalues as "(A && B) || (C && D)".
The list of currently supported predicates is the following:
@@ -854,6 +861,10 @@
.endif
.endif
+ .if streq("$WITH_SSL",yes) && feature(OPENSSL)
+ bind :443 ssl crt ...
+ .endif
+
.if version_atleast(2.4-dev19)
profiling.memory on
.endif
diff --git a/include/haproxy/cfgcond-t.h b/include/haproxy/cfgcond-t.h
index b154cb2..f4f2796 100644
--- a/include/haproxy/cfgcond-t.h
+++ b/include/haproxy/cfgcond-t.h
@@ -77,4 +77,22 @@
};
};
+/* condition sub-expression for an AND:
+ * expr_and = <term> '&&' <expr_and>
+ * | <term>
+ */
+struct cfg_cond_and {
+ struct cfg_cond_term *left;
+ struct cfg_cond_and *right; // may be NULL
+};
+
+/* condition expression:
+ * expr = <expr_and> '||' <expr>
+ * | <expr_and>
+ */
+struct cfg_cond_expr {
+ struct cfg_cond_and *left;
+ struct cfg_cond_expr *right; // may be NULL
+};
+
#endif /* _HAPROXY_CFGCOND_T_H */
diff --git a/include/haproxy/cfgcond.h b/include/haproxy/cfgcond.h
index d17772d..6af88a2 100644
--- a/include/haproxy/cfgcond.h
+++ b/include/haproxy/cfgcond.h
@@ -29,6 +29,15 @@
int cfg_parse_cond_term(const char **text, struct cfg_cond_term **term, char **err, const char **errptr);
int cfg_eval_cond_term(const struct cfg_cond_term *term, char **err);
void cfg_free_cond_term(struct cfg_cond_term **term);
+
+int cfg_parse_cond_and(const char **text, struct cfg_cond_and **expr, char **err, const char **errptr);
+int cfg_eval_cond_and(struct cfg_cond_and *expr, char **err);
+void cfg_free_cond_and(struct cfg_cond_and **expr);
+
+int cfg_parse_cond_expr(const char **text, struct cfg_cond_expr **expr, char **err, const char **errptr);
+int cfg_eval_cond_expr(struct cfg_cond_expr *expr, char **err);
+void cfg_free_cond_expr(struct cfg_cond_expr **expr);
+
int cfg_eval_condition(char **args, char **err, const char **errptr);
#endif
diff --git a/src/cfgcond.c b/src/cfgcond.c
index 5b0aeec..a96832f 100644
--- a/src/cfgcond.c
+++ b/src/cfgcond.c
@@ -206,6 +206,178 @@
}
+/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
+void cfg_free_cond_and(struct cfg_cond_and **expr)
+{
+ while (expr && *expr) {
+ cfg_free_cond_term(&(*expr)->left);
+ expr = &(*expr)->right;
+ }
+}
+
+/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
+void cfg_free_cond_expr(struct cfg_cond_expr **expr)
+{
+ while (expr && *expr) {
+ cfg_free_cond_and(&(*expr)->left);
+ expr = &(*expr)->right;
+ }
+}
+
+/* Parse an indirect input text as a possible config condition sub-expr.
+ * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
+ * success. <expr> is filled with the parsed info, and <text> is updated on
+ * success to point to the first unparsed character, or is left untouched
+ * on failure. On success, the caller will have to free all lower-level
+ * allocated structs using cfg_free_cond_expr(). An error will be set in
+ * <err> on error, and only in this case. In this case the first bad
+ * character will be reported in <errptr>.
+ */
+int cfg_parse_cond_and(const char **text, struct cfg_cond_and **expr, char **err, const char **errptr)
+{
+ struct cfg_cond_and *e;
+ const char *in = *text;
+ int ret = -1;
+
+ if (!*in) /* empty expr does not parse */
+ return 0;
+
+ e = *expr = calloc(1, sizeof(**expr));
+ if (!e) {
+ memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
+ goto done;
+ }
+
+ ret = cfg_parse_cond_term(&in, &e->left, err, errptr);
+ if (ret == -1) // parse error, error already reported
+ goto done;
+
+ if (ret == 0) {
+ /* ret == 0, no other way to parse this */
+ memprintf(err, "unparsable conditional sub-expression '%s'", in);
+ if (errptr)
+ *errptr = in;
+ ret = -1;
+ goto done;
+ }
+
+ /* ret=1, we have a term in the left hand set */
+
+ /* find an optionnal '&&' */
+ while (*in == ' ' || *in == '\t')
+ in++;
+
+ *text = in;
+ if (in[0] != '&' || in[1] != '&')
+ goto done;
+
+ /* we have a '&&', let's parse the right handset's subexp */
+ in += 2;
+ while (*in == ' ' || *in == '\t')
+ in++;
+
+ ret = cfg_parse_cond_and(&in, &e->right, err, errptr);
+ if (ret > 0)
+ *text = in;
+ done:
+ if (ret < 0)
+ cfg_free_cond_and(expr);
+ return ret;
+}
+
+/* Parse an indirect input text as a possible config condition term.
+ * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
+ * success. <expr> is filled with the parsed info, and <text> is updated on
+ * success to point to the first unparsed character, or is left untouched
+ * on failure. On success, the caller will have to free all lower-level
+ * allocated structs using cfg_free_cond_expr(). An error will be set in
+ * <err> on error, and only in this case. In this case the first bad
+ * character will be reported in <errptr>.
+ */
+int cfg_parse_cond_expr(const char **text, struct cfg_cond_expr **expr, char **err, const char **errptr)
+{
+ struct cfg_cond_expr *e;
+ const char *in = *text;
+ int ret = -1;
+
+ if (!*in) /* empty expr does not parse */
+ return 0;
+
+ e = *expr = calloc(1, sizeof(**expr));
+ if (!e) {
+ memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
+ goto done;
+ }
+
+ ret = cfg_parse_cond_and(&in, &e->left, err, errptr);
+ if (ret == -1) // parse error, error already reported
+ goto done;
+
+ if (ret == 0) {
+ /* ret == 0, no other way to parse this */
+ memprintf(err, "unparsable conditional expression '%s'", in);
+ if (errptr)
+ *errptr = in;
+ ret = -1;
+ goto done;
+ }
+
+ /* ret=1, we have a sub-expr in the left hand set */
+
+ /* find an optionnal '||' */
+ while (*in == ' ' || *in == '\t')
+ in++;
+
+ *text = in;
+ if (in[0] != '|' || in[1] != '|')
+ goto done;
+
+ /* we have a '||', let's parse the right handset's subexp */
+ in += 2;
+ while (*in == ' ' || *in == '\t')
+ in++;
+
+ ret = cfg_parse_cond_expr(&in, &e->right, err, errptr);
+ if (ret > 0)
+ *text = in;
+ done:
+ if (ret < 0)
+ cfg_free_cond_expr(expr);
+ return ret;
+}
+
+/* evaluate an sub-expression on a .if/.elif line. The expression is valid and
+ * was already parsed in <expr>. Returns -1 on error (in which case err is
+ * filled with a message, and only in this case), 0 if the condition is false,
+ * 1 if it's true.
+ */
+int cfg_eval_cond_and(struct cfg_cond_and *expr, char **err)
+{
+ int ret;
+
+ /* AND: loop on terms and sub-exp's terms as long as they're TRUE
+ * (stop on FALSE and ERROR).
+ */
+ while ((ret = cfg_eval_cond_term(expr->left, err)) > 0 && expr->right)
+ expr = expr->right;
+ return ret;
+}
+
+/* evaluate an expression on a .if/.elif line. The expression is valid and was
+ * already parsed in <expr>. Returns -1 on error (in which case err is filled
+ * with a message, and only in this case), 0 if the condition is false, 1 if
+ * it's true.
+ */
+int cfg_eval_cond_expr(struct cfg_cond_expr *expr, char **err)
+{
+ int ret;
+
+ /* OR: loop on sub-exps as long as they're FALSE (stop on TRUE and ERROR) */
+ while ((ret = cfg_eval_cond_and(expr->left, err)) == 0 && expr->right)
+ expr = expr->right;
+ return ret;
+}
+
/* evaluate a condition on a .if/.elif line. The condition is already tokenized
* in <err>. Returns -1 on error (in which case err is filled with a message,
* and only in this case), 0 if the condition is false, 1 if it's true. If
@@ -213,14 +385,14 @@
*/
int cfg_eval_condition(char **args, char **err, const char **errptr)
{
- struct cfg_cond_term *term = NULL;
+ struct cfg_cond_expr *expr = NULL;
const char *text = args[0];
int ret = -1;
if (!*text) /* note: empty = false */
return 0;
- ret = cfg_parse_cond_term(&text, &term, err, errptr);
+ ret = cfg_parse_cond_expr(&text, &expr, err, errptr);
if (ret != 0) {
if (ret == -1) // parse error, error already reported
goto done;
@@ -234,7 +406,7 @@
goto fail;
}
- ret = cfg_eval_cond_term(term, err);
+ ret = cfg_eval_cond_expr(expr, err);
goto done;
}
@@ -245,6 +417,6 @@
if (errptr)
*errptr = text;
done:
- cfg_free_cond_term(&term);
+ cfg_free_cond_expr(&expr);
return ret;
}