]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: config: move the condition preprocessing code to its own file
authorWilly Tarreau <w@1wt.eu>
Fri, 16 Jul 2021 13:39:28 +0000 (15:39 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 16 Jul 2021 17:18:41 +0000 (19:18 +0200)
The .if/.else/.endif and condition evaluation code is quite dirty and
was dumped into cfgparse.c because it was easy. But it should be tidied
quite a bit as it will need to evolve.

Let's move all that to cfgcond.{c,h}.

Makefile
include/haproxy/cfgcond-t.h [new file with mode: 0644]
include/haproxy/cfgcond.h [new file with mode: 0644]
include/haproxy/cfgparse.h
src/cfgcond.c [new file with mode: 0644]
src/cfgparse.c
src/haproxy.c

index 4b067676796bea8bd3cdf252444344ca4b4207c5..d598e60acae48a217f39c6727f2b2194d507fd76 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -868,7 +868,7 @@ OBJS += src/mux_h2.o src/mux_fcgi.o src/http_ana.o src/mux_h1.o src/stream.o   \
         src/listener.o src/dns.o src/connection.o src/tcp_rules.o src/debug.o  \
         src/sink.o src/payload.o src/mux_pt.o src/filters.o src/fcgi-app.o     \
         src/server_state.o src/vars.o src/map.o src/cfgparse-global.o          \
-        src/task.o src/flt_http_comp.o src/session.o src/sock.o                \
+        src/task.o src/flt_http_comp.o src/session.o src/sock.o src/cfgcond.o  \
         src/flt_trace.o src/acl.o src/trace.o src/http_rules.o src/queue.o     \
         src/mjson.o src/h2.o src/h1.o src/mworker.o src/lb_chash.o src/ring.o  \
         src/activity.o src/tcp_sample.o src/proto_tcp.o src/htx.o src/h1_htx.o \
diff --git a/include/haproxy/cfgcond-t.h b/include/haproxy/cfgcond-t.h
new file mode 100644 (file)
index 0000000..04c8df1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * include/haproxy/cfgcond-t.h
+ * Types for the configuration condition preprocessor
+ *
+ * Copyright (C) 2000-2021 Willy Tarreau - w@1wt.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_CFGCOND_T_H
+#define _HAPROXY_CFGCOND_T_H
+
+#include <haproxy/api-t.h>
+
+/* nested if/elif/else/endif block states */
+enum nested_cond_state {
+       NESTED_COND_IF_TAKE,      // "if" with a true condition
+       NESTED_COND_IF_DROP,      // "if" with a false condition
+       NESTED_COND_IF_SKIP,      // "if" masked by an outer false condition
+
+       NESTED_COND_ELIF_TAKE,    // "elif" with a true condition from a false one
+       NESTED_COND_ELIF_DROP,    // "elif" with a false condition from a false one
+       NESTED_COND_ELIF_SKIP,    // "elif" masked by an outer false condition or a previously taken if
+
+       NESTED_COND_ELSE_TAKE,    // taken "else" after an if false condition
+       NESTED_COND_ELSE_DROP,    // "else" masked by outer false condition or an if true condition
+};
+
+/* 100 levels of nested conditions should already be sufficient */
+#define MAXNESTEDCONDS 100
+
+/* supported conditional predicates for .if/.elif */
+enum cond_predicate {
+       CFG_PRED_NONE,            // none
+       CFG_PRED_DEFINED,         // "defined"
+       CFG_PRED_FEATURE,         // "feature"
+       CFG_PRED_STREQ,           // "streq"
+       CFG_PRED_STRNEQ,          // "strneq"
+       CFG_PRED_VERSION_ATLEAST, // "version_atleast"
+       CFG_PRED_VERSION_BEFORE,  // "version_before"
+};
+
+/* keyword for a condition predicate */
+struct cond_pred_kw {
+       const char *word;         // NULL marks the end of the list
+       enum cond_predicate prd;  // one of the CFG_PRED_* above
+       uint64_t arg_mask;        // mask of supported arguments (strings only)
+};
+
+#endif /* _HAPROXY_CFGCOND_T_H */
diff --git a/include/haproxy/cfgcond.h b/include/haproxy/cfgcond.h
new file mode 100644 (file)
index 0000000..bf8f58d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * include/haproxy/cfgcond.h
+ * Configuration condition preprocessor
+ *
+ * Copyright (C) 2000-2021 Willy Tarreau - w@1wt.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_CFGCOND_H
+#define _HAPROXY_CFGCOND_H
+
+#include <haproxy/api.h>
+#include <haproxy/cfgcond-t.h>
+
+const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str);
+int cfg_eval_condition(char **args, char **err, const char **errptr);
+
+#endif
index 51e5b9edfe28d4826d88d32e12e285027befc6e5..1c97a88b92a6176077059dff67be4cb24a69d678 100644 (file)
@@ -126,7 +126,6 @@ void free_email_alert(struct proxy *p);
 const char *cfg_find_best_match(const char *word, const struct list *list, int section, const char **extra);
 int warnifnotcap(struct proxy *proxy, int cap, const char *file, int line, const char *arg, const char *hint);
 int failifnotcap(struct proxy *proxy, int cap, const char *file, int line, const char *arg, const char *hint);
-int cfg_eval_condition(char **args, char **err, const char **errptr);
 
 /* simplified way to define a section parser */
 #define REGISTER_CONFIG_SECTION(name, parse, post)                            \
diff --git a/src/cfgcond.c b/src/cfgcond.c
new file mode 100644 (file)
index 0000000..d3c087b
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Configuration condition preprocessor
+ *
+ * Copyright 2000-2021 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <haproxy/api.h>
+#include <haproxy/arg.h>
+#include <haproxy/cfgcond.h>
+#include <haproxy/global.h>
+#include <haproxy/tools.h>
+
+/* supported condition predicates */
+const struct cond_pred_kw cond_predicates[] = {
+       { "defined",          CFG_PRED_DEFINED,         ARG1(1, STR)         },
+       { "feature",          CFG_PRED_FEATURE,         ARG1(1, STR)         },
+       { "streq",            CFG_PRED_STREQ,           ARG2(2, STR, STR)    },
+       { "strneq",           CFG_PRED_STRNEQ,          ARG2(2, STR, STR)    },
+       { "version_atleast",  CFG_PRED_VERSION_ATLEAST, ARG1(1, STR)         },
+       { "version_before",   CFG_PRED_VERSION_BEFORE,  ARG1(1, STR)         },
+       { NULL, CFG_PRED_NONE, 0 }
+};
+
+/* looks up a cond predicate matching the keyword in <str>, possibly followed
+ * by a parenthesis. Returns a pointer to it or NULL if not found.
+ */
+const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
+{
+       const struct cond_pred_kw *ret;
+       int len = strcspn(str, " (");
+
+       for (ret = &cond_predicates[0]; ret->word; ret++) {
+               if (len != strlen(ret->word))
+                       continue;
+               if (strncmp(str, ret->word, len) != 0)
+                       continue;
+               return ret;
+       }
+       return NULL;
+}
+
+/* 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
+ * <errptr> is not NULL, it's set to the first invalid character on error.
+ */
+int cfg_eval_condition(char **args, char **err, const char **errptr)
+{
+       const struct cond_pred_kw *cond_pred = NULL;
+       const char *end_ptr;
+       struct arg *argp = NULL;
+       int err_arg;
+       int nbargs;
+       int ret = -1;
+       char *end;
+       long val;
+
+       if (!*args[0]) /* note: empty = false */
+               return 0;
+
+       val = strtol(args[0], &end, 0);
+       if (end && *end == '\0')
+               return val != 0;
+
+       /* below we'll likely all make_arg_list() so we must return only via
+        * the <done> label which frees the arg list.
+        */
+       cond_pred = cfg_lookup_cond_pred(args[0]);
+       if (cond_pred) {
+               nbargs = make_arg_list(args[0] + strlen(cond_pred->word), -1,
+                                      cond_pred->arg_mask, &argp, err,
+                                      &end_ptr, &err_arg, NULL);
+
+               if (nbargs < 0) {
+                       memprintf(err, "%s in argument %d of predicate '%s' used in conditional expression", *err, err_arg, cond_pred->word);
+                       if (errptr)
+                               *errptr = end_ptr;
+                       goto done;
+               }
+
+               /* here we know we have a valid predicate with <nbargs> valid
+                * arguments, placed in <argp> (which we'll need to free).
+                */
+               switch (cond_pred->prd) {
+               case CFG_PRED_DEFINED:  // checks if arg exists as an environment variable
+                       ret = getenv(argp[0].data.str.area) != NULL;
+                       goto done;
+
+               case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
+                       const char *p;
+
+                       for (p = build_features; (p = strstr(p, argp[0].data.str.area)); p++) {
+                               if ((p[argp[0].data.str.data] == ' ' || p[argp[0].data.str.data] == 0) &&
+                                   p > build_features) {
+                                       if (*(p-1) == '+') { // "+OPENSSL"
+                                               ret = 1;
+                                               goto done;
+                                       }
+                                       else if (*(p-1) == '-') { // "-OPENSSL"
+                                               ret = 0;
+                                               goto done;
+                                       }
+                                       /* it was a sub-word, let's restart from next place */
+                               }
+                       }
+                       /* not found */
+                       ret = 0;
+                       goto done;
+               }
+               case CFG_PRED_STREQ:    // checks if the two arg are equal
+                       ret = strcmp(argp[0].data.str.area, argp[1].data.str.area) == 0;
+                       goto done;
+
+               case CFG_PRED_STRNEQ:   // checks if the two arg are different
+                       ret = strcmp(argp[0].data.str.area, argp[1].data.str.area) != 0;
+                       goto done;
+
+               case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
+                       ret = compare_current_version(argp[0].data.str.area) <= 0;
+                       goto done;
+
+               case CFG_PRED_VERSION_BEFORE:  // checks if the current version is older than this one
+                       ret = compare_current_version(argp[0].data.str.area) > 0;
+                       goto done;
+
+               default:
+                       memprintf(err, "internal error: unhandled conditional expression predicate '%s'", cond_pred->word);
+                       if (errptr)
+                               *errptr = args[0];
+                       goto done;
+               }
+       }
+
+       memprintf(err, "unparsable conditional expression '%s'", args[0]);
+       if (errptr)
+               *errptr = args[0];
+ done:
+       free_args(argp);
+       ha_free(&argp);
+       return ret;
+}
index a1b9a47640e970ec3dd7da1e6f25f8bdcf2f0454..4cee8a7b6c4a9055ed9be4ba95fb8cb7e401164a 100644 (file)
@@ -41,6 +41,7 @@
 #include <haproxy/auth.h>
 #include <haproxy/backend.h>
 #include <haproxy/capture.h>
+#include <haproxy/cfgcond.h>
 #include <haproxy/cfgparse.h>
 #include <haproxy/channel.h>
 #include <haproxy/check.h>
@@ -116,51 +117,6 @@ struct cfg_kw_list cfg_keywords = {
        .list = LIST_HEAD_INIT(cfg_keywords.list)
 };
 
-/* nested if/elif/else/endif block states */
-enum nested_cond_state {
-       NESTED_COND_IF_TAKE,      // "if" with a true condition
-       NESTED_COND_IF_DROP,      // "if" with a false condition
-       NESTED_COND_IF_SKIP,      // "if" masked by an outer false condition
-
-       NESTED_COND_ELIF_TAKE,    // "elif" with a true condition from a false one
-       NESTED_COND_ELIF_DROP,    // "elif" with a false condition from a false one
-       NESTED_COND_ELIF_SKIP,    // "elif" masked by an outer false condition or a previously taken if
-
-       NESTED_COND_ELSE_TAKE,    // taken "else" after an if false condition
-       NESTED_COND_ELSE_DROP,    // "else" masked by outer false condition or an if true condition
-};
-
-/* 100 levels of nested conditions should already be sufficient */
-#define MAXNESTEDCONDS 100
-
-/* supported conditional predicates for .if/.elif */
-enum cond_predicate {
-       CFG_PRED_NONE,            // none
-       CFG_PRED_DEFINED,         // "defined"
-       CFG_PRED_FEATURE,         // "feature"
-       CFG_PRED_STREQ,           // "streq"
-       CFG_PRED_STRNEQ,          // "strneq"
-       CFG_PRED_VERSION_ATLEAST, // "version_atleast"
-       CFG_PRED_VERSION_BEFORE,  // "version_before"
-};
-
-struct cond_pred_kw {
-       const char *word;         // NULL marks the end of the list
-       enum cond_predicate prd;  // one of the CFG_PRED_* above
-       uint64_t arg_mask;        // mask of supported arguments (strings only)
-};
-
-/* supported condition predicates */
-const struct cond_pred_kw cond_predicates[] = {
-       { "defined",          CFG_PRED_DEFINED,         ARG1(1, STR)         },
-       { "feature",          CFG_PRED_FEATURE,         ARG1(1, STR)         },
-       { "streq",            CFG_PRED_STREQ,           ARG2(2, STR, STR)    },
-       { "strneq",           CFG_PRED_STRNEQ,          ARG2(2, STR, STR)    },
-       { "version_atleast",  CFG_PRED_VERSION_ATLEAST, ARG1(1, STR)         },
-       { "version_before",   CFG_PRED_VERSION_BEFORE,  ARG1(1, STR)         },
-       { NULL, CFG_PRED_NONE, 0 }
-};
-
 /*
  * converts <str> to a list of listeners which are dynamically allocated.
  * The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where :
@@ -1713,125 +1669,6 @@ static int cfg_parse_global_def_path(char **args, int section_type, struct proxy
        return ret;
 }
 
-/* looks up a cond predicate matching the keyword in <str>, possibly followed
- * by a parenthesis. Returns a pointer to it or NULL if not found.
- */
-static const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
-{
-       const struct cond_pred_kw *ret;
-       int len = strcspn(str, " (");
-
-       for (ret = &cond_predicates[0]; ret->word; ret++) {
-               if (len != strlen(ret->word))
-                       continue;
-               if (strncmp(str, ret->word, len) != 0)
-                       continue;
-               return ret;
-       }
-       return NULL;
-}
-
-/* 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
- * <errptr> is not NULL, it's set to the first invalid character on error.
- */
-int cfg_eval_condition(char **args, char **err, const char **errptr)
-{
-       const struct cond_pred_kw *cond_pred = NULL;
-       const char *end_ptr;
-       struct arg *argp = NULL;
-       int err_arg;
-       int nbargs;
-       int ret = -1;
-       char *end;
-       long val;
-
-       if (!*args[0]) /* note: empty = false */
-               return 0;
-
-       val = strtol(args[0], &end, 0);
-       if (end && *end == '\0')
-               return val != 0;
-
-       /* below we'll likely all make_arg_list() so we must return only via
-        * the <done> label which frees the arg list.
-        */
-       cond_pred = cfg_lookup_cond_pred(args[0]);
-       if (cond_pred) {
-               nbargs = make_arg_list(args[0] + strlen(cond_pred->word), -1,
-                                      cond_pred->arg_mask, &argp, err,
-                                      &end_ptr, &err_arg, NULL);
-
-               if (nbargs < 0) {
-                       memprintf(err, "%s in argument %d of predicate '%s' used in conditional expression", *err, err_arg, cond_pred->word);
-                       if (errptr)
-                               *errptr = end_ptr;
-                       goto done;
-               }
-
-               /* here we know we have a valid predicate with <nbargs> valid
-                * arguments, placed in <argp> (which we'll need to free).
-                */
-               switch (cond_pred->prd) {
-               case CFG_PRED_DEFINED:  // checks if arg exists as an environment variable
-                       ret = getenv(argp[0].data.str.area) != NULL;
-                       goto done;
-
-               case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
-                       const char *p;
-
-                       for (p = build_features; (p = strstr(p, argp[0].data.str.area)); p++) {
-                               if ((p[argp[0].data.str.data] == ' ' || p[argp[0].data.str.data] == 0) &&
-                                   p > build_features) {
-                                       if (*(p-1) == '+') { // "+OPENSSL"
-                                               ret = 1;
-                                               goto done;
-                                       }
-                                       else if (*(p-1) == '-') { // "-OPENSSL"
-                                               ret = 0;
-                                               goto done;
-                                       }
-                                       /* it was a sub-word, let's restart from next place */
-                               }
-                       }
-                       /* not found */
-                       ret = 0;
-                       goto done;
-               }
-               case CFG_PRED_STREQ:    // checks if the two arg are equal
-                       ret = strcmp(argp[0].data.str.area, argp[1].data.str.area) == 0;
-                       goto done;
-
-               case CFG_PRED_STRNEQ:   // checks if the two arg are different
-                       ret = strcmp(argp[0].data.str.area, argp[1].data.str.area) != 0;
-                       goto done;
-
-               case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
-                       ret = compare_current_version(argp[0].data.str.area) <= 0;
-                       goto done;
-
-               case CFG_PRED_VERSION_BEFORE:  // checks if the current version is older than this one
-                       ret = compare_current_version(argp[0].data.str.area) > 0;
-                       goto done;
-
-               default:
-                       memprintf(err, "internal error: unhandled conditional expression predicate '%s'", cond_pred->word);
-                       if (errptr)
-                               *errptr = args[0];
-                       goto done;
-               }
-       }
-
-       memprintf(err, "unparsable conditional expression '%s'", args[0]);
-       if (errptr)
-               *errptr = args[0];
- done:
-       free_args(argp);
-       ha_free(&argp);
-       return ret;
-}
-
 /*
  * This function reads and parses the configuration file given in the argument.
  * Returns the error code, 0 if OK, -1 if the config file couldn't be opened,
index 2d32bb84f6973599fe8ef89c6994007eef6cb004..c863e13ec70e53abb7a382b54b1d54f41051b4e1 100644 (file)
@@ -87,6 +87,7 @@
 #include <haproxy/auth.h>
 #include <haproxy/base64.h>
 #include <haproxy/capture-t.h>
+#include <haproxy/cfgcond.h>
 #include <haproxy/cfgdiag.h>
 #include <haproxy/cfgparse.h>
 #include <haproxy/chunk.h>