blob: d3c087b6648daae4c17c4929b05925c3796bfce1 [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[] = {
21 { "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 { NULL, CFG_PRED_NONE, 0 }
28};
29
30/* looks up a cond predicate matching the keyword in <str>, possibly followed
31 * by a parenthesis. Returns a pointer to it or NULL if not found.
32 */
33const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
34{
35 const struct cond_pred_kw *ret;
36 int len = strcspn(str, " (");
37
38 for (ret = &cond_predicates[0]; ret->word; ret++) {
39 if (len != strlen(ret->word))
40 continue;
41 if (strncmp(str, ret->word, len) != 0)
42 continue;
43 return ret;
44 }
45 return NULL;
46}
47
48/* evaluate a condition on a .if/.elif line. The condition is already tokenized
49 * in <err>. Returns -1 on error (in which case err is filled with a message,
50 * and only in this case), 0 if the condition is false, 1 if it's true. If
51 * <errptr> is not NULL, it's set to the first invalid character on error.
52 */
53int cfg_eval_condition(char **args, char **err, const char **errptr)
54{
55 const struct cond_pred_kw *cond_pred = NULL;
56 const char *end_ptr;
57 struct arg *argp = NULL;
58 int err_arg;
59 int nbargs;
60 int ret = -1;
61 char *end;
62 long val;
63
64 if (!*args[0]) /* note: empty = false */
65 return 0;
66
67 val = strtol(args[0], &end, 0);
68 if (end && *end == '\0')
69 return val != 0;
70
71 /* below we'll likely all make_arg_list() so we must return only via
72 * the <done> label which frees the arg list.
73 */
74 cond_pred = cfg_lookup_cond_pred(args[0]);
75 if (cond_pred) {
76 nbargs = make_arg_list(args[0] + strlen(cond_pred->word), -1,
77 cond_pred->arg_mask, &argp, err,
78 &end_ptr, &err_arg, NULL);
79
80 if (nbargs < 0) {
81 memprintf(err, "%s in argument %d of predicate '%s' used in conditional expression", *err, err_arg, cond_pred->word);
82 if (errptr)
83 *errptr = end_ptr;
84 goto done;
85 }
86
87 /* here we know we have a valid predicate with <nbargs> valid
88 * arguments, placed in <argp> (which we'll need to free).
89 */
90 switch (cond_pred->prd) {
91 case CFG_PRED_DEFINED: // checks if arg exists as an environment variable
92 ret = getenv(argp[0].data.str.area) != NULL;
93 goto done;
94
95 case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
96 const char *p;
97
98 for (p = build_features; (p = strstr(p, argp[0].data.str.area)); p++) {
99 if ((p[argp[0].data.str.data] == ' ' || p[argp[0].data.str.data] == 0) &&
100 p > build_features) {
101 if (*(p-1) == '+') { // "+OPENSSL"
102 ret = 1;
103 goto done;
104 }
105 else if (*(p-1) == '-') { // "-OPENSSL"
106 ret = 0;
107 goto done;
108 }
109 /* it was a sub-word, let's restart from next place */
110 }
111 }
112 /* not found */
113 ret = 0;
114 goto done;
115 }
116 case CFG_PRED_STREQ: // checks if the two arg are equal
117 ret = strcmp(argp[0].data.str.area, argp[1].data.str.area) == 0;
118 goto done;
119
120 case CFG_PRED_STRNEQ: // checks if the two arg are different
121 ret = strcmp(argp[0].data.str.area, argp[1].data.str.area) != 0;
122 goto done;
123
124 case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
125 ret = compare_current_version(argp[0].data.str.area) <= 0;
126 goto done;
127
128 case CFG_PRED_VERSION_BEFORE: // checks if the current version is older than this one
129 ret = compare_current_version(argp[0].data.str.area) > 0;
130 goto done;
131
132 default:
133 memprintf(err, "internal error: unhandled conditional expression predicate '%s'", cond_pred->word);
134 if (errptr)
135 *errptr = args[0];
136 goto done;
137 }
138 }
139
140 memprintf(err, "unparsable conditional expression '%s'", args[0]);
141 if (errptr)
142 *errptr = args[0];
143 done:
144 free_args(argp);
145 ha_free(&argp);
146 return ret;
147}