From fa41cb6792ddcf39b800217337d7910d586ef922 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Thu, 1 Oct 2020 14:32:35 +0200 Subject: [PATCH] MINOR: tools: support for word expansion of environment in parse_line Allow the syntax "${...[*]}" to expand an environment variable containing several values separated by spaces as individual arguments. A new flag PARSE_OPT_WORD_EXPAND has been added to toggle this feature on parse_line invocation. In case of an invalid syntax, a new error PARSE_ERR_WRONG_EXPAND will be triggered. This feature has been asked on the github issue #165. --- doc/configuration.txt | 5 ++++- include/haproxy/tools-t.h | 2 ++ src/cfgparse.c | 13 ++++++++++++- src/tools.c | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 87f35e984..db4a4a760 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -475,7 +475,10 @@ interpreted only within double quotes. Variables are expanded during the configuration parsing. Variable names must be preceded by a dollar ("$") and optionally enclosed with braces ("{}") similarly to what is done in Bourne shell. Variable names can contain alphanumerical characters or the character -underscore ("_") but should not start with a digit. +underscore ("_") but should not start with a digit. If the variable contains a +list of several values separated by spaces, it can be expanded as individual +arguments by enclosing the variable with braces and appending the suffix '[*]' +before the closing brace. Example: diff --git a/include/haproxy/tools-t.h b/include/haproxy/tools-t.h index b1e72494f..34f79bea8 100644 --- a/include/haproxy/tools-t.h +++ b/include/haproxy/tools-t.h @@ -57,6 +57,7 @@ #define PARSE_OPT_DQUOTE 0x00000008 // '"' encloses a string #define PARSE_OPT_ENV 0x00000010 // '$' is followed by environment variables #define PARSE_OPT_INPLACE 0x00000020 // parse and tokenize in-place (src == dst) +#define PARSE_OPT_WORD_EXPAND 0x00000040 // '[*]' suffix to expand an environment variable as several individual arguments /* return error flags from parse_line() */ #define PARSE_ERR_TOOLARGE 0x00000001 // result is too large for initial outlen @@ -66,6 +67,7 @@ #define PARSE_ERR_HEX 0x00000010 // unparsable hex sequence (at errptr) #define PARSE_ERR_VARNAME 0x00000020 // invalid variable name (at errptr) #define PARSE_ERR_OVERLAP 0x00000040 // output overlaps with input, need to allocate +#define PARSE_ERR_WRONG_EXPAND 0x00000080 // unparsable word expansion sequence /* special return values for the time parser (parse_time_err()) */ #define PARSE_TIME_UNDER ((char *)1) diff --git a/src/cfgparse.c b/src/cfgparse.c index 768fea338..cb48fef2b 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1908,7 +1908,8 @@ next_line: outlen = outlinesize; err = parse_line(line, outline, &outlen, args, &arg, PARSE_OPT_ENV | PARSE_OPT_DQUOTE | PARSE_OPT_SQUOTE | - PARSE_OPT_BKSLASH | PARSE_OPT_SHARP, &errptr); + PARSE_OPT_BKSLASH | PARSE_OPT_SHARP | PARSE_OPT_WORD_EXPAND, + &errptr); if (err & PARSE_ERR_QUOTE) { size_t newpos = sanitize_for_printing(line, errptr - line, 80); @@ -1950,6 +1951,16 @@ next_line: goto next_line; } + if (err & PARSE_ERR_WRONG_EXPAND) { + size_t newpos = sanitize_for_printing(line, errptr - line, 80); + + ha_alert("parsing [%s:%d]: truncated or invalid word expansion sequence at position %d:\n" + " %s\n %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^"); + err_code |= ERR_ALERT | ERR_FATAL; + fatal++; + goto next_line; + } + if (err & (PARSE_ERR_TOOLARGE|PARSE_ERR_OVERLAP)) { outlinesize = (outlen + 1023) & -1024; outline = realloc(outline, outlinesize); diff --git a/src/tools.c b/src/tools.c index 53d2486f4..c2a96642d 100644 --- a/src/tools.c +++ b/src/tools.c @@ -4911,6 +4911,7 @@ uint32_t parse_line(char *in, char *out, size_t *outlen, char **args, int *nbarg { char *quote = NULL; char *brace = NULL; + char *word_expand = NULL; unsigned char hex1, hex2; size_t outmax = *outlen; int argsmax = *nbargs - 1; @@ -5089,6 +5090,19 @@ uint32_t parse_line(char *in, char *out, size_t *outlen, char **args, int *nbarg value = getenv(var_name); *in = save_char; + /* support for '[*]' sequence to force word expansion, + * only available inside braces */ + if (*in == '[' && brace && (opts & PARSE_OPT_WORD_EXPAND)) { + word_expand = in++; + + if (*in++ != '*' || *in++ != ']') { + err |= PARSE_ERR_WRONG_EXPAND; + if (errptr) + *errptr = word_expand; + goto leave; + } + } + if (brace) { if (*in != '}') { /* unmatched brace */ @@ -5102,9 +5116,25 @@ uint32_t parse_line(char *in, char *out, size_t *outlen, char **args, int *nbarg } if (value) { - while (*value) - EMIT_CHAR(*value++); + while (*value) { + /* expand as individual parameters on a space character */ + if (word_expand && isspace(*value)) { + EMIT_CHAR(0); + ++arg; + if (arg < argsmax) + args[arg] = out + outpos; + else + err |= PARSE_ERR_TOOMANY; + + /* skip consecutive spaces */ + while (isspace(*++value)) + ; + } else { + EMIT_CHAR(*value++); + } + } } + word_expand = NULL; } else { /* any other regular char */