From: Willy Tarreau Date: Thu, 6 May 2021 13:49:04 +0000 (+0200) Subject: MINOR: config: make cfg_eval_condition() support predicates with arguments X-Git-Tag: v2.4-dev19~111 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=732525fae7abee4b73d980b7f0b7f7aa5761cf23;p=thirdparty%2Fhaproxy.git MINOR: config: make cfg_eval_condition() support predicates with arguments Now we can look up a list of known predicates and pre-parse their arguments. For now the list is empty. The code needed to be arranged with a common exit point to release all arguments because there's no default argument freeing function (it likely only used to exist in the deinit code). Since we only support simple arguments for now it's no big deal, only a 2-liner loop. --- diff --git a/src/cfgparse.c b/src/cfgparse.c index 1d85943fff..c7a746fe63 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -132,6 +133,22 @@ enum nested_cond_state { /* 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 +}; + +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[] = { + { NULL, CFG_PRED_NONE, 0 } +}; + /* * converts to a list of listeners which are dynamically allocated. * The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where : @@ -1634,6 +1651,24 @@ 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 , 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 . 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 @@ -1641,6 +1676,12 @@ static int cfg_parse_global_def_path(char **args, int section_type, struct proxy */ static 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; @@ -1651,10 +1692,44 @@ static int cfg_eval_condition(char **args, char **err, const char **errptr) if (end && *end == '\0') return val != 0; + /* below we'll likely all make_arg_list() so we must return only via + * the 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 valid + * arguments, placed in (which we'll need to free). + */ + switch (cond_pred->prd) { + 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]; - return -1; + done: + for (nbargs = 0; argp && argp[nbargs].type != ARGT_STOP; nbargs++) { + if (argp[nbargs].type == ARGT_STR) + free(argp[nbargs].data.str.area); + } + free(argp); + return ret; } /*