Hello Willy,
Sorry for the delay.
Here are the three amended patches.
Changes:
- update the documentation
- add some comments regarding the detection of the payload pattern
and the input that is zero terminated
- use appctx->chunk to gather data without using the trash
- allow semicolons in a payload (NEW)
- make "add map" payload parsing clearer
- fix "set ssl ocsp-response" payload parsing
- don't skip whitespaces at the beginning of the payload (the less it
is modified the better it is, I think)
"0001-wip.patch" is an incremental patch for the review.
Thanks.
--
Aurélien.
From 1c7b61400e1647cb0e9dd783375f24336352f464 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
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 <[email protected]>
---
doc/management.txt | 12 +++
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 | 281 +++++++++++++++++++++++++++++++------------------
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, 237 insertions(+), 145 deletions(-)
diff --git a/doc/management.txt b/doc/management.txt
index 4b6901851..bca0fd469 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1298,6 +1298,18 @@ 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 "+ ".
+
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..38d715f83 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,69 @@ 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++;
+ appctx->st2 = 0;
+ memset(&appctx->ctx.cli, 0, sizeof(appctx->ctx.cli));
- arg = 0;
- args[arg] = line;
+ p = appctx->chunk->str;
+ end = p + appctx->chunk->len;
- while (*line && arg < MAX_STATS_ARGS) {
- if (*line == '\\') {
- line++;
- if (*line == '\0')
- break;
- }
- else if (isspace((unsigned char)*line)) {
- *line++ = '\0';
+ /*
+ * Get the payload start if there is one.
+ * For the sake of simplicity, the payload pattern is looked up
+ * everywhere from the start of the input but it can only be found
+ * at the end of the first line if APPCTX_CLI_ST1_PAYLOAD is set.
+ *
+ * The input string was zero terminated so it is safe to use
+ * the str*() functions throughout the parsing
+ */
+ if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) {
+ payload = strstr(p, PAYLOAD_PATTERN);
+ end = payload;
+ /* skip the pattern */
+ payload += strlen(PAYLOAD_PATTERN);
+ }
- 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 +464,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,9 +541,23 @@ 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) {
+ char *str;
+
+ /* 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;
+ }
+ }
+
+ str = appctx->chunk->str + appctx->chunk->len;
+
/* ensure we have some output room left in the event we
* would want to return some info right after parsing.
*/
@@ -530,7 +566,8 @@ static void cli_io_handler(struct appctx *appctx)
break;
}
- reql = co_getline(si_oc(si), trash.str, trash.size);
+ /* '- 1' is to ensure a null byte can always be inserted at the end */
+ reql = co_getline(si_oc(si), str, appctx->chunk->size - appctx->chunk->len - 1);
if (reql <= 0) { /* closed or EOL not found */
if (reql == 0)
break;
@@ -538,18 +575,20 @@ static void cli_io_handler(struct appctx *appctx)
continue;
}
- /* seek for a possible unescaped semi-colon. If we find
- * one, we replace it with an LF and skip only this part.
- */
- for (len = 0; len < reql; len++) {
- if (trash.str[len] == '\\') {
- len++;
- continue;
- }
- if (trash.str[len] == ';') {
- trash.str[len] = '\n';
- reql = len + 1;
- break;
+ if (!(appctx->st1 & APPCTX_CLI_ST1_PAYLOAD)) {
+ /* seek for a possible unescaped semi-colon. If we find
+ * one, we replace it with an LF and skip only this part.
+ */
+ for (len = 0; len < reql; len++) {
+ if (str[len] == '\\') {
+ len++;
+ continue;
+ }
+ if (str[len] == ';') {
+ str[len] = '\n';
+ reql = len + 1;
+ break;
+ }
}
}
@@ -558,56 +597,58 @@ static void cli_io_handler(struct appctx *appctx)
* line.
*/
len = reql - 1;
- if (trash.str[len] != '\n') {
+ if (str[len] != '\n') {
appctx->st0 = CLI_ST_END;
continue;
}
- if (len && trash.str[len-1] == '\r')
+ if (len && str[len-1] == '\r')
len--;
- trash.str[len] = '\0';
+ str[len] = '\0';
+ appctx->chunk->len += len;
+
+ if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) {
+ appctx->chunk->str[appctx->chunk->len] = '\n';
+ appctx->chunk->str[appctx->chunk->len + 1] = 0;
+ appctx->chunk->len++;
+ }
appctx->st0 = CLI_ST_PROMPT;
- if (len) {
- if (strcmp(trash.str, "quit") == 0) {
- 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;
+
+ 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;
}
- /* 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.
+ else {
+ /*
+ * Look for the "payload start" pattern at the end of a line
+ * Its location is not remembered here, this is just to switch
+ * to a gathering mode.
*/
- cli_gen_usage_msg();
- if (dynamic_usage_msg) {
- appctx->ctx.cli.severity = LOG_INFO;
- appctx->ctx.cli.msg = dynamic_usage_msg;
- }
+ 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 +697,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 +729,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 +1078,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 +1112,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 +1127,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 +1164,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 +1217,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 +1228,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 +1354,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 +1532,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 +1557,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 5096768be..da5611065 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -6926,7 +6926,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 6730375ef..f2a76823a 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -12489,7 +12489,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 84369b751f8de56adb867d3de534865a675e4890 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
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 <[email protected]>
---
doc/management.txt | 19 +++++++++-
src/map.c | 102 ++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 99 insertions(+), 22 deletions(-)
diff --git a/doc/management.txt b/doc/management.txt
index bca0fd469..c0c3f4823 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1331,11 +1331,28 @@ add acl <acl> <pattern>
In this case, you must use the command "add map" in place of "add acl".
add map <map> <key> <value>
+add map <map> <payload>
Add an entry into the map <map> to associate the value <value> to the key
<key>. This command does not verify if the entry already exists. It is
mainly used to fill a map after a clear operation. Note that if the reference
<map> is a file and is shared with a map, this map will contain also a new
- pattern entry.
+ pattern entry. Using the payload syntax it is possible to add multiple
+ key/value pairs by entering them on separate lines. On each new line, the
+ first word is the key and the rest of the line is considered to be the value
+ which can even contains spaces.
+
+ Example:
+
+ # socat /tmp/sock1 -
+ prompt
+
+ > add map #-1 <<
+ + key1 value1
+ + key2 value2 with spaces
+ + key3 value3 also with spaces
+ + key4 value4
+
+ >
clear counters
Clear the max values of the statistics counters in each proxy (frontend &
diff --git a/src/map.c b/src/map.c
index d02a0255c..ad47c7f23 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,69 @@ 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");
+ payload += l;
+ if (*payload)
+ payload++;
+ value[l] = 0;
+
+ 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 a4d6eaf74179cf43b55ab4c696201139246f5fae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
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 <[email protected]>
---
doc/management.txt | 6 +++++-
src/ssl_sock.c | 16 ++++++++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/doc/management.txt b/doc/management.txt
index c0c3f4823..a2e8d8fc3 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1712,7 +1712,7 @@ set severity-output [ none | number | string ]
Change the severity output format of the stats socket connected to for the
duration of the current session.
-set ssl ocsp-response <response>
+set ssl ocsp-response <response | payload>
This command is used to update an OCSP Response for a certificate (see "crt"
on "bind" lines). Same controls are performed as during the initial loading of
the response. The <response> must be passed as a base64 encoded string of the
@@ -1725,6 +1725,10 @@ set ssl ocsp-response <response>
echo "set ssl ocsp-response $(base64 -w 10000 resp.der)" | \
socat stdio /var/run/haproxy.stat
+ using the payload syntax:
+ echo -e "set ssl ocsp-response <<\n$(base64 resp.der)\n" | \
+ socat stdio /var/run/haproxy.stat
+
set ssl tls-key <id> <tlskey>
Set the next TLS key for the <id> listener to <tlskey>. This key becomes the
ultimate key, while the penultimate one is used for encryption (others just
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 70bf66024..db9d4c119 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -8565,16 +8565,28 @@ 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];
+ }
+ payload[j] = 0;
+
+ trash.len = base64dec(payload, j, 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
From 539e7ffbc2667fbcdb9f277cf3a85cb02d2f6b9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
Date: Thu, 26 Apr 2018 07:50:00 +0200
Subject: [PATCH] wip
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Aurélien Nephtali <[email protected]>
---
doc/management.txt | 30 +++++++++++++++-----
src/cli.c | 81 ++++++++++++++++++++++++++++++++++--------------------
src/map.c | 4 ++-
src/ssl_sock.c | 3 +-
4 files changed, 79 insertions(+), 39 deletions(-)
diff --git a/doc/management.txt b/doc/management.txt
index 5fe0d2e8e..a2e8d8fc3 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1310,11 +1310,6 @@ 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.
@@ -1336,11 +1331,28 @@ add acl <acl> <pattern>
In this case, you must use the command "add map" in place of "add acl".
add map <map> <key> <value>
+add map <map> <payload>
Add an entry into the map <map> to associate the value <value> to the key
<key>. This command does not verify if the entry already exists. It is
mainly used to fill a map after a clear operation. Note that if the reference
<map> is a file and is shared with a map, this map will contain also a new
- pattern entry.
+ pattern entry. Using the payload syntax it is possible to add multiple
+ key/value pairs by entering them on separate lines. On each new line, the
+ first word is the key and the rest of the line is considered to be the value
+ which can even contains spaces.
+
+ Example:
+
+ # socat /tmp/sock1 -
+ prompt
+
+ > add map #-1 <<
+ + key1 value1
+ + key2 value2 with spaces
+ + key3 value3 also with spaces
+ + key4 value4
+
+ >
clear counters
Clear the max values of the statistics counters in each proxy (frontend &
@@ -1700,7 +1712,7 @@ set severity-output [ none | number | string ]
Change the severity output format of the stats socket connected to for the
duration of the current session.
-set ssl ocsp-response <response>
+set ssl ocsp-response <response | payload>
This command is used to update an OCSP Response for a certificate (see "crt"
on "bind" lines). Same controls are performed as during the initial loading of
the response. The <response> must be passed as a base64 encoded string of the
@@ -1713,6 +1725,10 @@ set ssl ocsp-response <response>
echo "set ssl ocsp-response $(base64 -w 10000 resp.der)" | \
socat stdio /var/run/haproxy.stat
+ using the payload syntax:
+ echo -e "set ssl ocsp-response <<\n$(base64 resp.der)\n" | \
+ socat stdio /var/run/haproxy.stat
+
set ssl tls-key <id> <tlskey>
Set the next TLS key for the <id> listener to <tlskey>. This key becomes the
ultimate key, while the penultimate one is used for encryption (others just
diff --git a/src/cli.c b/src/cli.c
index 7a61601f0..38d715f83 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -405,14 +405,20 @@ static int cli_parse_request(struct appctx *appctx)
p = appctx->chunk->str;
end = p + appctx->chunk->len;
- /* look for a payload */
+ /*
+ * Get the payload start if there is one.
+ * For the sake of simplicity, the payload pattern is looked up
+ * everywhere from the start of the input but it can only be found
+ * at the end of the first line if APPCTX_CLI_ST1_PAYLOAD is set.
+ *
+ * The input string was zero terminated so it is safe to use
+ * the str*() functions throughout the parsing
+ */
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");
}
/*
@@ -539,6 +545,19 @@ static void cli_io_handler(struct appctx *appctx)
break;
}
else if (appctx->st0 == CLI_ST_GETREQ) {
+ char *str;
+
+ /* 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;
+ }
+ }
+
+ str = appctx->chunk->str + appctx->chunk->len;
+
/* ensure we have some output room left in the event we
* would want to return some info right after parsing.
*/
@@ -547,7 +566,8 @@ static void cli_io_handler(struct appctx *appctx)
break;
}
- reql = co_getline(si_oc(si), trash.str, trash.size);
+ /* '- 1' is to ensure a null byte can always be inserted at the end */
+ reql = co_getline(si_oc(si), str, appctx->chunk->size - appctx->chunk->len - 1);
if (reql <= 0) { /* closed or EOL not found */
if (reql == 0)
break;
@@ -555,18 +575,20 @@ static void cli_io_handler(struct appctx *appctx)
continue;
}
- /* seek for a possible unescaped semi-colon. If we find
- * one, we replace it with an LF and skip only this part.
- */
- for (len = 0; len < reql; len++) {
- if (trash.str[len] == '\\') {
- len++;
- continue;
- }
- if (trash.str[len] == ';') {
- trash.str[len] = '\n';
- reql = len + 1;
- break;
+ if (!(appctx->st1 & APPCTX_CLI_ST1_PAYLOAD)) {
+ /* seek for a possible unescaped semi-colon. If we find
+ * one, we replace it with an LF and skip only this part.
+ */
+ for (len = 0; len < reql; len++) {
+ if (str[len] == '\\') {
+ len++;
+ continue;
+ }
+ if (str[len] == ';') {
+ str[len] = '\n';
+ reql = len + 1;
+ break;
+ }
}
}
@@ -575,27 +597,22 @@ static void cli_io_handler(struct appctx *appctx)
* line.
*/
len = reql - 1;
- if (trash.str[len] != '\n') {
+ if (str[len] != '\n') {
appctx->st0 = CLI_ST_END;
continue;
}
- if (len && trash.str[len-1] == '\r')
+ if (len && str[len-1] == '\r')
len--;
- trash.str[len] = '\0';
+ str[len] = '\0';
+ appctx->chunk->len += len;
- /* 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;
- }
+ if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) {
+ appctx->chunk->str[appctx->chunk->len] = '\n';
+ appctx->chunk->str[appctx->chunk->len + 1] = 0;
+ appctx->chunk->len++;
}
- chunk_appendf(appctx->chunk, "%s", trash.str);
- if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD)
- chunk_appendf(appctx->chunk, "\n");
appctx->st0 = CLI_ST_PROMPT;
@@ -618,7 +635,11 @@ static void cli_io_handler(struct appctx *appctx)
}
}
else {
- /* look for the "payload start" pattern at the end of a line */
+ /*
+ * Look for the "payload start" pattern at the end of a line
+ * Its location is not remembered here, this is just to switch
+ * to a gathering mode.
+ */
if (!strcmp(appctx->chunk->str + appctx->chunk->len - strlen(PAYLOAD_PATTERN), PAYLOAD_PATTERN))
appctx->st1 |= APPCTX_CLI_ST1_PAYLOAD;
else {
diff --git a/src/map.c b/src/map.c
index 64222dc2e..ad47c7f23 100644
--- a/src/map.c
+++ b/src/map.c
@@ -892,8 +892,10 @@ static int cli_parse_add_map(char **args, char *payload, struct appctx *appctx,
payload += strspn(payload, " \t");
value = payload;
l = strcspn(value, "\n");
+ payload += l;
+ if (*payload)
+ payload++;
value[l] = 0;
- payload += l + 1;
ret = map_add_key_value(appctx, key, value, &err);
if (!ret) {
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index ddcc95cb0..db9d4c119 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -8584,8 +8584,9 @@ static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx
continue;
payload[j++] = payload[i];
}
+ payload[j] = 0;
- trash.len = base64dec(payload, strlen(payload), trash.str, trash.size);
+ trash.len = base64dec(payload, j, 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