Hello Willy, On Thu, Apr 19, 2018 at 8:51 AM, Willy Tarreau <w...@1wt.eu> wrote:
> Hoping this helps, Yes, I hope it does too. I guess I was sent off-track by the will to make something very flexible (at least in my mind) despite your multiple good examples, sorry about that. Anyway, here are three patches that, I hope, will do what is needed: - add a way to specify an optional payload by using << at the end of the first line of a command - add payload support to "add map" - add payload support to "set ssl ocsp-response" More commands could benefit from using a payload but for a start I only did these two. Thanks for all your time, I really appreciated it. -- Aurélien Nephtali
>From f7b1a17afff1d054d02300535f03c7921e8ef7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <aurelien.nepht...@corp.ovh.com> Date: Wed, 18 Apr 2018 13:26:46 +0200 Subject: [PATCH 1/3] MEDIUM: cli: Add payload support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to use arbitrary data in the CLI (multiple lines or group of words that must be considered as a whole, for example), it is now possible to add a payload to the commands. To do so, the first line needs to end with a special pattern: <<\n. Everything that follows will be left untouched by the CLI parser and will be passed to the commands parsers. Per-command support will need to be added to take advantage of this feature. Signed-off-by: Aurélien Nephtali <aurelien.nepht...@corp.ovh.com> --- doc/management.txt | 17 ++++ include/proto/applet.h | 4 +- include/proto/cli.h | 1 - include/types/applet.h | 6 +- include/types/cli.h | 2 +- src/cache.c | 2 +- src/cli.c | 228 ++++++++++++++++++++++++++++++------------------- src/dns.c | 2 +- src/hlua.c | 2 +- src/map.c | 12 +-- src/proto_http.c | 2 +- src/proxy.c | 16 ++-- src/server.c | 20 ++--- src/ssl_sock.c | 6 +- src/stats.c | 6 +- src/stick_table.c | 2 +- src/stream.c | 6 +- 17 files changed, 205 insertions(+), 129 deletions(-) diff --git a/doc/management.txt b/doc/management.txt index 4b6901851..5fe0d2e8e 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1298,6 +1298,23 @@ delimiter to mark an end of output for each command, and takes care of ensuring that no command can emit an empty line on output. A script can thus easily parse the output even when multiple commands were pipelined on a single line. +Some commands may take an optional payload. To add one to a command, the first +line needs to end with the "<<\n" pattern. The next lines will be treated as +the payload and can contain as many lines as needed. To validate a command with +a payload, it needs to end with an empty line. + +Limitations do exist: the length of the whole buffer passed to the CLI must +not be greater than tune.bfsize and the pattern "<<" must not be glued to the +last word of the line. + +When entering a paylod while in interactive mode, the prompt will change from +"> " to "+ ". + +Example: + + # echo -e "set ssl ocsp-response <<\n$(base64 ocsp.der)\n" | \ + socat /tmp/sock1 - + It is important to understand that when multiple haproxy processes are started on the same sockets, any process may pick up the request and will output its own stats. diff --git a/include/proto/applet.h b/include/proto/applet.h index cdd4d90f0..7cc2c0ad1 100644 --- a/include/proto/applet.h +++ b/include/proto/applet.h @@ -43,11 +43,13 @@ static int inline appctx_res_wakeup(struct appctx *appctx); /* Initializes all required fields for a new appctx. Note that it does the * minimum acceptable initialization for an appctx. This means only the - * 3 integer states st0, st1, st2 are zeroed. + * 3 integer states st0, st1, st2 and the chunk used to gather unfinished + * commands are zeroed */ static inline void appctx_init(struct appctx *appctx, unsigned long thread_mask) { appctx->st0 = appctx->st1 = appctx->st2 = 0; + appctx->chunk = NULL; appctx->io_release = NULL; appctx->thread_mask = thread_mask; appctx->state = APPLET_SLEEPING; diff --git a/include/proto/cli.h b/include/proto/cli.h index d5feb862a..da80af7d3 100644 --- a/include/proto/cli.h +++ b/include/proto/cli.h @@ -24,7 +24,6 @@ #define _PROTO_CLI_H -struct cli_kw* cli_find_kw(char **args); void cli_register_kw(struct cli_kw_list *kw_list); int cli_has_level(struct appctx *appctx, int level); diff --git a/include/types/applet.h b/include/types/applet.h index 89c318c1d..b0715866e 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -50,6 +50,9 @@ struct applet { #define APPLET_WOKEN_UP 0x02 /* applet was running and requested to woken up again */ #define APPLET_WANT_DIE 0x04 /* applet was running and requested to die */ +#define APPCTX_CLI_ST1_PROMPT (1 << 0) +#define APPCTX_CLI_ST1_PAYLOAD (1 << 1) + /* Context of a running applet. */ struct appctx { struct list runq; /* chaining in the applet run queue */ @@ -57,7 +60,8 @@ struct appctx { /* 3 unused bytes here */ unsigned short state; /* Internal appctx state */ unsigned int st0; /* CLI state for stats, session state for peers */ - unsigned int st1; /* prompt for stats, session error for peers */ + unsigned int st1; /* prompt/payload (bitwise OR of APPCTX_CLI_ST1_*) for stats, session error for peers */ + struct chunk *chunk; /* used to store unfinished commands */ unsigned int st2; /* output state for stats, unused by peers */ struct applet *applet; /* applet this context refers to */ void *owner; /* pointer to upper layer's entity (eg: stream interface) */ diff --git a/include/types/cli.h b/include/types/cli.h index 63e0e9d05..4e7e6b124 100644 --- a/include/types/cli.h +++ b/include/types/cli.h @@ -27,7 +27,7 @@ struct cli_kw { const char *str_kw[5]; /* keywords ended by NULL, limited to 5 separated keywords combination */ const char *usage; /* usage message */ - int (*parse)(char **args, struct appctx *appctx, void *private); + int (*parse)(char **args, char *payload, struct appctx *appctx, void *private); int (*io_handler)(struct appctx *appctx); void (*io_release)(struct appctx *appctx); void *private; diff --git a/src/cache.c b/src/cache.c index 39e0bad44..c72d9dee9 100644 --- a/src/cache.c +++ b/src/cache.c @@ -952,7 +952,7 @@ struct flt_ops cache_ops = { }; -static int cli_parse_show_cache(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_cache(char **args, char *payload, struct appctx *appctx, void *private) { if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) return 1; diff --git a/src/cli.c b/src/cli.c index 965709ec8..7a61601f0 100644 --- a/src/cli.c +++ b/src/cli.c @@ -67,6 +67,8 @@ #include <proto/task.h> #include <proto/proto_udp.h> +#define PAYLOAD_PATTERN "<<" + static struct applet cli_applet; static const char stats_sock_usage_msg[] = @@ -90,7 +92,7 @@ static struct cli_kw_list cli_keywords = { extern const char *stat_status_codes[]; -char *cli_gen_usage_msg() +static char *cli_gen_usage_msg(struct appctx *appctx) { struct cli_kw_list *kw_list; struct cli_kw *kw; @@ -101,7 +103,7 @@ char *cli_gen_usage_msg() dynamic_usage_msg = NULL; if (LIST_ISEMPTY(&cli_keywords.list)) - return NULL; + goto end; chunk_reset(tmp); chunk_strcat(tmp, stats_sock_usage_msg); @@ -115,6 +117,18 @@ char *cli_gen_usage_msg() chunk_init(&out, NULL, 0); chunk_dup(&out, tmp); dynamic_usage_msg = out.str; + +end: + if (dynamic_usage_msg) { + appctx->ctx.cli.severity = LOG_INFO; + appctx->ctx.cli.msg = dynamic_usage_msg; + } + else { + appctx->ctx.cli.severity = LOG_INFO; + appctx->ctx.cli.msg = stats_sock_usage_msg; + } + appctx->st0 = CLI_ST_PRINT; + return dynamic_usage_msg; } @@ -379,61 +393,63 @@ static int cli_get_severity_output(struct appctx *appctx) * If a keyword parser is NULL and an I/O handler is declared, the I/O handler * will automatically be used. */ -static int cli_parse_request(struct appctx *appctx, char *line) +static int cli_parse_request(struct appctx *appctx) { - char *args[MAX_STATS_ARGS + 1]; + char *args[MAX_STATS_ARGS + 1], *p, *end, *payload = NULL; + int i = 0; struct cli_kw *kw; - int arg; - int i, j; - - while (isspace((unsigned char)*line)) - line++; - arg = 0; - args[arg] = line; + appctx->st2 = 0; + memset(&appctx->ctx.cli, 0, sizeof(appctx->ctx.cli)); - while (*line && arg < MAX_STATS_ARGS) { - if (*line == '\\') { - line++; - if (*line == '\0') - break; - } - else if (isspace((unsigned char)*line)) { - *line++ = '\0'; + p = appctx->chunk->str; + end = p + appctx->chunk->len; + + /* look for a payload */ + if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) { + payload = strstr(p, PAYLOAD_PATTERN); + end = payload; + /* skip the pattern */ + payload += strlen(PAYLOAD_PATTERN); + /* skip whitespaces */ + payload += strspn(payload, " \t"); + } - while (isspace((unsigned char)*line)) - line++; + /* + * Get pointers on words. + * One extra slot is reserved to store a pointer on a null byte. + */ + while (i < MAX_STATS_ARGS && p < end) { + int j, k; - args[++arg] = line; - continue; - } + /* skip leading spaces/tabs */ + p += strspn(p, " \t"); + if (!*p) + break; - line++; - } + args[i] = p; + p += strcspn(p, " \t"); + *p++ = 0; - while (++arg <= MAX_STATS_ARGS) - args[arg] = line; - - /* unescape '\' */ - arg = 0; - while (arg <= MAX_STATS_ARGS && *args[arg] != '\0') { - j = 0; - for (i=0; args[arg][i] != '\0'; i++) { - if (args[arg][i] == '\\') { - if (args[arg][i+1] == '\\') - i++; + /* unescape backslashes (\) */ + for (j = 0, k = 0; args[i][k]; k++) { + if (args[i][k] == '\\') { + if (args[i][k + 1] == '\\') + k++; else continue; } - args[arg][j] = args[arg][i]; + args[i][j] = args[i][k]; j++; } - args[arg][j] = '\0'; - arg++; - } + args[i][j] = 0; - appctx->st2 = 0; - memset(&appctx->ctx.cli, 0, sizeof(appctx->ctx.cli)); + i++; + } + /* fill unused slots */ + p = appctx->chunk->str + appctx->chunk->len; + for (; i < MAX_STATS_ARGS + 1; i++) + args[i] = p; kw = cli_find_kw(args); if (!kw) @@ -442,7 +458,7 @@ static int cli_parse_request(struct appctx *appctx, char *line) appctx->io_handler = kw->io_handler; appctx->io_release = kw->io_release; /* kw->parse could set its own io_handler or ip_release handler */ - if ((!kw->parse || kw->parse(args, appctx, kw->private) == 0) && appctx->io_handler) { + if ((!kw->parse || kw->parse(args, payload, appctx, kw->private) == 0) && appctx->io_handler) { appctx->st0 = CLI_ST_CALLBACK; } return 1; @@ -519,6 +535,7 @@ static void cli_io_handler(struct appctx *appctx) * side, the conditions below will complete if needed. */ si_shutw(si); + free_trash_chunk(appctx->chunk); break; } else if (appctx->st0 == CLI_ST_GETREQ) { @@ -568,46 +585,49 @@ static void cli_io_handler(struct appctx *appctx) trash.str[len] = '\0'; - appctx->st0 = CLI_ST_PROMPT; - if (len) { - if (strcmp(trash.str, "quit") == 0) { + /* use a trash chunk to store received data */ + if (!appctx->chunk) { + appctx->chunk = alloc_trash_chunk(); + if (!appctx->chunk) { appctx->st0 = CLI_ST_END; continue; } - else if (strcmp(trash.str, "prompt") == 0) - appctx->st1 = !appctx->st1; - else if (strcmp(trash.str, "help") == 0 || - !cli_parse_request(appctx, trash.str)) { - cli_gen_usage_msg(); - if (dynamic_usage_msg) { - appctx->ctx.cli.severity = LOG_INFO; - appctx->ctx.cli.msg = dynamic_usage_msg; - } - else { - appctx->ctx.cli.severity = LOG_INFO; - appctx->ctx.cli.msg = stats_sock_usage_msg; - } - appctx->st0 = CLI_ST_PRINT; - } - /* NB: stats_sock_parse_request() may have put - * another CLI_ST_O_* into appctx->st0. - */ } - else if (!appctx->st1) { - /* if prompt is disabled, print help on empty lines, - * so that the user at least knows how to enable - * prompt and find help. - */ - cli_gen_usage_msg(); - if (dynamic_usage_msg) { - appctx->ctx.cli.severity = LOG_INFO; - appctx->ctx.cli.msg = dynamic_usage_msg; + chunk_appendf(appctx->chunk, "%s", trash.str); + if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) + chunk_appendf(appctx->chunk, "\n"); + + appctx->st0 = CLI_ST_PROMPT; + + if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) { + /* empty line */ + if (!len) { + /* remove the last two \n */ + appctx->chunk->len -= 2; + appctx->chunk->str[appctx->chunk->len] = 0; + + if (!cli_parse_request(appctx)) + cli_gen_usage_msg(appctx); + + chunk_reset(appctx->chunk); + /* NB: cli_sock_parse_request() may have put + * another CLI_ST_O_* into appctx->st0. + */ + + appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD; } + } + else { + /* look for the "payload start" pattern at the end of a line */ + if (!strcmp(appctx->chunk->str + appctx->chunk->len - strlen(PAYLOAD_PATTERN), PAYLOAD_PATTERN)) + appctx->st1 |= APPCTX_CLI_ST1_PAYLOAD; else { - appctx->ctx.cli.severity = LOG_INFO; - appctx->ctx.cli.msg = stats_sock_usage_msg; + /* no payload, the command is complete: parse the request */ + if (!cli_parse_request(appctx)) + cli_gen_usage_msg(appctx); + + chunk_reset(appctx->chunk); } - appctx->st0 = CLI_ST_PRINT; } /* re-adjust req buffer */ @@ -656,7 +676,24 @@ static void cli_io_handler(struct appctx *appctx) /* The post-command prompt is either LF alone or LF + '> ' in interactive mode */ if (appctx->st0 == CLI_ST_PROMPT) { - if (ci_putstr(si_ic(si), appctx->st1 ? "\n> " : "\n") != -1) + const char *prompt = ""; + + if (appctx->st1 & APPCTX_CLI_ST1_PROMPT) { + /* + * when entering a payload with interactive mode, change the prompt + * to emphasize that more data can still be sent + */ + if (appctx->chunk->len && appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) + prompt = "+ "; + else + prompt = "\n> "; + } + else { + if (!(appctx->st1 & APPCTX_CLI_ST1_PAYLOAD)) + prompt = "\n"; + } + + if (ci_putstr(si_ic(si), prompt) != -1) appctx->st0 = CLI_ST_GETREQ; else si_applet_cant_put(si); @@ -671,7 +708,7 @@ static void cli_io_handler(struct appctx *appctx) * buffer is empty. This still allows pipelined requests * to be sent in non-interactive mode. */ - if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!appctx->st1 && !req->buf->o)) { + if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!(appctx->st1 & APPCTX_CLI_ST1_PROMPT) && !req->buf->o)) { appctx->st0 = CLI_ST_END; continue; } @@ -1020,7 +1057,7 @@ static int cli_io_handler_show_cli_sock(struct appctx *appctx) * wants to stop here. It puts the variable to be dumped into cli.p0 if a single * variable is requested otherwise puts environ there. */ -static int cli_parse_show_env(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_env(char **args, char *payload, struct appctx *appctx, void *private) { extern char **environ; char **var; @@ -1054,7 +1091,7 @@ static int cli_parse_show_env(char **args, struct appctx *appctx, void *private) * wants to stop here. It puts the FD number into cli.i0 if a specific FD is * requested and sets st2 to STAT_ST_END, otherwise leaves 0 in i0. */ -static int cli_parse_show_fd(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_fd(char **args, char *payload, struct appctx *appctx, void *private) { if (!cli_has_level(appctx, ACCESS_LVL_OPER)) return 1; @@ -1069,7 +1106,7 @@ static int cli_parse_show_fd(char **args, struct appctx *appctx, void *private) } /* parse a "set timeout" CLI request. It always returns 1. */ -static int cli_parse_set_timeout(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_timeout(char **args, char *payload, struct appctx *appctx, void *private) { struct stream_interface *si = appctx->owner; struct stream *s = si_strm(si); @@ -1106,7 +1143,7 @@ static int cli_parse_set_timeout(char **args, struct appctx *appctx, void *priva } /* parse a "set maxconn global" command. It always returns 1. */ -static int cli_parse_set_maxconn_global(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_maxconn_global(char **args, char *payload, struct appctx *appctx, void *private) { int v; @@ -1159,7 +1196,7 @@ static int set_severity_output(int *target, char *argument) } /* parse a "set severity-output" command. */ -static int cli_parse_set_severity_output(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_severity_output(char **args, char *payload, struct appctx *appctx, void *private) { if (*args[2] && set_severity_output(&appctx->cli_severity_output, args[2])) return 0; @@ -1170,13 +1207,13 @@ static int cli_parse_set_severity_output(char **args, struct appctx *appctx, voi return 1; } -int cli_parse_default(char **args, struct appctx *appctx, void *private) +int cli_parse_default(char **args, char *payload, struct appctx *appctx, void *private) { return 0; } /* parse a "set rate-limit" command. It always returns 1. */ -static int cli_parse_set_ratelimit(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_ratelimit(char **args, char *payload, struct appctx *appctx, void *private) { int v; int *res; @@ -1296,7 +1333,7 @@ static int bind_parse_severity_output(char **args, int cur_arg, struct proxy *px } /* Send all the bound sockets, always returns 1 */ -static int _getsocks(char **args, struct appctx *appctx, void *private) +static int _getsocks(char **args, char *payload, struct appctx *appctx, void *private) { char *cmsgbuf = NULL; unsigned char *tmpbuf = NULL; @@ -1474,6 +1511,20 @@ out: return 1; } +static int cli_parse_simple(char **args, char *payload, struct appctx *appctx, void *private) +{ + if (*args[0] == 'h') + /* help */ + cli_gen_usage_msg(appctx); + else if (*args[0] == 'p') + /* prompt */ + appctx->st1 ^= APPCTX_CLI_ST1_PROMPT; + else if (*args[0] == 'q') + /* quit */ + appctx->st0 = CLI_ST_END; + + return 1; +} static struct applet cli_applet = { @@ -1485,6 +1536,9 @@ static struct applet cli_applet = { /* register cli keywords */ static struct cli_kw_list cli_kws = {{ },{ + { { "help", NULL }, NULL, cli_parse_simple, NULL }, + { { "prompt", NULL }, NULL, cli_parse_simple, NULL }, + { { "quit", NULL }, NULL, cli_parse_simple, NULL }, { { "set", "maxconn", "global", NULL }, "set maxconn global : change the per-process maxconn setting", cli_parse_set_maxconn_global, NULL }, { { "set", "rate-limit", NULL }, "set rate-limit : change a rate limiting value", cli_parse_set_ratelimit, NULL }, { { "set", "severity-output", NULL }, "set severity-output [none|number|string] : set presence of severity level in feedback information", cli_parse_set_severity_output, NULL, NULL }, diff --git a/src/dns.c b/src/dns.c index 5fe44d433..385ecbb6d 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1948,7 +1948,7 @@ static int dns_finalize_config(void) } /* if an arg is found, it sets the resolvers section pointer into cli.p0 */ -static int cli_parse_stat_resolvers(char **args, struct appctx *appctx, void *private) +static int cli_parse_stat_resolvers(char **args, char *payload, struct appctx *appctx, void *private) { struct dns_resolvers *presolvers; diff --git a/src/hlua.c b/src/hlua.c index 60cf8f948..d8d8d7def 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -6925,7 +6925,7 @@ __LJMP static int hlua_register_service(lua_State *L) /* This function initialises Lua cli handler. It copies the * arguments in the Lua stack and create channel IO objects. */ -static int hlua_cli_parse_fct(char **args, struct appctx *appctx, void *private) +static int hlua_cli_parse_fct(char **args, char *payload, struct appctx *appctx, void *private) { struct hlua *hlua; struct hlua_function *fcn; diff --git a/src/map.c b/src/map.c index 7953c2a0b..d02a0255c 100644 --- a/src/map.c +++ b/src/map.c @@ -565,7 +565,7 @@ static void cli_release_mlook(struct appctx *appctx) } -static int cli_parse_get_map(char **args, struct appctx *appctx, void *private) +static int cli_parse_get_map(char **args, char *payload, struct appctx *appctx, void *private) { if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) { /* Set flags. */ @@ -632,7 +632,7 @@ static void cli_release_show_map(struct appctx *appctx) } } -static int cli_parse_show_map(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_map(char **args, char *payload, struct appctx *appctx, void *private) { if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) { @@ -672,7 +672,7 @@ static int cli_parse_show_map(char **args, struct appctx *appctx, void *private) return 0; } -static int cli_parse_set_map(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_map(char **args, char *payload, struct appctx *appctx, void *private) { if (strcmp(args[1], "map") == 0) { char *err; @@ -772,7 +772,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private) return 1; } -static int cli_parse_add_map(char **args, struct appctx *appctx, void *private) +static int cli_parse_add_map(char **args, char *payload, struct appctx *appctx, void *private) { if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) { @@ -862,7 +862,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private) return 0; } -static int cli_parse_del_map(char **args, struct appctx *appctx, void *private) +static int cli_parse_del_map(char **args, char *payload, struct appctx *appctx, void *private) { if (args[1][0] == 'm') appctx->ctx.map.display_flags = PAT_REF_MAP; @@ -958,7 +958,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private) } -static int cli_parse_clear_map(char **args, struct appctx *appctx, void *private) +static int cli_parse_clear_map(char **args, char *payload, struct appctx *appctx, void *private) { if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) { /* Set ACL or MAP flags. */ diff --git a/src/proto_http.c b/src/proto_http.c index 8370889b4..540574b86 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -12491,7 +12491,7 @@ struct action_kw *action_http_res_custom(const char *kw) /* "show errors" handler for the CLI. Returns 0 if wants to continue, 1 to stop * now. */ -static int cli_parse_show_errors(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_errors(char **args, char *payload, struct appctx *appctx, void *private) { if (!cli_has_level(appctx, ACCESS_LVL_OPER)) return 1; diff --git a/src/proxy.c b/src/proxy.c index 89f679ff5..31253f14d 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -1393,7 +1393,7 @@ struct proxy *cli_find_backend(struct appctx *appctx, const char *arg) * 1 if it stops immediately. If an argument is specified, it will set the proxy * pointer into cli.p0 and its ID into cli.i0. */ -static int cli_parse_show_servers(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_servers(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; @@ -1558,7 +1558,7 @@ static int cli_io_handler_show_backend(struct appctx *appctx) } /* Parses the "enable dynamic-cookies backend" directive, it always returns 1 */ -static int cli_parse_enable_dyncookie_backend(char **args, struct appctx *appctx, void *private) +static int cli_parse_enable_dyncookie_backend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; struct server *s; @@ -1579,7 +1579,7 @@ static int cli_parse_enable_dyncookie_backend(char **args, struct appctx *appctx } /* Parses the "disable dynamic-cookies backend" directive, it always returns 1 */ -static int cli_parse_disable_dyncookie_backend(char **args, struct appctx *appctx, void *private) +static int cli_parse_disable_dyncookie_backend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; struct server *s; @@ -1604,7 +1604,7 @@ static int cli_parse_disable_dyncookie_backend(char **args, struct appctx *appct } /* Parses the "set dynamic-cookie-key backend" directive, it always returns 1 */ -static int cli_parse_set_dyncookie_key_backend(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_dyncookie_key_backend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; struct server *s; @@ -1641,7 +1641,7 @@ static int cli_parse_set_dyncookie_key_backend(char **args, struct appctx *appct } /* Parses the "set maxconn frontend" directive, it always returns 1 */ -static int cli_parse_set_maxconn_frontend(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_maxconn_frontend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; struct listener *l; @@ -1686,7 +1686,7 @@ static int cli_parse_set_maxconn_frontend(char **args, struct appctx *appctx, vo } /* Parses the "shutdown frontend" directive, it always returns 1 */ -static int cli_parse_shutdown_frontend(char **args, struct appctx *appctx, void *private) +static int cli_parse_shutdown_frontend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; @@ -1713,7 +1713,7 @@ static int cli_parse_shutdown_frontend(char **args, struct appctx *appctx, void } /* Parses the "disable frontend" directive, it always returns 1 */ -static int cli_parse_disable_frontend(char **args, struct appctx *appctx, void *private) +static int cli_parse_disable_frontend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; @@ -1748,7 +1748,7 @@ static int cli_parse_disable_frontend(char **args, struct appctx *appctx, void * } /* Parses the "enable frontend" directive, it always returns 1 */ -static int cli_parse_enable_frontend(char **args, struct appctx *appctx, void *private) +static int cli_parse_enable_frontend(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; diff --git a/src/server.c b/src/server.c index 28cc7418c..ebac357fb 100644 --- a/src/server.c +++ b/src/server.c @@ -4088,7 +4088,7 @@ struct server *cli_find_server(struct appctx *appctx, char *arg) } -static int cli_parse_set_server(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_server(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; const char *warning; @@ -4271,7 +4271,7 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat return 1; } -static int cli_parse_get_weight(char **args, struct appctx *appctx, void *private) +static int cli_parse_get_weight(char **args, char *payload, struct appctx *appctx, void *private) { struct stream_interface *si = appctx->owner; struct proxy *px; @@ -4309,7 +4309,7 @@ static int cli_parse_get_weight(char **args, struct appctx *appctx, void *privat return 1; } -static int cli_parse_set_weight(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_weight(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; const char *warning; @@ -4331,7 +4331,7 @@ static int cli_parse_set_weight(char **args, struct appctx *appctx, void *privat } /* parse a "set maxconn server" command. It always returns 1. */ -static int cli_parse_set_maxconn_server(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_maxconn_server(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; const char *warning; @@ -4353,7 +4353,7 @@ static int cli_parse_set_maxconn_server(char **args, struct appctx *appctx, void } /* parse a "disable agent" command. It always returns 1. */ -static int cli_parse_disable_agent(char **args, struct appctx *appctx, void *private) +static int cli_parse_disable_agent(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; @@ -4369,7 +4369,7 @@ static int cli_parse_disable_agent(char **args, struct appctx *appctx, void *pri } /* parse a "disable health" command. It always returns 1. */ -static int cli_parse_disable_health(char **args, struct appctx *appctx, void *private) +static int cli_parse_disable_health(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; @@ -4385,7 +4385,7 @@ static int cli_parse_disable_health(char **args, struct appctx *appctx, void *pr } /* parse a "disable server" command. It always returns 1. */ -static int cli_parse_disable_server(char **args, struct appctx *appctx, void *private) +static int cli_parse_disable_server(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; @@ -4401,7 +4401,7 @@ static int cli_parse_disable_server(char **args, struct appctx *appctx, void *pr } /* parse a "enable agent" command. It always returns 1. */ -static int cli_parse_enable_agent(char **args, struct appctx *appctx, void *private) +static int cli_parse_enable_agent(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; @@ -4424,7 +4424,7 @@ static int cli_parse_enable_agent(char **args, struct appctx *appctx, void *priv } /* parse a "enable health" command. It always returns 1. */ -static int cli_parse_enable_health(char **args, struct appctx *appctx, void *private) +static int cli_parse_enable_health(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; @@ -4440,7 +4440,7 @@ static int cli_parse_enable_health(char **args, struct appctx *appctx, void *pri } /* parse a "enable server" command. It always returns 1. */ -static int cli_parse_enable_server(char **args, struct appctx *appctx, void *private) +static int cli_parse_enable_server(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 23ad35b18..70bf66024 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -8500,7 +8500,7 @@ static int cli_io_handler_tlskeys_files(struct appctx *appctx) { } /* sets cli.i0 to non-zero if only file lists should be dumped */ -static int cli_parse_show_tlskeys(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_tlskeys(char **args, char *payload, struct appctx *appctx, void *private) { /* no parameter, shows only file list */ if (!*args[2]) { @@ -8525,7 +8525,7 @@ static int cli_parse_show_tlskeys(char **args, struct appctx *appctx, void *priv return 0; } -static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_tlskeys(char **args, char *payload, struct appctx *appctx, void *private) { struct tls_keys_ref *ref; @@ -8561,7 +8561,7 @@ static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *priva } #endif -static int cli_parse_set_ocspresponse(char **args, struct appctx *appctx, void *private) +static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private) { #if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) char *err = NULL; diff --git a/src/stats.c b/src/stats.c index b592ced57..7ad30a178 100644 --- a/src/stats.c +++ b/src/stats.c @@ -3524,7 +3524,7 @@ static int stats_dump_json_schema_to_buffer(struct stream_interface *si) return 1; } -static int cli_parse_clear_counters(char **args, struct appctx *appctx, void *private) +static int cli_parse_clear_counters(char **args, char *payload, struct appctx *appctx, void *private) { struct proxy *px; struct server *sv; @@ -3586,7 +3586,7 @@ static int cli_parse_clear_counters(char **args, struct appctx *appctx, void *pr } -static int cli_parse_show_info(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_info(char **args, char *payload, struct appctx *appctx, void *private) { appctx->ctx.stats.scope_str = 0; appctx->ctx.stats.scope_len = 0; @@ -3600,7 +3600,7 @@ static int cli_parse_show_info(char **args, struct appctx *appctx, void *private } -static int cli_parse_show_stat(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx, void *private) { appctx->ctx.stats.scope_str = 0; appctx->ctx.stats.scope_len = 0; diff --git a/src/stick_table.c b/src/stick_table.c index 73c70d3e2..3e44747c1 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -3375,7 +3375,7 @@ static int table_prepare_data_request(struct appctx *appctx, char **args) } /* returns 0 if wants to be called, 1 if has ended processing */ -static int cli_parse_table_req(char **args, struct appctx *appctx, void *private) +static int cli_parse_table_req(char **args, char *payload, struct appctx *appctx, void *private) { appctx->ctx.table.data_type = -1; appctx->ctx.table.target = NULL; diff --git a/src/stream.c b/src/stream.c index 2d8f27834..1d0b22ca3 100644 --- a/src/stream.c +++ b/src/stream.c @@ -3025,7 +3025,7 @@ static int stats_dump_full_strm_to_buffer(struct stream_interface *si, struct st } -static int cli_parse_show_sess(char **args, struct appctx *appctx, void *private) +static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx, void *private) { if (!cli_has_level(appctx, ACCESS_LVL_OPER)) return 1; @@ -3280,7 +3280,7 @@ static void cli_release_show_sess(struct appctx *appctx) } /* Parses the "shutdown session" directive, it always returns 1 */ -static int cli_parse_shutdown_session(char **args, struct appctx *appctx, void *private) +static int cli_parse_shutdown_session(char **args, char *payload, struct appctx *appctx, void *private) { struct stream *strm, *ptr; @@ -3315,7 +3315,7 @@ static int cli_parse_shutdown_session(char **args, struct appctx *appctx, void * } /* Parses the "shutdown session server" directive, it always returns 1 */ -static int cli_parse_shutdown_sessions_server(char **args, struct appctx *appctx, void *private) +static int cli_parse_shutdown_sessions_server(char **args, char *payload, struct appctx *appctx, void *private) { struct server *sv; struct stream *strm, *strm_bck; -- 2.11.0
>From 63439f2b089613973f72a37dd5dda17f45f26545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <aurelien.nepht...@corp.ovh.com> Date: Wed, 18 Apr 2018 14:04:47 +0200 Subject: [PATCH 2/3] MINOR: map: Add payload support to "add map" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is now possible to use a payload with the "add map" command. These syntaxes will work the same way: # echo "add map #-1 key value" | socat /tmp/sock1 - # echo -e "add map #-1 <<\n$(cat data)\n" | socat /tmp/sock1 - with # cat data key1 value1 with spaces key2 value2 key3 value3 also with spaces Signed-off-by: Aurélien Nephtali <aurelien.nepht...@corp.ovh.com> --- src/map.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/src/map.c b/src/map.c index d02a0255c..64222dc2e 100644 --- a/src/map.c +++ b/src/map.c @@ -772,6 +772,20 @@ static int cli_parse_set_map(char **args, char *payload, struct appctx *appctx, return 1; } +static int map_add_key_value(struct appctx *appctx, const char *key, const char *value, char **err) +{ + int ret; + + HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock); + if (appctx->ctx.map.display_flags == PAT_REF_MAP) + ret = pat_ref_add(appctx->ctx.map.ref, key, value, err); + else + ret = pat_ref_add(appctx->ctx.map.ref, key, NULL, err); + HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock); + + return ret; +} + static int cli_parse_add_map(char **args, char *payload, struct appctx *appctx, void *private) { if (strcmp(args[1], "map") == 0 || @@ -785,13 +799,16 @@ static int cli_parse_add_map(char **args, char *payload, struct appctx *appctx, else appctx->ctx.map.display_flags = PAT_REF_ACL; - /* If the keywork is "map", we expect three parameters, if it - * is "acl", we expect only two parameters + /* If the keyword is "map", we expect: + * - three parameters if there is no payload + * - one parameter if there is a payload + * If it is "acl", we expect only two parameters */ if (appctx->ctx.map.display_flags == PAT_REF_MAP) { - if (!*args[2] || !*args[3] || !*args[4]) { + if ((!payload && (!*args[2] || !*args[3] || !*args[4])) || + (payload && !*args[2])) { appctx->ctx.cli.severity = LOG_ERR; - appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n"; + appctx->ctx.cli.msg = "'add map' expects three parameters (map identifier, key and value) or one parameter (map identifier) and a payload\n"; appctx->st0 = CLI_ST_PRINT; return 1; } @@ -832,26 +849,67 @@ static int cli_parse_add_map(char **args, char *payload, struct appctx *appctx, return 1; } - /* Add value. */ + /* Add value(s). */ err = NULL; - HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock); - if (appctx->ctx.map.display_flags == PAT_REF_MAP) - ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err); - else - ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err); - HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock); - if (!ret) { - if (err) { - memprintf(&err, "%s.\n", err); - appctx->ctx.cli.err = err; - appctx->st0 = CLI_ST_PRINT_FREE; + if (!payload) { + ret = map_add_key_value(appctx, args[3], args[4], &err); + if (!ret) { + if (err) { + memprintf(&err, "%s.\n", err); + appctx->ctx.cli.err = err; + appctx->st0 = CLI_ST_PRINT_FREE; + } + else { + appctx->ctx.cli.severity = LOG_ERR; + appctx->ctx.cli.msg = "Failed to add an entry.\n"; + appctx->st0 = CLI_ST_PRINT; + } + return 1; } - else { - appctx->ctx.cli.severity = LOG_ERR; - appctx->ctx.cli.msg = "Failed to add an entry.\n"; - appctx->st0 = CLI_ST_PRINT; + } + else { + const char *end = payload + strlen(payload); + + while (payload < end) { + char *key, *value; + size_t l; + + /* key */ + key = payload; + l = strcspn(key, " \t"); + payload += l; + + if (!*payload && appctx->ctx.map.display_flags == PAT_REF_MAP) { + memprintf(&err, "Missing value for key '%s'.\n", key); + appctx->ctx.cli.err = err; + appctx->st0 = CLI_ST_PRINT_FREE; + return 1; + } + key[l] = 0; + payload++; + + /* value */ + payload += strspn(payload, " \t"); + value = payload; + l = strcspn(value, "\n"); + value[l] = 0; + payload += l + 1; + + ret = map_add_key_value(appctx, key, value, &err); + if (!ret) { + if (err) { + memprintf(&err, "%s.\n", err); + appctx->ctx.cli.err = err; + appctx->st0 = CLI_ST_PRINT_FREE; + } + else { + appctx->ctx.cli.severity = LOG_ERR; + appctx->ctx.cli.msg = "Failed to add a key.\n"; + appctx->st0 = CLI_ST_PRINT; + } + return 1; + } } - return 1; } /* The add is done, send message. */ -- 2.11.0
>From 1b7f6c40b010521a8b55103c984d3c8f305e818c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <aurelien.nepht...@corp.ovh.com> Date: Wed, 18 Apr 2018 14:04:58 +0200 Subject: [PATCH 3/3] MINOR: ssl: Add payload support to "set ssl ocsp-response" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is now possible to use a payload with the "set ssl ocsp-response" command. These syntaxes will work the same way: # echo "set ssl ocsp-response $(base64 -w 10000 ocsp.der)" | \ socat /tmp/sock1 - # echo -e "set ssl ocsp-response <<\n$(base64 ocsp.der)\n" | \ socat /tmp/sock1 - Signed-off-by: Aurélien Nephtali <aurelien.nepht...@corp.ovh.com> --- src/ssl_sock.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 70bf66024..ddcc95cb0 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -8565,16 +8565,27 @@ static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx { #if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) char *err = NULL; + int i, j; + + if (!payload) + payload = args[3]; /* Expect one parameter: the new response in base64 encoding */ - if (!*args[3]) { + if (!*payload) { appctx->ctx.cli.severity = LOG_ERR; appctx->ctx.cli.msg = "'set ssl ocsp-response' expects response in base64 encoding.\n"; appctx->st0 = CLI_ST_PRINT; return 1; } - trash.len = base64dec(args[3], strlen(args[3]), trash.str, trash.size); + /* remove \r and \n from the payload */ + for (i = 0, j = 0; payload[i]; i++) { + if (payload[i] == '\r' || payload[i] == '\n') + continue; + payload[j++] = payload[i]; + } + + trash.len = base64dec(payload, strlen(payload), trash.str, trash.size); if (trash.len < 0) { appctx->ctx.cli.severity = LOG_ERR; appctx->ctx.cli.msg = "'set ssl ocsp-response' received invalid base64 encoded response.\n"; -- 2.11.0