Hello,
Here is another attempt to add multi-lines support to the CLI.
To enter a multi-lines command, a special pattern (<<) has to be used at
the end of the first line. Once in this mode, everything else is
gathered as it and the command is considered terminated on the reception
of an empty line. The command is then parsed and passed to a parser if a
valid keyword is found. This mode is not persistent across commands.
Technical part:
---------------
Parsing consists of "tagging" the position of each word BEFORE the
multi-lines pattern or until the end of the buffer if there is only one
line. This way, the input buffer is untouched and it is the parser
responsabilty to decide if it needs tokenization of its arguments. To do
this, there are two new functions: cli_resync_args() and
cli_split_args().
The main difficulty is that the parser does not know how many arguments
takes a command and if it accepts a payload (what is found after the
multi-lines pattern), that is why it only tags the input. If a parser
needs N arguments (mandatory or optional) it can call
cli_resync_args(ci, N) to try to get them from the payload. If there is
no payload (or not enough data to reach N args) the missing slots are
filled with the empty string). The list of arguments always ends with a
pointer on an empty string.
Example:
If this command is entered:
set server bk/fe <<
state ready
Three arguments and a payload will be tagged. This command requires (in
some cases) seven arguments (including the keywords) so a call to
cli_resync_args(ci, 7) will extract what can be found from the payload.
Once a parser has done everything it can to ensure it has access to its
arguments, it can call cli_split_args() to do the proper tokenization
and modify the input string to insert null bytes.
For now three commands was modified to take advantage of the multi-line
mode: "set ssl ocsp-response" and "add map"/"add acl".
The number of arguments has been increased to 8192 (from 64) and the
array to hold the pointers gets its memory from a pool. I chose 8192
since it's the maximum number of arguments possible with an input buffer
of 16384 (default tune.bufsize) (16384 / 2 if all arguments are one-char
words). Would computing the max value at runtime be better ?
A field, 'arg_count', is left unused by the parsers (but filled) in
'struct cli_input'. As a next step, instead of checking if the n-th
argument is not an empty string, it could be a good idea to convert all
parsers to check this field so they can know how many arguments they can
access to.
--
Aurélien.
>From 37b3c2f86bc77f6fd9a9eb416b43c1183cd81351 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
Date: Sun, 15 Apr 2018 18:38:51 +0200
Subject: [PATCH] MEDIUM: cli: Add multi-line mode support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It is now possible to write commands spanning on multiple lines. To do
so, a special pattern (<<) must be entered at the end of the first line.
Example:
# echo -e "set ssl ocsp-response <<\n$(base64 ocsp.der)\n" | \
socat /tmp/sock1 -
All commands support this mode and some of them ("set ssl ocsp-response"
and "add map"/"add acl") have seen their syntax enhanced to take advantage
of it (i.e.: accept base64 on multiple lines or accept multiple key/value
pairs).
Signed-off-by: Aurélien Nephtali <[email protected]>
---
doc/management.txt | 24 ++++
include/common/defaults.h | 4 +-
include/proto/applet.h | 4 +-
include/proto/cli.h | 3 +-
include/types/applet.h | 6 +-
include/types/cli.h | 22 ++-
src/cache.c | 2 +-
src/cli.c | 344 +++++++++++++++++++++++++++++++---------------
src/dns.c | 9 +-
src/hlua.c | 8 +-
src/map.c | 188 +++++++++++++++++--------
src/proto_http.c | 15 +-
src/proxy.c | 66 ++++++---
src/server.c | 131 +++++++++++-------
src/ssl_sock.c | 42 ++++--
src/stats.c | 39 ++++--
src/stick_table.c | 19 +--
src/stream.c | 27 ++--
18 files changed, 647 insertions(+), 306 deletions(-)
diff --git a/doc/management.txt b/doc/management.txt
index 4b6901851..f01c87e95 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1298,6 +1298,30 @@ 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.
+It is also possible to send commands spanning on multiple lines. This is done by
+ending the first line with the "<<" pattern. The next lines will be treated as
+being part of the command and an empty line will validate the command.
+This multi-line mode is also usable with the interactive mode: in this case the
+prompt will change from "> " to "+ ".
+The pattern can be used anywhere after the command keyword (i.e: after "set server"
+but not between "set" and "server").
+
+Examples:
+
+ # echo -e "set ssl ocsp-response <<\n$(base64 ocsp.der)\n" | \
+ socat /tmp/sock1 -
+
+ # socat /tmp/sock1 -
+ prompt
+
+ > add map tests/map_acl <<
+ + test.com bk
+ + test2.com bk
+ + test3.com bk
+ +
+
+ >
+
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/common/defaults.h b/include/common/defaults.h
index f53c611e2..08c034f2f 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -81,8 +81,8 @@
#define MAX_CRT_ARGS 2048
// max # args on a stats socket
-// This should cover at least 5 + twice the # of data_types
-#define MAX_STATS_ARGS 64
+// one extra slot to store an empty string
+#define MAX_STATS_ARGS (8192 + 1)
// max # of matches per regexp
#define MAX_MATCH 10
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..433b202ce 100644
--- a/include/proto/cli.h
+++ b/include/proto/cli.h
@@ -24,10 +24,11 @@
#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);
+void cli_resync_args(struct cli_input *ci, int total);
+void cli_split_args(struct cli_input *ci);
#endif /* _PROTO_CLI_H */
diff --git a/include/types/applet.h b/include/types/applet.h
index 89c318c1d..837403dab 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_MULTILINE (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/multi-line (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..e7f0d4ff0 100644
--- a/include/types/cli.h
+++ b/include/types/cli.h
@@ -23,11 +23,31 @@
#include <common/mini-clist.h>
#include <types/applet.h>
+struct cli_input {
+ /*
+ * array of pointers on each arg
+ *
+ * NOTES:
+ * - tokenization is NOT done (no null byte between args)
+ * - if a parser needs to split args, it shall call cli_split_args()
+ * - unused slots are filled with a pointer on the null byte of the input
+ * string
+ */
+ char **args;
+ /* number of args */
+ int arg_count;
+ /*
+ * pointer on the payload (data after the multi-line pattern) if any,
+ * NULL otherwise
+ */
+ char *payload;
+};
+
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)(struct cli_input *kw, 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..215805de9 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(struct cli_input *ci, 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..e61dbc7c8 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -67,6 +67,8 @@
#include <proto/task.h>
#include <proto/proto_udp.h>
+#define MULTILINE_PATTERN "<<"
+
static struct applet cli_applet;
static const char stats_sock_usage_msg[] =
@@ -82,6 +84,7 @@ static const char stats_permission_denied_msg[] =
static THREAD_LOCAL char *dynamic_usage_msg = NULL;
+static struct pool_head *cli_args_pool;
/* List head of cli keywords */
static struct cli_kw_list cli_keywords = {
@@ -90,7 +93,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 +104,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 +118,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;
}
@@ -123,7 +138,7 @@ struct cli_kw* cli_find_kw(char **args)
struct cli_kw_list *kw_list;
struct cli_kw *kw;/* current cli_kw */
char **tmp_args;
- const char **tmp_str_kw;
+ const char **tmp_str_kw, *tmp_args_end;
int found = 0;
if (LIST_ISEMPTY(&cli_keywords.list))
@@ -134,8 +149,9 @@ struct cli_kw* cli_find_kw(char **args)
while (*kw->str_kw) {
tmp_args = args;
tmp_str_kw = kw->str_kw;
+ tmp_args_end = *tmp_args + strlen(*tmp_str_kw);
while (*tmp_str_kw) {
- if (strcmp(*tmp_str_kw, *tmp_args) == 0) {
+ if (!strncmp(*tmp_str_kw, *tmp_args, strlen(*tmp_str_kw)) && (!*tmp_args_end || *tmp_args_end == ' ' || *tmp_args_end == '\t')) {
found = 1;
} else {
found = 0;
@@ -369,6 +385,49 @@ static int cli_get_severity_output(struct appctx *appctx)
return strm_li(si_strm(appctx->owner))->bind_conf->severity_output;
}
+/*
+ * Consume the data in <ci->payload> to tag more args.
+ * <ci->payload> is set to NULL if it contains no more data.
+ */
+void cli_resync_args(struct cli_input *ci, int total)
+{
+ char *p;
+
+ if (!ci->payload)
+ return ;
+
+ p = ci->payload;
+ while (ci->arg_count < total && ci->arg_count < MAX_STATS_ARGS && *p) {
+ p += strspn(p, " \t\n");
+ if (!*p)
+ break;
+
+ ci->args[ci->arg_count++] = p;
+ p += strcspn(p, " \t\n");
+ }
+ ci->payload = p + strspn(p, "\n");
+ if (!*ci->payload)
+ ci->payload = NULL;
+}
+
+/*
+ * Tokenize the input.
+ * Given a pre-tagged input (array of pointers in <ci->args>), tokenize the first
+ * <ci->arg_count> args.
+ */
+void cli_split_args(struct cli_input *ci)
+{
+ int i;
+
+ for (i = 0; i < ci->arg_count; i++) {
+ size_t l;
+
+ ci->args[i] += strspn(ci->args[i], " \t\n");
+ l = strcspn(ci->args[i], " \t\n");
+ ci->args[i][l] = 0;
+ }
+}
+
/* Processes the CLI interpreter on the stats socket. This function is called
* from the CLI's IO handler running in an appctx context. The function returns 1
* if the request was understood, otherwise zero. It is called with appctx->st0
@@ -379,73 +438,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];
+ int ret = 0;
+ struct cli_input ci;
+ char *p;
+ 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;
+ memset(&ci, 0, sizeof(ci));
- while (*line && arg < MAX_STATS_ARGS) {
- if (*line == '\\') {
- line++;
- if (*line == '\0')
- break;
- }
- else if (isspace((unsigned char)*line)) {
- *line++ = '\0';
+ ci.args = pool_alloc(cli_args_pool);
+ if (!ci.args)
+ return 0;
- while (isspace((unsigned char)*line))
- line++;
+ /*
+ * Get pointers on words.
+ * If the multi-line mode pattern is found, the rest of
+ * the buffer will be considered as the payload.
+ * One extra slot if reserved to store a pointer on a null byte.
+ */
+ p = appctx->chunk->str;
+ while (i < MAX_STATS_ARGS - 1 && *p) {
+ p += strspn(p, " \t");
+ if (!*p)
+ break;
- args[++arg] = line;
- continue;
+ if (appctx->st1 & APPCTX_CLI_ST1_MULTILINE &&
+ !ci.payload &&
+ !strncmp(p, MULTILINE_PATTERN, strlen(MULTILINE_PATTERN))) {
+ p += strlen(MULTILINE_PATTERN);
+ p += strspn(p, " \t");
+ ci.payload = p;
}
+ ci.args[i++] = p;
+ if (!ci.payload)
+ ci.arg_count++;
- line++;
- }
-
- 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++;
- else
- continue;
- }
- args[arg][j] = args[arg][i];
- j++;
- }
- args[arg][j] = '\0';
- arg++;
+ p += strcspn(p, " \t");
}
+ /* fill unused slots */
+ p = appctx->chunk->str + appctx->chunk->len;
+ for (; i < MAX_STATS_ARGS; i++)
+ ci.args[i] = p;
- appctx->st2 = 0;
- memset(&appctx->ctx.cli, 0, sizeof(appctx->ctx.cli));
-
- kw = cli_find_kw(args);
+ kw = cli_find_kw(ci.args);
if (!kw)
- return 0;
+ goto end;
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(&ci, appctx, kw->private) == 0) && appctx->io_handler) {
appctx->st0 = CLI_ST_CALLBACK;
}
- return 1;
+
+ ret = 1;
+end:
+ pool_free(cli_args_pool, ci.args);
+
+ return ret;
}
/* prepends then outputs the argument msg with a syslog-type severity depending on severity_output value */
@@ -519,6 +574,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 +624,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_MULTILINE)
+ chunk_appendf(appctx->chunk, "\n");
+
+ appctx->st0 = CLI_ST_PROMPT;
+
+ if (appctx->st1 & APPCTX_CLI_ST1_MULTILINE) {
+ /* 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_MULTILINE;
}
+ }
+ else {
+ /* the multi-line mode pattern must be at the end of a line */
+ if (!strcmp(appctx->chunk->str + appctx->chunk->len - strlen(MULTILINE_PATTERN), MULTILINE_PATTERN))
+ appctx->st1 |= APPCTX_CLI_ST1_MULTILINE;
else {
- appctx->ctx.cli.severity = LOG_INFO;
- appctx->ctx.cli.msg = stats_sock_usage_msg;
+ /* "simple line" mode, 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 +715,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) {
+ /*
+ * in multi-line and interactive mode, change the prompt
+ * to emphasize that more data can still be sent
+ */
+ if (appctx->chunk->len && appctx->st1 & APPCTX_CLI_ST1_MULTILINE)
+ prompt = "+ ";
+ else
+ prompt = "\n> ";
+ }
+ else {
+ if (!(appctx->st1 & APPCTX_CLI_ST1_MULTILINE))
+ prompt = "\n";
+ }
+
+ if (ci_putstr(si_ic(si), prompt) != -1)
appctx->st0 = CLI_ST_GETREQ;
else
si_applet_cant_put(si);
@@ -671,7 +747,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 +1096,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
extern char **environ;
char **var;
@@ -1030,11 +1106,14 @@ static int cli_parse_show_env(char **args, struct appctx *appctx, void *private)
var = environ;
- if (*args[2]) {
- int len = strlen(args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (*ci->args[2]) {
+ int len = strlen(ci->args[2]);
for (; *var; var++) {
- if (strncmp(*var, args[2], len) == 0 &&
+ if (strncmp(*var, ci->args[2], len) == 0 &&
(*var)[len] == '=')
break;
}
@@ -1054,38 +1133,44 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
return 1;
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
appctx->ctx.cli.i0 = 0;
- if (*args[2]) {
- appctx->ctx.cli.i0 = atoi(args[2]);
+ if (*ci->args[2]) {
+ appctx->ctx.cli.i0 = atoi(ci->args[2]);
appctx->st2 = STAT_ST_END;
}
return 0;
}
/* 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct stream_interface *si = appctx->owner;
struct stream *s = si_strm(si);
- if (strcmp(args[2], "cli") == 0) {
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[2], "cli") == 0) {
unsigned timeout;
const char *res;
- if (!*args[3]) {
+ if (!*ci->args[3]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Expects an integer value.\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
+ res = parse_time_err(ci->args[3], &timeout, TIME_UNIT_S);
if (res || timeout < 1) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Invalid timeout value.\n";
@@ -1106,21 +1191,24 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
int v;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- if (!*args[3]) {
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ if (!*ci->args[3]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Expects an integer value.\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- v = atoi(args[3]);
+ v = atoi(ci->args[3]);
if (v > global.hardmaxconn) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Value out of range.\n";
@@ -1159,9 +1247,12 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (*args[2] && set_severity_output(&appctx->cli_severity_output, args[2]))
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (*ci->args[2] && set_severity_output(&appctx->cli_severity_output, ci->args[2]))
return 0;
appctx->ctx.cli.severity = LOG_ERR;
@@ -1170,13 +1261,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(struct cli_input *ci, 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
int v;
int *res;
@@ -1185,15 +1276,18 @@ static int cli_parse_set_ratelimit(char **args, struct appctx *appctx, void *pri
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- if (strcmp(args[2], "connections") == 0 && strcmp(args[3], "global") == 0)
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[2], "connections") == 0 && strcmp(ci->args[3], "global") == 0)
res = &global.cps_lim;
- else if (strcmp(args[2], "sessions") == 0 && strcmp(args[3], "global") == 0)
+ else if (strcmp(ci->args[2], "sessions") == 0 && strcmp(ci->args[3], "global") == 0)
res = &global.sps_lim;
#ifdef USE_OPENSSL
- else if (strcmp(args[2], "ssl-sessions") == 0 && strcmp(args[3], "global") == 0)
+ else if (strcmp(ci->args[2], "ssl-sessions") == 0 && strcmp(ci->args[3], "global") == 0)
res = &global.ssl_lim;
#endif
- else if (strcmp(args[2], "http-compression") == 0 && strcmp(args[3], "global") == 0) {
+ else if (strcmp(ci->args[2], "http-compression") == 0 && strcmp(ci->args[3], "global") == 0) {
res = &global.comp_rate_lim;
mul = 1024;
}
@@ -1211,14 +1305,14 @@ static int cli_parse_set_ratelimit(char **args, struct appctx *appctx, void *pri
return 1;
}
- if (!*args[4]) {
+ if (!*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Expects an integer value.\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- v = atoi(args[4]);
+ v = atoi(ci->args[4]);
if (v < 0) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Value out of range.\n";
@@ -1296,7 +1390,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
char *cmsgbuf = NULL;
unsigned char *tmpbuf = NULL;
@@ -1474,6 +1568,20 @@ out:
return 1;
}
+static int cli_parse_simple(struct cli_input *ci, struct appctx *appctx, void *private)
+{
+ if (*ci->args[0] == 'h')
+ /* help */
+ cli_gen_usage_msg(appctx);
+ else if (*ci->args[0] == 'p')
+ /* prompt */
+ appctx->st1 ^= APPCTX_CLI_ST1_PROMPT;
+ else if (*ci->args[0] == 'q')
+ /* quit */
+ appctx->st0 = CLI_ST_END;
+
+ return 1;
+}
static struct applet cli_applet = {
@@ -1485,6 +1593,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 },
@@ -1509,12 +1620,19 @@ static struct bind_kw_list bind_kws = { "STAT", { }, {
{ NULL, NULL, 0 },
}};
+static void __cli_deinit(void)
+{
+ pool_destroy(cli_args_pool);
+}
+
__attribute__((constructor))
static void __dumpstats_module_init(void)
{
cfg_register_keywords(&cfg_kws);
cli_register_kw(&cli_kws);
bind_register_keywords(&bind_kws);
+ hap_register_post_deinit(__cli_deinit);
+ cli_args_pool = create_pool("cli_args", MAX_STATS_ARGS * sizeof(char*), MEM_F_SHARED);
}
/*
diff --git a/src/dns.c b/src/dns.c
index 5fe44d433..ee7e27658 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -1948,13 +1948,16 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct dns_resolvers *presolvers;
- if (*args[2]) {
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (*ci->args[2]) {
list_for_each_entry(presolvers, &dns_resolvers, list) {
- if (strcmp(presolvers->id, args[2]) == 0) {
+ if (strcmp(presolvers->id, ci->args[2]) == 0) {
appctx->ctx.cli.p0 = presolvers;
break;
}
diff --git a/src/hlua.c b/src/hlua.c
index 60cf8f948..9d7d250e8 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct hlua *hlua;
struct hlua_function *fcn;
@@ -6990,14 +6990,16 @@ static int hlua_cli_parse_fct(char **args, struct appctx *appctx, void *private)
}
hlua->nargs = 1;
+ cli_split_args(ci);
+
/* push keywords in the stack. */
- for (i = 0; *args[i]; i++) {
+ for (i = 0; *ci->args[i]; i++) {
/* Check stack available size. */
if (!lua_checkstack(hlua->T, 1)) {
SEND_ERR(NULL, "Lua cli '%s': full stack.\n", fcn->name);
goto error;
}
- lua_pushstring(hlua->T, args[i]);
+ lua_pushstring(hlua->T, ci->args[i]);
hlua->nargs++;
}
diff --git a/src/map.c b/src/map.c
index 7953c2a0b..da508ee93 100644
--- a/src/map.c
+++ b/src/map.c
@@ -565,17 +565,20 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[1], "map") == 0 || strcmp(ci->args[1], "acl") == 0) {
/* Set flags. */
- if (args[1][0] == 'm')
+ if (ci->args[1][0] == 'm')
appctx->ctx.map.display_flags = PAT_REF_MAP;
else
appctx->ctx.map.display_flags = PAT_REF_ACL;
/* No parameter. */
- if (!*args[2] || !*args[3]) {
+ if (!*ci->args[2] || !*ci->args[3]) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
@@ -589,7 +592,7 @@ static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
}
/* lookup into the maps */
- appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+ appctx->ctx.map.ref = pat_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.map.ref) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
appctx->ctx.cli.severity = LOG_ERR;
@@ -607,9 +610,9 @@ static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
* it may be used over multiple iterations. It's released
* at the end and upon abort anyway.
*/
- appctx->ctx.map.chunk.len = strlen(args[3]);
+ appctx->ctx.map.chunk.len = strlen(ci->args[3]);
appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
- appctx->ctx.map.chunk.str = strdup(args[3]);
+ appctx->ctx.map.chunk.str = strdup(ci->args[3]);
if (!appctx->ctx.map.chunk.str) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Out of memory error.\n";
@@ -632,25 +635,28 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (strcmp(args[1], "map") == 0 ||
- strcmp(args[1], "acl") == 0) {
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[1], "map") == 0 ||
+ strcmp(ci->args[1], "acl") == 0) {
/* Set ACL or MAP flags. */
- if (args[1][0] == 'm')
+ if (ci->args[1][0] == 'm')
appctx->ctx.map.display_flags = PAT_REF_MAP;
else
appctx->ctx.map.display_flags = PAT_REF_ACL;
/* no parameter: display all map available */
- if (!*args[2]) {
+ if (!*ci->args[2]) {
appctx->io_handler = cli_io_handler_pats_list;
return 0;
}
/* lookup into the refs and check the map flag */
- appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+ appctx->ctx.map.ref = pat_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.map.ref ||
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
@@ -672,16 +678,19 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (strcmp(args[1], "map") == 0) {
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[1], "map") == 0) {
char *err;
/* Set flags. */
appctx->ctx.map.display_flags = PAT_REF_MAP;
/* Expect three parameters: map name, key and new value. */
- if (!*args[2] || !*args[3] || !*args[4]) {
+ if (!*ci->args[2] || !*ci->args[3] || !*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -689,7 +698,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
}
/* Lookup the reference in the maps. */
- appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+ appctx->ctx.map.ref = pat_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.map.ref) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
@@ -700,13 +709,13 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
/* If the entry identifier start with a '#', it is considered as
* pointer id
*/
- if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
+ if (ci->args[3][0] == '#' && ci->args[3][1] == '0' && ci->args[3][2] == 'x') {
struct pat_ref_elt *ref;
long long int conv;
char *error;
/* Convert argument to integer value. */
- conv = strtoll(&args[3][1], &error, 16);
+ conv = strtoll(&ci->args[3][1], &error, 16);
if (*error != '\0') {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
@@ -726,7 +735,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
/* Try to modify the entry. */
err = NULL;
HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
- if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
+ if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, ci->args[4], &err)) {
HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
if (err) {
memprintf(&err, "%s.\n", err);
@@ -748,7 +757,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
*/
err = NULL;
HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
- if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
+ if (!pat_ref_set(appctx->ctx.map.ref, ci->args[3], ci->args[4], &err)) {
HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
if (err) {
memprintf(&err, "%s.\n", err);
@@ -772,15 +781,32 @@ 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 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (strcmp(args[1], "map") == 0 ||
- strcmp(args[1], "acl") == 0) {
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[1], "map") == 0 ||
+ strcmp(ci->args[1], "acl") == 0) {
int ret;
char *err;
/* Set flags. */
- if (args[1][0] == 'm')
+ if (ci->args[1][0] == 'm')
appctx->ctx.map.display_flags = PAT_REF_MAP;
else
appctx->ctx.map.display_flags = PAT_REF_ACL;
@@ -789,7 +815,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
* is "acl", we expect only two parameters
*/
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
- if (!*args[2] || !*args[3] || !*args[4]) {
+ if (!*ci->args[2] || !*ci->args[3] || !*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -797,7 +823,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
}
}
else {
- if (!*args[2] || !*args[3]) {
+ if (!*ci->args[2] || !*ci->args[3]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -806,7 +832,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
}
/* Lookup for the reference. */
- appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+ appctx->ctx.map.ref = pat_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.map.ref) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
appctx->ctx.cli.severity = LOG_ERR;
@@ -832,26 +858,66 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
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 (!ci->payload) {
+ ret = map_add_key_value(appctx, ci->args[3], ci->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 = ci->payload + strlen(ci->payload);
+
+ while (ci->payload < end) {
+ char *key, *value;
+ size_t l;
+
+ /* key */
+ key = ci->payload;
+ l = strcspn(key, " \t");
+ key[l] = 0;
+ ci->payload += l + 1;
+
+ if (*ci->payload == '\n' && 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;
+ }
+
+ /* value */
+ ci->payload += strspn(ci->payload, " \t");
+ value = ci->payload;
+ l = strcspn(value, "\n");
+ value[l] = 0;
+ ci->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. */
@@ -862,16 +928,19 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (args[1][0] == 'm')
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ if (ci->args[1][0] == 'm')
appctx->ctx.map.display_flags = PAT_REF_MAP;
else
appctx->ctx.map.display_flags = PAT_REF_ACL;
/* Expect two parameters: map name and key. */
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
- if (!*args[2] || !*args[3]) {
+ if (!*ci->args[2] || !*ci->args[3]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -880,7 +949,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
}
else {
- if (!*args[2] || !*args[3]) {
+ if (!*ci->args[2] || !*ci->args[3]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -889,7 +958,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
}
/* Lookup the reference in the maps. */
- appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+ appctx->ctx.map.ref = pat_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.map.ref ||
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
appctx->ctx.cli.severity = LOG_ERR;
@@ -901,13 +970,13 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
/* If the entry identifier start with a '#', it is considered as
* pointer id
*/
- if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
+ if (ci->args[3][0] == '#' && ci->args[3][1] == '0' && ci->args[3][2] == 'x') {
struct pat_ref_elt *ref;
long long int conv;
char *error;
/* Convert argument to integer value. */
- conv = strtoll(&args[3][1], &error, 16);
+ conv = strtoll(&ci->args[3][1], &error, 16);
if (*error != '\0') {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
@@ -941,7 +1010,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
* string and try to delete the entry.
*/
HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
- if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
+ if (!pat_ref_delete(appctx->ctx.map.ref, ci->args[3])) {
HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
/* The entry is not found, send message. */
appctx->ctx.cli.severity = LOG_ERR;
@@ -958,17 +1027,20 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
- if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[1], "map") == 0 || strcmp(ci->args[1], "acl") == 0) {
/* Set ACL or MAP flags. */
- if (args[1][0] == 'm')
+ if (ci->args[1][0] == 'm')
appctx->ctx.map.display_flags = PAT_REF_MAP;
else
appctx->ctx.map.display_flags = PAT_REF_ACL;
/* no parameter */
- if (!*args[2]) {
+ if (!*ci->args[2]) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Missing map identifier.\n";
@@ -982,7 +1054,7 @@ static int cli_parse_clear_map(char **args, struct appctx *appctx, void *private
}
/* lookup into the refs and check the map flag */
- appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+ appctx->ctx.map.ref = pat_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.map.ref ||
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
diff --git a/src/proto_http.c b/src/proto_http.c
index 8370889b4..75ecc7f39 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -12491,19 +12491,22 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
return 1;
- if (*args[2]) {
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ if (*ci->args[2]) {
struct proxy *px;
- px = proxy_find_by_name(args[2], 0, 0);
+ px = proxy_find_by_name(ci->args[2], 0, 0);
if (px)
appctx->ctx.errors.iid = px->uuid;
else
- appctx->ctx.errors.iid = atoi(args[2]);
+ appctx->ctx.errors.iid = atoi(ci->args[2]);
if (!appctx->ctx.errors.iid) {
appctx->ctx.cli.severity = LOG_ERR;
@@ -12516,9 +12519,9 @@ static int cli_parse_show_errors(char **args, struct appctx *appctx, void *priva
appctx->ctx.errors.iid = -1; // dump all proxies
appctx->ctx.errors.flag = 0;
- if (strcmp(args[3], "request") == 0)
+ if (strcmp(ci->args[3], "request") == 0)
appctx->ctx.errors.flag |= 4; // ignore response
- else if (strcmp(args[3], "response") == 0)
+ else if (strcmp(ci->args[3], "response") == 0)
appctx->ctx.errors.flag |= 2; // ignore request
appctx->ctx.errors.px = NULL;
return 0;
diff --git a/src/proxy.c b/src/proxy.c
index 89f679ff5..273055c8a 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1393,14 +1393,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
/* check if a backend name has been provided */
- if (*args[3]) {
+ if (*ci->args[3]) {
/* read server state from local file */
- px = proxy_be_by_name(args[3]);
+ px = proxy_be_by_name(ci->args[3]);
if (!px) {
appctx->ctx.cli.severity = LOG_ERR;
@@ -1558,7 +1561,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
struct server *s;
@@ -1566,7 +1569,10 @@ static int cli_parse_enable_dyncookie_backend(char **args, struct appctx *appctx
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_backend(appctx, args[3]);
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ px = cli_find_backend(appctx, ci->args[3]);
if (!px)
return 1;
@@ -1579,7 +1585,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
struct server *s;
@@ -1587,7 +1593,10 @@ static int cli_parse_disable_dyncookie_backend(char **args, struct appctx *appct
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_backend(appctx, args[3]);
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ px = cli_find_backend(appctx, ci->args[3]);
if (!px)
return 1;
@@ -1604,7 +1613,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
struct server *s;
@@ -1613,18 +1622,21 @@ static int cli_parse_set_dyncookie_key_backend(char **args, struct appctx *appct
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_backend(appctx, args[3]);
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ px = cli_find_backend(appctx, ci->args[3]);
if (!px)
return 1;
- if (!*args[4]) {
+ if (!*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "String value expected.\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- newkey = strdup(args[4]);
+ newkey = strdup(ci->args[4]);
if (!newkey) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Failed to allocate memory.\n";
@@ -1641,7 +1653,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
struct listener *l;
@@ -1650,18 +1662,21 @@ static int cli_parse_set_maxconn_frontend(char **args, struct appctx *appctx, vo
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_frontend(appctx, args[3]);
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ px = cli_find_frontend(appctx, ci->args[3]);
if (!px)
return 1;
- if (!*args[4]) {
+ if (!*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Integer value expected.\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- v = atoi(args[4]);
+ v = atoi(ci->args[4]);
if (v < 0) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Value out of range.\n";
@@ -1686,14 +1701,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_frontend(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ px = cli_find_frontend(appctx, ci->args[2]);
if (!px)
return 1;
@@ -1713,14 +1731,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_frontend(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ px = cli_find_frontend(appctx, ci->args[2]);
if (!px)
return 1;
@@ -1748,14 +1769,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- px = cli_find_frontend(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ px = cli_find_frontend(appctx, ci->args[2]);
if (!px)
return 1;
diff --git a/src/server.c b/src/server.c
index 28cc7418c..cd4a9a306 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
const char *warning;
@@ -4096,26 +4096,29 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 7);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
- if (strcmp(args[3], "weight") == 0) {
- warning = server_parse_weight_change_request(sv, args[4]);
+ if (strcmp(ci->args[3], "weight") == 0) {
+ warning = server_parse_weight_change_request(sv, ci->args[4]);
if (warning) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = warning;
appctx->st0 = CLI_ST_PRINT;
}
}
- else if (strcmp(args[3], "state") == 0) {
- if (strcmp(args[4], "ready") == 0)
+ else if (strcmp(ci->args[3], "state") == 0) {
+ if (strcmp(ci->args[4], "ready") == 0)
srv_adm_set_ready(sv);
- else if (strcmp(args[4], "drain") == 0)
+ else if (strcmp(ci->args[4], "drain") == 0)
srv_adm_set_drain(sv);
- else if (strcmp(args[4], "maint") == 0)
+ else if (strcmp(ci->args[4], "maint") == 0)
srv_adm_set_maint(sv);
else {
appctx->ctx.cli.severity = LOG_ERR;
@@ -4123,21 +4126,21 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
appctx->st0 = CLI_ST_PRINT;
}
}
- else if (strcmp(args[3], "health") == 0) {
+ else if (strcmp(ci->args[3], "health") == 0) {
if (sv->track) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "cannot change health on a tracking server.\n";
appctx->st0 = CLI_ST_PRINT;
}
- else if (strcmp(args[4], "up") == 0) {
+ else if (strcmp(ci->args[4], "up") == 0) {
sv->check.health = sv->check.rise + sv->check.fall - 1;
srv_set_running(sv, "changed from CLI", NULL);
}
- else if (strcmp(args[4], "stopping") == 0) {
+ else if (strcmp(ci->args[4], "stopping") == 0) {
sv->check.health = sv->check.rise + sv->check.fall - 1;
srv_set_stopping(sv, "changed from CLI", NULL);
}
- else if (strcmp(args[4], "down") == 0) {
+ else if (strcmp(ci->args[4], "down") == 0) {
sv->check.health = 0;
srv_set_stopped(sv, "changed from CLI", NULL);
}
@@ -4147,17 +4150,17 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
appctx->st0 = CLI_ST_PRINT;
}
}
- else if (strcmp(args[3], "agent") == 0) {
+ else if (strcmp(ci->args[3], "agent") == 0) {
if (!(sv->agent.state & CHK_ST_ENABLED)) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
appctx->st0 = CLI_ST_PRINT;
}
- else if (strcmp(args[4], "up") == 0) {
+ else if (strcmp(ci->args[4], "up") == 0) {
sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
srv_set_running(sv, "changed from CLI", NULL);
}
- else if (strcmp(args[4], "down") == 0) {
+ else if (strcmp(ci->args[4], "down") == 0) {
sv->agent.health = 0;
srv_set_stopped(sv, "changed from CLI", NULL);
}
@@ -4167,26 +4170,26 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
appctx->st0 = CLI_ST_PRINT;
}
}
- else if (strcmp(args[3], "agent-addr") == 0) {
+ else if (strcmp(ci->args[3], "agent-addr") == 0) {
if (!(sv->agent.state & CHK_ST_ENABLED)) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
appctx->st0 = CLI_ST_PRINT;
} else {
- if (str2ip(args[4], &sv->agent.addr) == NULL) {
+ if (str2ip(ci->args[4], &sv->agent.addr) == NULL) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "incorrect addr address given for agent.\n";
appctx->st0 = CLI_ST_PRINT;
}
}
}
- else if (strcmp(args[3], "agent-send") == 0) {
+ else if (strcmp(ci->args[3], "agent-send") == 0) {
if (!(sv->agent.state & CHK_ST_ENABLED)) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
appctx->st0 = CLI_ST_PRINT;
} else {
- char *nss = strdup(args[4]);
+ char *nss = strdup(ci->args[4]);
if (!nss) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "cannot allocate memory for new string.\n";
@@ -4194,13 +4197,13 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
} else {
free(sv->agent.send_string);
sv->agent.send_string = nss;
- sv->agent.send_string_len = strlen(args[4]);
+ sv->agent.send_string_len = strlen(ci->args[4]);
}
}
}
- else if (strcmp(args[3], "check-port") == 0) {
+ else if (strcmp(ci->args[3], "check-port") == 0) {
int i = 0;
- if (strl2irc(args[4], strlen(args[4]), &i) != 0) {
+ if (strl2irc(ci->args[4], strlen(ci->args[4]), &i) != 0) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'set server <srv> check-port' expects an integer as argument.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -4224,20 +4227,20 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
appctx->ctx.cli.msg = "health check port updated.\n";
appctx->st0 = CLI_ST_PRINT;
}
- else if (strcmp(args[3], "addr") == 0) {
+ else if (strcmp(ci->args[3], "addr") == 0) {
char *addr = NULL;
char *port = NULL;
- if (strlen(args[4]) == 0) {
+ if (strlen(ci->args[4]) == 0) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "set server <b>/<s> addr requires an address and optionally a port.\n";
appctx->st0 = CLI_ST_PRINT;
goto out_unlock;
}
else {
- addr = args[4];
+ addr = ci->args[4];
}
- if (strcmp(args[5], "port") == 0) {
- port = args[6];
+ if (strcmp(ci->args[5], "port") == 0) {
+ port = ci->args[6];
}
warning = update_server_addr_port(sv, addr, port, "stats socket command");
if (warning) {
@@ -4247,14 +4250,14 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
}
srv_clr_admin_flag(sv, SRV_ADMF_RMAINT);
}
- else if (strcmp(args[3], "fqdn") == 0) {
- if (!*args[4]) {
+ else if (strcmp(ci->args[3], "fqdn") == 0) {
+ if (!*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "set server <b>/<s> fqdn requires a FQDN.\n";
appctx->st0 = CLI_ST_PRINT;
goto out_unlock;
}
- warning = update_server_fqdn(sv, args[4], "stats socket command", 0);
+ warning = update_server_fqdn(sv, ci->args[4], "stats socket command", 0);
if (warning) {
appctx->ctx.cli.severity = LOG_WARNING;
appctx->ctx.cli.msg = warning;
@@ -4271,16 +4274,18 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct stream_interface *si = appctx->owner;
struct proxy *px;
struct server *sv;
char *line;
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
/* split "backend/server" and make <line> point to server */
- for (line = args[2]; *line; line++)
+ for (line = ci->args[2]; *line; line++)
if (*line == '/') {
*line++ = '\0';
break;
@@ -4293,7 +4298,7 @@ static int cli_parse_get_weight(char **args, struct appctx *appctx, void *privat
return 1;
}
- if (!get_backend_server(args[2], line, &px, &sv)) {
+ if (!get_backend_server(ci->args[2], line, &px, &sv)) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
appctx->st0 = CLI_ST_PRINT;
@@ -4309,7 +4314,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
const char *warning;
@@ -4317,11 +4322,14 @@ static int cli_parse_set_weight(char **args, struct appctx *appctx, void *privat
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
- warning = server_parse_weight_change_request(sv, args[3]);
+ warning = server_parse_weight_change_request(sv, ci->args[3]);
if (warning) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = warning;
@@ -4331,7 +4339,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
const char *warning;
@@ -4339,11 +4347,14 @@ static int cli_parse_set_maxconn_server(char **args, struct appctx *appctx, void
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[3]);
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[3]);
if (!sv)
return 1;
- warning = server_parse_maxconn_change_request(sv, args[4]);
+ warning = server_parse_maxconn_change_request(sv, ci->args[4]);
if (warning) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = warning;
@@ -4353,14 +4364,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
@@ -4369,14 +4383,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
@@ -4385,14 +4402,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
@@ -4401,14 +4421,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
@@ -4424,14 +4447,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
@@ -4440,14 +4466,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[2]);
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[2]);
if (!sv)
return 1;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 23ad35b18..777048b30 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -8500,20 +8500,23 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
/* no parameter, shows only file list */
- if (!*args[2]) {
+ if (!*ci->args[2]) {
appctx->ctx.cli.i0 = 1;
appctx->io_handler = cli_io_handler_tlskeys_files;
return 0;
}
- if (args[2][0] == '*') {
+ if (ci->args[2][0] == '*') {
/* list every TLS ticket keys */
appctx->ctx.cli.i0 = 1;
} else {
- appctx->ctx.cli.p0 = tlskeys_ref_lookup_ref(args[2]);
+ appctx->ctx.cli.p0 = tlskeys_ref_lookup_ref(ci->args[2]);
if (!appctx->ctx.cli.p0) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'show tls-keys' unable to locate referenced filename\n";
@@ -8525,19 +8528,22 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct tls_keys_ref *ref;
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
/* Expect two parameters: the filename and the new new TLS key in encoding */
- if (!*args[3] || !*args[4]) {
+ if (!*ci->args[3] || !*ci->args[4]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'set ssl tls-key' expects a filename and the new TLS key in base64 encoding.\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- ref = tlskeys_ref_lookup_ref(args[3]);
+ ref = tlskeys_ref_lookup_ref(ci->args[3]);
if (!ref) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'set ssl tls-key' unable to locate referenced filename\n";
@@ -8545,7 +8551,7 @@ static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *priva
return 1;
}
- trash.len = base64dec(args[4], strlen(args[4]), trash.str, trash.size);
+ trash.len = base64dec(ci->args[4], strlen(ci->args[4]), trash.str, trash.size);
if (trash.len != sizeof(struct tls_sess_key)) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "'set ssl tls-key' received invalid base64 encoded TLS key.\n";
@@ -8561,20 +8567,32 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
+ char *payload;
char *err = NULL;
+ int i, j;
+
+ cli_resync_args(ci, 4);
+
+ payload = ci->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";
diff --git a/src/stats.c b/src/stats.c
index b592ced57..8a8eb3285 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -3524,14 +3524,17 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct proxy *px;
struct server *sv;
struct listener *li;
int clrall = 0;
- if (strcmp(args[2], "all") == 0)
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[2], "all") == 0)
clrall = 1;
/* check permissions */
@@ -3586,34 +3589,40 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
appctx->ctx.stats.scope_str = 0;
appctx->ctx.stats.scope_len = 0;
appctx->ctx.stats.flags = 0;
- if (strcmp(args[2], "typed") == 0)
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (strcmp(ci->args[2], "typed") == 0)
appctx->ctx.stats.flags |= STAT_FMT_TYPED;
- else if (strcmp(args[2], "json") == 0)
+ else if (strcmp(ci->args[2], "json") == 0)
appctx->ctx.stats.flags |= STAT_FMT_JSON;
return 0;
}
-static int cli_parse_show_stat(char **args, struct appctx *appctx, void *private)
+static int cli_parse_show_stat(struct cli_input *ci, struct appctx *appctx, void *private)
{
appctx->ctx.stats.scope_str = 0;
appctx->ctx.stats.scope_len = 0;
appctx->ctx.stats.flags = 0;
- if (*args[2] && *args[3] && *args[4]) {
+ cli_resync_args(ci, 6);
+ cli_split_args(ci);
+
+ if (*ci->args[2] && *ci->args[3] && *ci->args[4]) {
struct proxy *px;
- px = proxy_find_by_name(args[2], 0, 0);
+ px = proxy_find_by_name(ci->args[2], 0, 0);
if (px)
appctx->ctx.stats.iid = px->uuid;
else
- appctx->ctx.stats.iid = atoi(args[2]);
+ appctx->ctx.stats.iid = atoi(ci->args[2]);
if (!appctx->ctx.stats.iid) {
appctx->ctx.cli.severity = LOG_ERR;
@@ -3623,16 +3632,16 @@ static int cli_parse_show_stat(char **args, struct appctx *appctx, void *private
}
appctx->ctx.stats.flags |= STAT_BOUND;
- appctx->ctx.stats.type = atoi(args[3]);
- appctx->ctx.stats.sid = atoi(args[4]);
- if (strcmp(args[5], "typed") == 0)
+ appctx->ctx.stats.type = atoi(ci->args[3]);
+ appctx->ctx.stats.sid = atoi(ci->args[4]);
+ if (strcmp(ci->args[5], "typed") == 0)
appctx->ctx.stats.flags |= STAT_FMT_TYPED;
- else if (strcmp(args[5], "json") == 0)
+ else if (strcmp(ci->args[5], "json") == 0)
appctx->ctx.stats.flags |= STAT_FMT_JSON;
}
- else if (strcmp(args[2], "typed") == 0)
+ else if (strcmp(ci->args[2], "typed") == 0)
appctx->ctx.stats.flags |= STAT_FMT_TYPED;
- else if (strcmp(args[2], "json") == 0)
+ else if (strcmp(ci->args[2], "json") == 0)
appctx->ctx.stats.flags |= STAT_FMT_JSON;
return 0;
diff --git a/src/stick_table.c b/src/stick_table.c
index 73c70d3e2..75a832074 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
appctx->ctx.table.data_type = -1;
appctx->ctx.table.target = NULL;
@@ -3383,8 +3383,11 @@ static int cli_parse_table_req(char **args, struct appctx *appctx, void *private
appctx->ctx.table.entry = NULL;
appctx->ctx.table.action = (long)private; // keyword argument, one of STK_CLI_ACT_*
- if (*args[2]) {
- appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
+ cli_resync_args(ci, 5);
+ cli_split_args(ci);
+
+ if (*ci->args[2]) {
+ appctx->ctx.table.target = proxy_tbl_by_name(ci->args[2]);
if (!appctx->ctx.table.target) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "No such table\n";
@@ -3398,11 +3401,11 @@ static int cli_parse_table_req(char **args, struct appctx *appctx, void *private
return 0;
}
- if (strcmp(args[3], "key") == 0)
- return table_process_entry_per_key(appctx, args);
- else if (strncmp(args[3], "data.", 5) == 0)
- return table_prepare_data_request(appctx, args);
- else if (*args[3])
+ if (strcmp(ci->args[3], "key") == 0)
+ return table_process_entry_per_key(appctx, ci->args);
+ else if (strncmp(ci->args[3], "data.", 5) == 0)
+ return table_prepare_data_request(appctx, ci->args);
+ else if (*ci->args[3])
goto err_args;
return 0;
diff --git a/src/stream.c b/src/stream.c
index 2d8f27834..0b1adc9d3 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -3025,15 +3025,18 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
return 1;
- if (*args[2] && strcmp(args[2], "all") == 0)
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (*ci->args[2] && strcmp(ci->args[2], "all") == 0)
appctx->ctx.sess.target = (void *)-1;
- else if (*args[2])
- appctx->ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
+ else if (*ci->args[2])
+ appctx->ctx.sess.target = (void *)strtoul(ci->args[2], NULL, 0);
else
appctx->ctx.sess.target = NULL;
appctx->ctx.sess.section = 0; /* start with stream status */
@@ -3280,21 +3283,24 @@ 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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct stream *strm, *ptr;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- if (!*args[2]) {
+ cli_resync_args(ci, 3);
+ cli_split_args(ci);
+
+ if (!*ci->args[2]) {
appctx->ctx.cli.severity = LOG_ERR;
appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
appctx->st0 = CLI_ST_PRINT;
return 1;
}
- ptr = (void *)strtoul(args[2], NULL, 0);
+ ptr = (void *)strtoul(ci->args[2], NULL, 0);
/* first, look for the requested stream in the stream table */
list_for_each_entry(strm, &streams, list) {
@@ -3315,7 +3321,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(struct cli_input *ci, struct appctx *appctx, void *private)
{
struct server *sv;
struct stream *strm, *strm_bck;
@@ -3323,7 +3329,10 @@ static int cli_parse_shutdown_sessions_server(char **args, struct appctx *appctx
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
- sv = cli_find_server(appctx, args[3]);
+ cli_resync_args(ci, 4);
+ cli_split_args(ci);
+
+ sv = cli_find_server(appctx, ci->args[3]);
if (!sv)
return 1;
--
2.11.0