diff --git a/doc/management.txt b/doc/management.txt index 80755be03..bd3622aa0 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1770,6 +1770,12 @@ get acl type="": The type of the returned sample. +get var + Show the existence, type and contents of the process-wide variable 'name'. + Only process-wide variables are readable, so the name must begin with + 'proc.' otherwise no variable will be found. This command requires levels + "operator" or "admin". + get weight / Report the current weight and the initial weight of server in backend or an error if either doesn't exist. The initial weight is diff --git a/reg-tests/sample_fetches/vars.vtc b/reg-tests/sample_fetches/vars.vtc index 146106888..b57156ce9 100644 --- a/reg-tests/sample_fetches/vars.vtc +++ b/reg-tests/sample_fetches/vars.vtc @@ -26,6 +26,11 @@ haproxy h1 -conf { http-request return status 200 hdr x-var "proc=%[var(proc.int5)] sess=%[var(sess.int5)] req=%[var(req.int5)] str=%[var(proc.str)] uuid=%[var(proc.uuid)]" } -start +haproxy h1 -cli { + send "get var proc.int5" + expect ~ "^proc.int5: type=sint value=<5>$" +} + client c1 -connect ${h1_fe1_sock} { txreq -req GET -url /req1_1 rxresp @@ -38,6 +43,11 @@ client c1 -connect ${h1_fe1_sock} { expect resp.http.x-var ~ "proc=10 sess=20 req=10 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*" } -run +haproxy h1 -cli { + send "get var proc.int5" + expect ~ "^proc.int5: type=sint value=<10>$" +} + client c2 -connect ${h1_fe1_sock} { txreq -req GET -url /req2_1 rxresp @@ -49,3 +59,8 @@ client c2 -connect ${h1_fe1_sock} { expect resp.status == 200 expect resp.http.x-var ~ "proc=20 sess=40 req=20 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*" } -run + +haproxy h1 -cli { + send "get var proc.int5" + expect ~ "^proc.int5: type=sint value=<20>$" +} diff --git a/src/vars.c b/src/vars.c index 9be070b71..11efd55a4 100644 --- a/src/vars.c +++ b/src/vars.c @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -867,6 +869,49 @@ static int vars_parse_global_set_var(char **args, int section_type, struct proxy return ret; } +/* parse CLI's "get var " */ +static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private) +{ + struct vars *vars; + struct sample smp; + int i; + + if (!cli_has_level(appctx, ACCESS_LVL_OPER)) + return 1; + + if (!*args[2]) + return cli_err(appctx, "Missing process-wide variable identifier.\n"); + + vars = get_vars(NULL, NULL, SCOPE_PROC); + if (!vars || vars->scope != SCOPE_PROC) + return 0; + + if (!vars_get_by_name(args[2], strlen(args[2]), &smp)) + return cli_err(appctx, "Variable not found.\n"); + + /* the sample returned by vars_get_by_name() is allocated into a trash + * chunk so we have no constraint to manipulate it. + */ + chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]); + + if (!sample_casts[smp.data.type][SMP_T_STR] || + !sample_casts[smp.data.type][SMP_T_STR](&smp)) { + chunk_appendf(&trash, "(undisplayable)"); + } else { + /* Display the displayable chars*. */ + b_putchr(&trash, '<'); + for (i = 0; i < smp.data.u.str.data; i++) { + if (isprint((unsigned char)smp.data.u.str.area[i])) + b_putchr(&trash, smp.data.u.str.area[i]); + else + b_putchr(&trash, '.'); + } + b_putchr(&trash, '>'); + b_putchr(&trash, 0); + } + return cli_msg(appctx, LOG_INFO, trash.area); +} + static int vars_max_size(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err, unsigned int *limit) @@ -1016,3 +1061,11 @@ static struct cfg_kw_list cfg_kws = {{ },{ }}; INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); + + +/* register cli keywords */ +static struct cli_kw_list cli_kws = {{ },{ + { { "get", "var", NULL }, "get var : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL }, + { { NULL }, NULL, NULL, NULL } +}}; +INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);