From ae79572f89969a19f9f04063e0d4c97b0381f549 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 16 Feb 2016 11:27:28 +0100 Subject: [PATCH] MINOR: cli: add a new "show env" command Using environment variables in configuration files can make troubleshooting complicated because there's no easy way to verify that the variables are correct. This patch introduces a new "show env" command which displays the whole environment on the CLI, one variable per line. The socket must at least have level operator to display the environment. --- doc/management.txt | 10 ++++++ include/types/applet.h | 3 ++ src/dumpstats.c | 69 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/doc/management.txt b/doc/management.txt index 466f40e7b..1b677be9b 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1472,6 +1472,16 @@ set weight / [%] "admin". Both the backend and the server may be specified either by their name or by their numeric ID, prefixed with a sharp ('#'). +show env [] + Dump one or all environment variables known by the process. Without any + argument, all variables are dumped. With an argument, only the specified + variable is dumped if it exists. Otherwise "Variable not found" is emitted. + Variables are dumped in the same format as they are stored or returned by the + "env" utility, that is, "=". This can be handy when debugging + certain configuration files making heavy use of environment variables to + ensure that they contain the expected values. This command is restricted and + can only be issued on sockets configured for levels "operator" or "admin". + show errors [] Dump last known request and response errors collected by frontends and backends. If is specified, the limit the dump to errors concerning diff --git a/include/types/applet.h b/include/types/applet.h index 2e1626a79..562575f02 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -133,6 +133,9 @@ struct appctx { struct { struct proxy *backend; } server_state; + struct { + char **var; + } env; } ctx; /* used by stats I/O handlers to dump the stats */ }; diff --git a/src/dumpstats.c b/src/dumpstats.c index b868ef56e..8b55fe3a2 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -94,6 +94,7 @@ enum { STAT_CLI_O_RESOLVERS,/* dump a resolver's section nameservers counters */ STAT_CLI_O_SERVERS_STATE, /* dump server state and changing information */ STAT_CLI_O_BACKEND, /* dump backend list */ + STAT_CLI_O_ENV, /* dump environment */ }; /* Actions available for the stats admin forms */ @@ -130,6 +131,7 @@ enum { }; static int stats_dump_backend_to_buffer(struct stream_interface *si); +static int stats_dump_env_to_buffer(struct stream_interface *si); static int stats_dump_info_to_buffer(struct stream_interface *si); static int stats_dump_servers_state_to_buffer(struct stream_interface *si); static int stats_dump_pools_to_buffer(struct stream_interface *si); @@ -191,6 +193,7 @@ static const char stats_sock_usage_msg[] = " prompt : toggle interactive mode with prompt\n" " quit : disconnect\n" " show backend : list backends in the current running config\n" + " show env [var] : dump environment variables known to the process\n" " show info : report information about the running process\n" " show pools : report information about the memory pools usage\n" " show stat : report counters for each proxy and server\n" @@ -1162,7 +1165,35 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) appctx->st2 = STAT_ST_INIT; appctx->st0 = STAT_CLI_O_BACKEND; } - else if (strcmp(args[1], "stat") == 0) { + else if (strcmp(args[1], "env") == 0) { + extern char **environ; + + if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) { + appctx->ctx.cli.msg = stats_permission_denied_msg; + appctx->st0 = STAT_CLI_PRINT; + return 1; + } + appctx->ctx.env.var = environ; + appctx->st2 = STAT_ST_INIT; + appctx->st0 = STAT_CLI_O_ENV; // stats_dump_env_to_buffer + + if (*args[2]) { + int len = strlen(args[2]); + + for (; *appctx->ctx.env.var; appctx->ctx.env.var++) { + if (strncmp(*appctx->ctx.env.var, args[2], len) == 0 && + (*appctx->ctx.env.var)[len] == '=') + break; + } + if (!*appctx->ctx.env.var) { + appctx->ctx.cli.msg = "Variable not found\n"; + appctx->st0 = STAT_CLI_PRINT; + return 1; + } + appctx->st2 = STAT_ST_END; + } + } + else if (strcmp(args[1], "stat") == 0) { if (strcmp(args[2], "resolvers") == 0) { struct dns_resolvers *presolvers; @@ -2589,6 +2620,10 @@ static void cli_io_handler(struct appctx *appctx) appctx->st0 = STAT_CLI_PROMPT; break; #endif + case STAT_CLI_O_ENV: /* environment dump */ + if (stats_dump_env_to_buffer(si)) + appctx->st0 = STAT_CLI_PROMPT; + break; default: /* abnormal state */ si->flags |= SI_FL_ERR; break; @@ -6655,6 +6690,38 @@ static int stats_dump_errors_to_buffer(struct stream_interface *si) return 1; } +/* This function dumps all environmnent variables to the buffer. It returns 0 + * if the output buffer is full and it needs to be called again, otherwise + * non-zero. Dumps only one entry if st2 == STAT_ST_END. + */ +static int stats_dump_env_to_buffer(struct stream_interface *si) +{ + struct appctx *appctx = __objt_appctx(si->end); + + if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) + return 1; + + chunk_reset(&trash); + + /* we have two inner loops here, one for the proxy, the other one for + * the buffer. + */ + while (*appctx->ctx.env.var) { + chunk_printf(&trash, "%s\n", *appctx->ctx.env.var); + + if (bi_putchk(si_ic(si), &trash) == -1) { + si_applet_cant_put(si); + return 0; + } + if (appctx->st2 == STAT_ST_END) + break; + appctx->ctx.env.var++; + } + + /* dump complete */ + return 1; +} + /* parse the "level" argument on the bind lines */ static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) {