diff --git a/doc/configuration.txt b/doc/configuration.txt index b55bb0628..c3aa6fa84 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -10554,6 +10554,18 @@ level <level> - "admin" should be used with care, as everything is permitted (eg: clear all counters). +severity-output <format> + This setting is used with the stats sockets only to configure severity + level output prepended to informational feedback messages. Severity + level of messages can range between 0 and 7, conforming to syslog + rfc5424. Valid and successful socket commands requesting data + (i.e. "show map", "get acl foo" etc.) will never have a severity level + prepended. It is ignored by other sockets. <format> can be one of : + - "none" (default) no severity level is prepended to feedback messages. + - "number" severity level is prepended as a number. + - "string" severity level is prepended as a string following the + rfc5424 convention. + maxconn <maxconn> Limits the sockets to this number of concurrent connections. Extraneous connections will remain in the system's backlog until a connection is diff --git a/doc/management.txt b/doc/management.txt index 27f86dbc8..0df2b116a 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1672,6 +1672,10 @@ set server <backend>/<server> weight <weight>[%] set server <backend>/<server> fqdn <FQDN> Change a server's FQDN to the value passed in argument. +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> 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 diff --git a/include/proto/listener.h b/include/proto/listener.h index 69a1f4bfd..0ad0beb8c 100644 --- a/include/proto/listener.h +++ b/include/proto/listener.h @@ -25,6 +25,7 @@ #include <string.h> #include <types/listener.h> +#include <types/cli.h> /* This function tries to temporarily disable a listener, depending on the OS * capabilities. Linux unbinds the listen socket after a SHUT_RD, and ignores @@ -122,6 +123,7 @@ static inline struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *fi bind_conf->ux.mode = 0; bind_conf->xprt = xprt; bind_conf->frontend = fe; + bind_conf->severity_output = CLI_SEVERITY_NONE; LIST_INIT(&bind_conf->listeners); return bind_conf; diff --git a/include/types/applet.h b/include/types/applet.h index 347c71a09..e96b703c8 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -65,6 +65,7 @@ struct appctx { int (*io_handler)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK */ void (*io_release)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK, if the command is terminated or the session released */ + int cli_severity_output; /* used within the cli_io_handler to format severity output of informational feedback */ struct buffer_wait buffer_wait; /* position in the list of objects waiting for a buffer */ unsigned long process_mask; /* mask of thread IDs authorized to process the applet */ @@ -97,6 +98,7 @@ struct appctx { } spoe; /* used by SPOE filter */ struct { const char *msg; /* pointer to a persistent message to be returned in CLI_ST_PRINT state */ + int severity; /* severity of the message to be returned according to (syslog) rfc5424 */ char *err; /* pointer to a 'must free' message to be returned in CLI_ST_PRINT_FREE state */ void *p0, *p1; /* general purpose pointers and integers for registered commands, initialized */ int i0, i1; /* to 0 by the CLI before first invocation of the keyword parser. */ diff --git a/include/types/cli.h b/include/types/cli.h index 80da45db4..63e0e9d05 100644 --- a/include/types/cli.h +++ b/include/types/cli.h @@ -50,5 +50,13 @@ enum { CLI_ST_CALLBACK, /* custom callback pointer */ }; +/* CLI severity output formats */ +enum { + CLI_SEVERITY_UNDEFINED = 0, /* undefined severity format */ + CLI_SEVERITY_NONE, /* no severity information prepended */ + CLI_SEVERITY_NUMBER, /* prepend informational cli messages with a severity as number */ + CLI_SEVERITY_STRING, /* prepend informational cli messages with a severity as string */ +}; + #endif /* _TYPES_CLI_H */ diff --git a/include/types/listener.h b/include/types/listener.h index f04a8eac9..3d9ad7f7b 100644 --- a/include/types/listener.h +++ b/include/types/listener.h @@ -161,6 +161,7 @@ struct bind_conf { mode_t mode; /* 0 to leave unchanged */ } ux; int level; /* stats access level (ACCESS_LVL_*) */ + int severity_output; /* default severity output format in cli feedback messages */ struct list by_fe; /* next binding for the same frontend, or NULL */ struct list listeners; /* list of listeners using this bind config */ uint32_t ns_cip_magic; /* Excepted NetScaler Client IP magic number */ diff --git a/src/cli.c b/src/cli.c index 47020ffbf..266bade8a 100644 --- a/src/cli.c +++ b/src/cli.c @@ -392,6 +392,13 @@ int cli_has_level(struct appctx *appctx, int level) return 1; } +/* Returns severity_output for the current session if set, or default for the socket */ +static int cli_get_severity_output(struct appctx *appctx) +{ + if (appctx->cli_severity_output) + return appctx->cli_severity_output; + return strm_li(si_strm(appctx->owner))->bind_conf->severity_output; +} /* 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 @@ -472,6 +479,38 @@ static int cli_parse_request(struct appctx *appctx, char *line) return 1; } +/* prepends then outputs the argument msg with a syslog-type severity depending on severity_output value */ +static int cli_output_msg(struct channel *chn, const char *msg, int severity, int severity_output) +{ + struct chunk *tmp; + + if (likely(severity_output == CLI_SEVERITY_NONE)) + return bi_putblk(chn, msg, strlen(msg)); + + tmp = get_trash_chunk(); + chunk_reset(tmp); + + if (severity < 0 || severity > 7) { + Warning("socket command feedback with invalid severity %d", severity); + chunk_printf(tmp, "[%d]: ", severity); + } + else { + switch (severity_output) { + case CLI_SEVERITY_NUMBER: + chunk_printf(tmp, "[%d]: ", severity); + break; + case CLI_SEVERITY_STRING: + chunk_printf(tmp, "[%s]: ", log_levels[severity]); + break; + default: + Warning("Unrecognized severity output %d", severity_output); + } + } + chunk_appendf(tmp, "%s", msg); + + return bi_putblk(chn, tmp->str, strlen(tmp->str)); +} + /* This I/O handler runs as an applet embedded in a stream interface. It is * used to processes I/O from/to the stats unix socket. The system relies on a * state machine handling requests and various responses. We read a request, @@ -485,6 +524,7 @@ static void cli_io_handler(struct appctx *appctx) struct stream_interface *si = appctx->owner; struct channel *req = si_oc(si); struct channel *res = si_ic(si); + struct bind_conf *bind_conf = strm_li(si_strm(si))->bind_conf; int reql; int len; @@ -501,6 +541,8 @@ static void cli_io_handler(struct appctx *appctx) if (appctx->st0 == CLI_ST_INIT) { /* Stats output not initialized yet */ memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats)); + /* reset severity to default at init */ + appctx->cli_severity_output = bind_conf->severity_output; appctx->st0 = CLI_ST_GETREQ; } else if (appctx->st0 == CLI_ST_END) { @@ -600,13 +642,14 @@ static void cli_io_handler(struct appctx *appctx) case CLI_ST_PROMPT: break; case CLI_ST_PRINT: - if (bi_putstr(si_ic(si), appctx->ctx.cli.msg) != -1) + if (cli_output_msg(res, appctx->ctx.cli.msg, appctx->ctx.cli.severity, + cli_get_severity_output(appctx)) != -1) appctx->st0 = CLI_ST_PROMPT; else si_applet_cant_put(si); break; case CLI_ST_PRINT_FREE: - if (bi_putstr(si_ic(si), appctx->ctx.cli.err) != -1) { + if (cli_output_msg(res, appctx->ctx.cli.err, LOG_ERR, cli_get_severity_output(appctx)) != -1) { free(appctx->ctx.cli.err); appctx->st0 = CLI_ST_PROMPT; } @@ -1049,6 +1092,34 @@ static int cli_parse_set_maxconn_global(char **args, struct appctx *appctx, void return 1; } +static int set_severity_output(int *target, char *argument) +{ + if (!strcmp(argument, "none")) { + *target = CLI_SEVERITY_NONE; + return 1; + } + else if (!strcmp(argument, "number")) { + *target = CLI_SEVERITY_NUMBER; + return 1; + } + else if (!strcmp(argument, "string")) { + *target = CLI_SEVERITY_STRING; + return 1; + } + return 0; +} + +/* parse a "set severity-output" command. */ +static int cli_parse_set_severity_output(char **args, struct appctx *appctx, void *private) +{ + if (*args[2] && set_severity_output(&appctx->cli_severity_output, args[2])) + return 0; + + appctx->ctx.cli.severity = LOG_ERR; + appctx->ctx.cli.msg = "one of 'none', 'number', 'string' is a required argument"; + appctx->st0 = CLI_ST_PRINT; + return 1; +} int cli_parse_default(char **args, struct appctx *appctx, void *private) { @@ -1156,6 +1227,22 @@ static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct b return 0; } +static int bind_parse_severity_output(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) +{ + if (!*args[cur_arg + 1]) { + memprintf(err, "'%s' : missing severity format", args[cur_arg]); + return ERR_ALERT | ERR_FATAL; + } + + if (set_severity_output(&conf->severity_output, args[cur_arg+1])) + return 0; + else { + memprintf(err, "'%s' only supports 'none', 'number', and 'string' (got '%s')", + args[cur_arg], args[cur_arg+1]); + return ERR_ALERT | ERR_FATAL; + } +} + /* Send all the bound sockets, always returns 1 */ static int _getsocks(char **args, struct appctx *appctx, void *private) { @@ -1349,6 +1436,7 @@ static struct applet cli_applet = { static struct cli_kw_list cli_kws = {{ },{ { { "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 }, { { "set", "timeout", NULL }, "set timeout : change a timeout setting", cli_parse_set_timeout, NULL, NULL }, { { "show", "env", NULL }, "show env [var] : dump environment variables known to the process", cli_parse_show_env, cli_io_handler_show_env, NULL }, { { "show", "cli", "sockets", NULL }, "show cli sockets : dump list of cli sockets", cli_parse_default, cli_io_handler_show_cli_sock, NULL }, @@ -1365,6 +1453,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { static struct bind_kw_list bind_kws = { "STAT", { }, { { "level", bind_parse_level, 1 }, /* set the unix socket admin level */ { "expose-fd", bind_parse_expose_fd, 1 }, /* set the unix socket expose fd rights */ + { "severity-output", bind_parse_severity_output, 1 }, /* set the severity output format */ { NULL, NULL, 0 }, }};