mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-31 02:22:07 +00:00
REORG: cli: move table dump/clear/set to stick_table.c
The table dump code was a horrible mess, with common parts interleaved all the way to deal with the various actions (set/clear/show). A few error messages were still incorrect, as the "set" operation did not update them so they would still report "unknown action" (now fixed). The action was now passed as a private argument to the CLI keyword which itself is copied into the appctx private field. It's just an int cast to a pointer. Some minor issues were noticed while doing this, for example when dumping an entry by key, if the key doesn't exist, nothing is printed, not even the table's header. It's unclear whether this was intentional but it doesn't really match what is done for data-based dumps. It was left unchanged for now so that a later fix can be backported if needed. Enum entries STAT_CLI_O_TAB, STAT_CLI_O_CLR and STAT_CLI_O_SET were removed.
This commit is contained in:
parent
97c2ae13bc
commit
f13ebdf286
@ -48,9 +48,6 @@ enum {
|
||||
STAT_CLI_PRINT, /* display message in cli->msg */
|
||||
STAT_CLI_PRINT_FREE, /* display message in cli->msg. After the display, free the pointer */
|
||||
STAT_CLI_O_ERR, /* dump errors */
|
||||
STAT_CLI_O_TAB, /* dump tables */
|
||||
STAT_CLI_O_CLR, /* clear tables */
|
||||
STAT_CLI_O_SET, /* set entries in tables */
|
||||
STAT_CLI_O_ENV, /* dump environment */
|
||||
STAT_CLI_O_CUSTOM, /* custom callback pointer */
|
||||
};
|
||||
|
544
src/cli.c
544
src/cli.c
@ -69,22 +69,17 @@
|
||||
|
||||
static int stats_dump_env_to_buffer(struct stream_interface *si);
|
||||
static int stats_dump_errors_to_buffer(struct stream_interface *si);
|
||||
static int stats_table_request(struct stream_interface *si, int show);
|
||||
|
||||
|
||||
static struct applet cli_applet;
|
||||
|
||||
static const char stats_sock_usage_msg[] =
|
||||
"Unknown command. Please enter one of the following commands only :\n"
|
||||
" clear counters : clear max statistics counters (add 'all' for all counters)\n"
|
||||
" clear table : remove an entry from a table\n"
|
||||
" help : this message\n"
|
||||
" prompt : toggle interactive mode with prompt\n"
|
||||
" quit : disconnect\n"
|
||||
" show env [var] : dump environment variables known to the process\n"
|
||||
" show errors : report last request and response errors for each proxy\n"
|
||||
" show table [id]: report table usage stats or dump this table's contents\n"
|
||||
" set table [id] : update or create a table entry's data\n"
|
||||
" set timeout : change a timeout setting\n"
|
||||
" set maxconn : change a maxconn setting\n"
|
||||
" set rate-limit : change a rate limiting value\n"
|
||||
@ -409,365 +404,6 @@ int cli_has_level(struct appctx *appctx, int level)
|
||||
}
|
||||
|
||||
|
||||
/* Dump the status of a table to a stream interface's
|
||||
* read buffer. It returns 0 if the output buffer is full
|
||||
* and needs to be called again, otherwise non-zero.
|
||||
*/
|
||||
static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
|
||||
struct proxy *proxy, struct proxy *target)
|
||||
{
|
||||
struct stream *s = si_strm(si);
|
||||
|
||||
chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
|
||||
proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
|
||||
|
||||
/* any other information should be dumped here */
|
||||
|
||||
if (target && strm_li(s)->bind_conf->level < ACCESS_LVL_OPER)
|
||||
chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
|
||||
|
||||
if (bi_putchk(si_ic(si), msg) == -1) {
|
||||
si_applet_cant_put(si);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Dump the a table entry to a stream interface's
|
||||
* read buffer. It returns 0 if the output buffer is full
|
||||
* and needs to be called again, otherwise non-zero.
|
||||
*/
|
||||
static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
|
||||
struct proxy *proxy, struct stksess *entry)
|
||||
{
|
||||
int dt;
|
||||
|
||||
chunk_appendf(msg, "%p:", entry);
|
||||
|
||||
if (proxy->table.type == SMP_T_IPV4) {
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
|
||||
chunk_appendf(msg, " key=%s", addr);
|
||||
}
|
||||
else if (proxy->table.type == SMP_T_IPV6) {
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
|
||||
chunk_appendf(msg, " key=%s", addr);
|
||||
}
|
||||
else if (proxy->table.type == SMP_T_SINT) {
|
||||
chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
|
||||
}
|
||||
else if (proxy->table.type == SMP_T_STR) {
|
||||
chunk_appendf(msg, " key=");
|
||||
dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
|
||||
}
|
||||
else {
|
||||
chunk_appendf(msg, " key=");
|
||||
dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
|
||||
}
|
||||
|
||||
chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
|
||||
|
||||
for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
|
||||
void *ptr;
|
||||
|
||||
if (proxy->table.data_ofs[dt] == 0)
|
||||
continue;
|
||||
if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
|
||||
chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
|
||||
else
|
||||
chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
|
||||
|
||||
ptr = stktable_data_ptr(&proxy->table, entry, dt);
|
||||
switch (stktable_data_types[dt].std_type) {
|
||||
case STD_T_SINT:
|
||||
chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
|
||||
break;
|
||||
case STD_T_UINT:
|
||||
chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
|
||||
break;
|
||||
case STD_T_ULL:
|
||||
chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
|
||||
break;
|
||||
case STD_T_FRQP:
|
||||
chunk_appendf(msg, "%d",
|
||||
read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
|
||||
proxy->table.data_arg[dt].u));
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunk_appendf(msg, "\n");
|
||||
|
||||
if (bi_putchk(si_ic(si), msg) == -1) {
|
||||
si_applet_cant_put(si);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
|
||||
{
|
||||
struct stream *s = si_strm(si);
|
||||
struct appctx *appctx = __objt_appctx(si->end);
|
||||
struct proxy *px = appctx->ctx.table.target;
|
||||
struct stksess *ts;
|
||||
uint32_t uint32_key;
|
||||
unsigned char ip6_key[sizeof(struct in6_addr)];
|
||||
long long value;
|
||||
int data_type;
|
||||
int cur_arg;
|
||||
void *ptr;
|
||||
struct freq_ctr_period *frqp;
|
||||
|
||||
appctx->st0 = STAT_CLI_OUTPUT;
|
||||
|
||||
if (!*args[4]) {
|
||||
appctx->ctx.cli.msg = "Key value expected\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (px->table.type) {
|
||||
case SMP_T_IPV4:
|
||||
uint32_key = htonl(inetaddr_host(args[4]));
|
||||
static_table_key->key = &uint32_key;
|
||||
break;
|
||||
case SMP_T_IPV6:
|
||||
inet_pton(AF_INET6, args[4], ip6_key);
|
||||
static_table_key->key = &ip6_key;
|
||||
break;
|
||||
case SMP_T_SINT:
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long val;
|
||||
errno = 0;
|
||||
val = strtoul(args[4], &endptr, 10);
|
||||
if ((errno == ERANGE && val == ULONG_MAX) ||
|
||||
(errno != 0 && val == 0) || endptr == args[4] ||
|
||||
val > 0xffffffff) {
|
||||
appctx->ctx.cli.msg = "Invalid key\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
uint32_key = (uint32_t) val;
|
||||
static_table_key->key = &uint32_key;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SMP_T_STR:
|
||||
static_table_key->key = args[4];
|
||||
static_table_key->key_len = strlen(args[4]);
|
||||
break;
|
||||
default:
|
||||
switch (action) {
|
||||
case STAT_CLI_O_TAB:
|
||||
appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
|
||||
break;
|
||||
case STAT_CLI_O_CLR:
|
||||
appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
|
||||
break;
|
||||
default:
|
||||
appctx->ctx.cli.msg = "Unknown action\n";
|
||||
break;
|
||||
}
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
/* check permissions */
|
||||
if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
|
||||
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
ts = stktable_lookup_key(&px->table, static_table_key);
|
||||
|
||||
switch (action) {
|
||||
case STAT_CLI_O_TAB:
|
||||
if (!ts)
|
||||
return;
|
||||
chunk_reset(&trash);
|
||||
if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
|
||||
return;
|
||||
stats_dump_table_entry_to_buffer(&trash, si, px, ts);
|
||||
return;
|
||||
|
||||
case STAT_CLI_O_CLR:
|
||||
if (!ts)
|
||||
return;
|
||||
if (ts->ref_cnt) {
|
||||
/* don't delete an entry which is currently referenced */
|
||||
appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
stksess_kill(&px->table, ts);
|
||||
break;
|
||||
|
||||
case STAT_CLI_O_SET:
|
||||
if (ts)
|
||||
stktable_touch(&px->table, ts, 1);
|
||||
else {
|
||||
ts = stksess_new(&px->table, static_table_key);
|
||||
if (!ts) {
|
||||
/* don't delete an entry which is currently referenced */
|
||||
appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
stktable_store(&px->table, ts, 1);
|
||||
}
|
||||
|
||||
for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
|
||||
if (strncmp(args[cur_arg], "data.", 5) != 0) {
|
||||
appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
data_type = stktable_get_data_type(args[cur_arg] + 5);
|
||||
if (data_type < 0) {
|
||||
appctx->ctx.cli.msg = "Unknown data type\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!px->table.data_ofs[data_type]) {
|
||||
appctx->ctx.cli.msg = "Data type not stored in this table\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
|
||||
appctx->ctx.cli.msg = "Require a valid integer value to store\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = stktable_data_ptr(&px->table, ts, data_type);
|
||||
|
||||
switch (stktable_data_types[data_type].std_type) {
|
||||
case STD_T_SINT:
|
||||
stktable_data_cast(ptr, std_t_sint) = value;
|
||||
break;
|
||||
case STD_T_UINT:
|
||||
stktable_data_cast(ptr, std_t_uint) = value;
|
||||
break;
|
||||
case STD_T_ULL:
|
||||
stktable_data_cast(ptr, std_t_ull) = value;
|
||||
break;
|
||||
case STD_T_FRQP:
|
||||
/* We set both the current and previous values. That way
|
||||
* the reported frequency is stable during all the period
|
||||
* then slowly fades out. This allows external tools to
|
||||
* push measures without having to update them too often.
|
||||
*/
|
||||
frqp = &stktable_data_cast(ptr, std_t_frqp);
|
||||
frqp->curr_tick = now_ms;
|
||||
frqp->prev_ctr = 0;
|
||||
frqp->curr_ctr = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
appctx->ctx.cli.msg = "Unknown action\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
|
||||
{
|
||||
struct appctx *appctx = __objt_appctx(si->end);
|
||||
|
||||
if (action != STAT_CLI_O_TAB && action != STAT_CLI_O_CLR) {
|
||||
appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
/* condition on stored data value */
|
||||
appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
|
||||
if (appctx->ctx.table.data_type < 0) {
|
||||
appctx->ctx.cli.msg = "Unknown data type\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
|
||||
appctx->ctx.cli.msg = "Data type not stored in this table\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
appctx->ctx.table.data_op = get_std_op(args[4]);
|
||||
if (appctx->ctx.table.data_op < 0) {
|
||||
appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
|
||||
appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
|
||||
{
|
||||
struct appctx *appctx = __objt_appctx(si->end);
|
||||
|
||||
appctx->ctx.table.data_type = -1;
|
||||
appctx->st2 = STAT_ST_INIT;
|
||||
appctx->ctx.table.target = NULL;
|
||||
appctx->ctx.table.proxy = NULL;
|
||||
appctx->ctx.table.entry = NULL;
|
||||
appctx->st0 = action;
|
||||
|
||||
if (*args[2]) {
|
||||
appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
|
||||
if (!appctx->ctx.table.target) {
|
||||
appctx->ctx.cli.msg = "No such table\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (action != STAT_CLI_O_TAB)
|
||||
goto err_args;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(args[3], "key") == 0)
|
||||
stats_sock_table_key_request(si, args, action);
|
||||
else if (strncmp(args[3], "data.", 5) == 0)
|
||||
stats_sock_table_data_request(si, args, action);
|
||||
else if (*args[3])
|
||||
goto err_args;
|
||||
|
||||
return;
|
||||
|
||||
err_args:
|
||||
switch (action) {
|
||||
case STAT_CLI_O_TAB:
|
||||
appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
|
||||
break;
|
||||
case STAT_CLI_O_CLR:
|
||||
appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
|
||||
break;
|
||||
default:
|
||||
appctx->ctx.cli.msg = "Unknown action\n";
|
||||
break;
|
||||
}
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
}
|
||||
|
||||
/* Expects to find a frontend named <arg> and returns it, otherwise displays various
|
||||
* adequate error messages and returns NULL. This function also expects the stream
|
||||
* level to be admin.
|
||||
@ -955,9 +591,6 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
||||
appctx->st2 = STAT_ST_INIT;
|
||||
appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
|
||||
}
|
||||
else if (strcmp(args[1], "table") == 0) {
|
||||
stats_sock_table_request(si, args, STAT_CLI_O_TAB);
|
||||
}
|
||||
else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
|
||||
return 0;
|
||||
}
|
||||
@ -1021,11 +654,6 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
||||
global.sps_max = 0;
|
||||
return 1;
|
||||
}
|
||||
else if (strcmp(args[1], "table") == 0) {
|
||||
stats_sock_table_request(si, args, STAT_CLI_O_CLR);
|
||||
/* end of processing */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
/* unknown "clear" argument */
|
||||
return 0;
|
||||
@ -1298,9 +926,6 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[1], "table") == 0) {
|
||||
stats_sock_table_request(si, args, STAT_CLI_O_SET);
|
||||
} else { /* unknown "set" parameter */
|
||||
return 0;
|
||||
}
|
||||
@ -1670,11 +1295,6 @@ static void cli_io_handler(struct appctx *appctx)
|
||||
if (stats_dump_errors_to_buffer(si))
|
||||
appctx->st0 = STAT_CLI_PROMPT;
|
||||
break;
|
||||
case STAT_CLI_O_TAB:
|
||||
case STAT_CLI_O_CLR:
|
||||
if (stats_table_request(si, appctx->st0))
|
||||
appctx->st0 = STAT_CLI_PROMPT;
|
||||
break;
|
||||
case STAT_CLI_O_ENV: /* environment dump */
|
||||
if (stats_dump_env_to_buffer(si))
|
||||
appctx->st0 = STAT_CLI_PROMPT;
|
||||
@ -1759,176 +1379,12 @@ static void cli_release_handler(struct appctx *appctx)
|
||||
appctx->io_release(appctx);
|
||||
appctx->io_release = NULL;
|
||||
}
|
||||
else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) &&
|
||||
appctx->st2 == STAT_ST_LIST) {
|
||||
appctx->ctx.table.entry->ref_cnt--;
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
}
|
||||
else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
|
||||
free(appctx->ctx.cli.err);
|
||||
appctx->ctx.cli.err = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is used to either dump tables states (when action is set
|
||||
* to STAT_CLI_O_TAB) or clear tables (when action is STAT_CLI_O_CLR).
|
||||
* It returns 0 if the output buffer is full and it needs to be called
|
||||
* again, otherwise non-zero.
|
||||
*/
|
||||
static int stats_table_request(struct stream_interface *si, int action)
|
||||
{
|
||||
struct appctx *appctx = __objt_appctx(si->end);
|
||||
struct stream *s = si_strm(si);
|
||||
struct ebmb_node *eb;
|
||||
int dt;
|
||||
int skip_entry;
|
||||
int show = action == STAT_CLI_O_TAB;
|
||||
|
||||
/*
|
||||
* We have 3 possible states in appctx->st2 :
|
||||
* - STAT_ST_INIT : the first call
|
||||
* - STAT_ST_INFO : the proxy pointer points to the next table to
|
||||
* dump, the entry pointer is NULL ;
|
||||
* - STAT_ST_LIST : the proxy pointer points to the current table
|
||||
* and the entry pointer points to the next entry to be dumped,
|
||||
* and the refcount on the next entry is held ;
|
||||
* - STAT_ST_END : nothing left to dump, the buffer may contain some
|
||||
* data though.
|
||||
*/
|
||||
|
||||
if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
|
||||
/* in case of abort, remove any refcount we might have set on an entry */
|
||||
if (appctx->st2 == STAT_ST_LIST) {
|
||||
appctx->ctx.table.entry->ref_cnt--;
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
chunk_reset(&trash);
|
||||
|
||||
while (appctx->st2 != STAT_ST_FIN) {
|
||||
switch (appctx->st2) {
|
||||
case STAT_ST_INIT:
|
||||
appctx->ctx.table.proxy = appctx->ctx.table.target;
|
||||
if (!appctx->ctx.table.proxy)
|
||||
appctx->ctx.table.proxy = proxy;
|
||||
|
||||
appctx->ctx.table.entry = NULL;
|
||||
appctx->st2 = STAT_ST_INFO;
|
||||
break;
|
||||
|
||||
case STAT_ST_INFO:
|
||||
if (!appctx->ctx.table.proxy ||
|
||||
(appctx->ctx.table.target &&
|
||||
appctx->ctx.table.proxy != appctx->ctx.table.target)) {
|
||||
appctx->st2 = STAT_ST_END;
|
||||
break;
|
||||
}
|
||||
|
||||
if (appctx->ctx.table.proxy->table.size) {
|
||||
if (show && !stats_dump_table_head_to_buffer(&trash, si, appctx->ctx.table.proxy,
|
||||
appctx->ctx.table.target))
|
||||
return 0;
|
||||
|
||||
if (appctx->ctx.table.target &&
|
||||
strm_li(s)->bind_conf->level >= ACCESS_LVL_OPER) {
|
||||
/* dump entries only if table explicitly requested */
|
||||
eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
|
||||
if (eb) {
|
||||
appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
||||
appctx->ctx.table.entry->ref_cnt++;
|
||||
appctx->st2 = STAT_ST_LIST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
|
||||
break;
|
||||
|
||||
case STAT_ST_LIST:
|
||||
skip_entry = 0;
|
||||
|
||||
if (appctx->ctx.table.data_type >= 0) {
|
||||
/* we're filtering on some data contents */
|
||||
void *ptr;
|
||||
long long data;
|
||||
|
||||
dt = appctx->ctx.table.data_type;
|
||||
ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
|
||||
appctx->ctx.table.entry,
|
||||
dt);
|
||||
|
||||
data = 0;
|
||||
switch (stktable_data_types[dt].std_type) {
|
||||
case STD_T_SINT:
|
||||
data = stktable_data_cast(ptr, std_t_sint);
|
||||
break;
|
||||
case STD_T_UINT:
|
||||
data = stktable_data_cast(ptr, std_t_uint);
|
||||
break;
|
||||
case STD_T_ULL:
|
||||
data = stktable_data_cast(ptr, std_t_ull);
|
||||
break;
|
||||
case STD_T_FRQP:
|
||||
data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
|
||||
appctx->ctx.table.proxy->table.data_arg[dt].u);
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip the entry if the data does not match the test and the value */
|
||||
if ((data < appctx->ctx.table.value &&
|
||||
(appctx->ctx.table.data_op == STD_OP_EQ ||
|
||||
appctx->ctx.table.data_op == STD_OP_GT ||
|
||||
appctx->ctx.table.data_op == STD_OP_GE)) ||
|
||||
(data == appctx->ctx.table.value &&
|
||||
(appctx->ctx.table.data_op == STD_OP_NE ||
|
||||
appctx->ctx.table.data_op == STD_OP_GT ||
|
||||
appctx->ctx.table.data_op == STD_OP_LT)) ||
|
||||
(data > appctx->ctx.table.value &&
|
||||
(appctx->ctx.table.data_op == STD_OP_EQ ||
|
||||
appctx->ctx.table.data_op == STD_OP_LT ||
|
||||
appctx->ctx.table.data_op == STD_OP_LE)))
|
||||
skip_entry = 1;
|
||||
}
|
||||
|
||||
if (show && !skip_entry &&
|
||||
!stats_dump_table_entry_to_buffer(&trash, si, appctx->ctx.table.proxy,
|
||||
appctx->ctx.table.entry))
|
||||
return 0;
|
||||
|
||||
appctx->ctx.table.entry->ref_cnt--;
|
||||
|
||||
eb = ebmb_next(&appctx->ctx.table.entry->key);
|
||||
if (eb) {
|
||||
struct stksess *old = appctx->ctx.table.entry;
|
||||
appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
||||
if (show)
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
|
||||
else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
|
||||
stksess_kill(&appctx->ctx.table.proxy->table, old);
|
||||
appctx->ctx.table.entry->ref_cnt++;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (show)
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
|
||||
stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
|
||||
appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
|
||||
appctx->st2 = STAT_ST_INFO;
|
||||
break;
|
||||
|
||||
case STAT_ST_END:
|
||||
appctx->st2 = STAT_ST_FIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This function dumps all captured errors onto the stream interface's
|
||||
* read buffer. It returns 0 if the output buffer is full and it needs
|
||||
* to be called again, otherwise non-zero.
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
@ -22,12 +23,17 @@
|
||||
#include <ebmbtree.h>
|
||||
#include <ebsttree.h>
|
||||
|
||||
#include <types/cli.h>
|
||||
#include <types/stats.h>
|
||||
|
||||
#include <proto/arg.h>
|
||||
#include <proto/cli.h>
|
||||
#include <proto/proto_http.h>
|
||||
#include <proto/proto_tcp.h>
|
||||
#include <proto/proxy.h>
|
||||
#include <proto/sample.h>
|
||||
#include <proto/stream.h>
|
||||
#include <proto/stream_interface.h>
|
||||
#include <proto/stick_table.h>
|
||||
#include <proto/task.h>
|
||||
#include <proto/peers.h>
|
||||
@ -1514,6 +1520,568 @@ static enum act_parse_ret parse_set_gpt0(const char **args, int *arg, struct pro
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
|
||||
/* The functions below are used to manipulate table contents from the CLI.
|
||||
* There are 3 main actions, "clear", "set" and "show". The code is shared
|
||||
* between all actions, and the action is encoded in the void *private in
|
||||
* the appctx as well as in the keyword registration, among one of the
|
||||
* following values.
|
||||
*/
|
||||
|
||||
enum {
|
||||
STK_CLI_ACT_CLR,
|
||||
STK_CLI_ACT_SET,
|
||||
STK_CLI_ACT_SHOW,
|
||||
};
|
||||
|
||||
/* Dump the status of a table to a stream interface's
|
||||
* read buffer. It returns 0 if the output buffer is full
|
||||
* and needs to be called again, otherwise non-zero.
|
||||
*/
|
||||
static int table_dump_head_to_buffer(struct chunk *msg, struct stream_interface *si,
|
||||
struct proxy *proxy, struct proxy *target)
|
||||
{
|
||||
struct stream *s = si_strm(si);
|
||||
|
||||
chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
|
||||
proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
|
||||
|
||||
/* any other information should be dumped here */
|
||||
|
||||
if (target && strm_li(s)->bind_conf->level < ACCESS_LVL_OPER)
|
||||
chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
|
||||
|
||||
if (bi_putchk(si_ic(si), msg) == -1) {
|
||||
si_applet_cant_put(si);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Dump a table entry to a stream interface's
|
||||
* read buffer. It returns 0 if the output buffer is full
|
||||
* and needs to be called again, otherwise non-zero.
|
||||
*/
|
||||
static int table_dump_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
|
||||
struct proxy *proxy, struct stksess *entry)
|
||||
{
|
||||
int dt;
|
||||
|
||||
chunk_appendf(msg, "%p:", entry);
|
||||
|
||||
if (proxy->table.type == SMP_T_IPV4) {
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
|
||||
chunk_appendf(msg, " key=%s", addr);
|
||||
}
|
||||
else if (proxy->table.type == SMP_T_IPV6) {
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
|
||||
chunk_appendf(msg, " key=%s", addr);
|
||||
}
|
||||
else if (proxy->table.type == SMP_T_SINT) {
|
||||
chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
|
||||
}
|
||||
else if (proxy->table.type == SMP_T_STR) {
|
||||
chunk_appendf(msg, " key=");
|
||||
dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
|
||||
}
|
||||
else {
|
||||
chunk_appendf(msg, " key=");
|
||||
dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
|
||||
}
|
||||
|
||||
chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
|
||||
|
||||
for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
|
||||
void *ptr;
|
||||
|
||||
if (proxy->table.data_ofs[dt] == 0)
|
||||
continue;
|
||||
if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
|
||||
chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
|
||||
else
|
||||
chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
|
||||
|
||||
ptr = stktable_data_ptr(&proxy->table, entry, dt);
|
||||
switch (stktable_data_types[dt].std_type) {
|
||||
case STD_T_SINT:
|
||||
chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
|
||||
break;
|
||||
case STD_T_UINT:
|
||||
chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
|
||||
break;
|
||||
case STD_T_ULL:
|
||||
chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
|
||||
break;
|
||||
case STD_T_FRQP:
|
||||
chunk_appendf(msg, "%d",
|
||||
read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
|
||||
proxy->table.data_arg[dt].u));
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunk_appendf(msg, "\n");
|
||||
|
||||
if (bi_putchk(si_ic(si), msg) == -1) {
|
||||
si_applet_cant_put(si);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Processes a single table entry matching a specific key passed in argument.
|
||||
* returns 0 if wants to be called again, 1 if has ended processing.
|
||||
*/
|
||||
static int table_process_entry_per_key(struct appctx *appctx, char **args)
|
||||
{
|
||||
struct stream_interface *si = appctx->owner;
|
||||
int action = (long)appctx->private;
|
||||
struct proxy *px = appctx->ctx.table.target;
|
||||
struct stksess *ts;
|
||||
uint32_t uint32_key;
|
||||
unsigned char ip6_key[sizeof(struct in6_addr)];
|
||||
long long value;
|
||||
int data_type;
|
||||
int cur_arg;
|
||||
void *ptr;
|
||||
struct freq_ctr_period *frqp;
|
||||
|
||||
if (!*args[4]) {
|
||||
appctx->ctx.cli.msg = "Key value expected\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (px->table.type) {
|
||||
case SMP_T_IPV4:
|
||||
uint32_key = htonl(inetaddr_host(args[4]));
|
||||
static_table_key->key = &uint32_key;
|
||||
break;
|
||||
case SMP_T_IPV6:
|
||||
inet_pton(AF_INET6, args[4], ip6_key);
|
||||
static_table_key->key = &ip6_key;
|
||||
break;
|
||||
case SMP_T_SINT:
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long val;
|
||||
errno = 0;
|
||||
val = strtoul(args[4], &endptr, 10);
|
||||
if ((errno == ERANGE && val == ULONG_MAX) ||
|
||||
(errno != 0 && val == 0) || endptr == args[4] ||
|
||||
val > 0xffffffff) {
|
||||
appctx->ctx.cli.msg = "Invalid key\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
uint32_key = (uint32_t) val;
|
||||
static_table_key->key = &uint32_key;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SMP_T_STR:
|
||||
static_table_key->key = args[4];
|
||||
static_table_key->key_len = strlen(args[4]);
|
||||
break;
|
||||
default:
|
||||
switch (action) {
|
||||
case STK_CLI_ACT_SHOW:
|
||||
appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
|
||||
break;
|
||||
case STK_CLI_ACT_CLR:
|
||||
appctx->ctx.cli.msg = "Removing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
|
||||
break;
|
||||
case STK_CLI_ACT_SET:
|
||||
appctx->ctx.cli.msg = "Inserting keys into tables of type other than ip, ipv6, string and integer is not supported\n";
|
||||
break;
|
||||
default:
|
||||
appctx->ctx.cli.msg = "Unknown action\n";
|
||||
break;
|
||||
}
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check permissions */
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
|
||||
return 1;
|
||||
|
||||
ts = stktable_lookup_key(&px->table, static_table_key);
|
||||
|
||||
switch (action) {
|
||||
case STK_CLI_ACT_SHOW:
|
||||
if (!ts)
|
||||
return 1;
|
||||
chunk_reset(&trash);
|
||||
if (!table_dump_head_to_buffer(&trash, si, px, px))
|
||||
return 0;
|
||||
if (!table_dump_entry_to_buffer(&trash, si, px, ts))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case STK_CLI_ACT_CLR:
|
||||
if (!ts)
|
||||
return 1;
|
||||
if (ts->ref_cnt) {
|
||||
/* don't delete an entry which is currently referenced */
|
||||
appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
stksess_kill(&px->table, ts);
|
||||
break;
|
||||
|
||||
case STK_CLI_ACT_SET:
|
||||
if (ts)
|
||||
stktable_touch(&px->table, ts, 1);
|
||||
else {
|
||||
ts = stksess_new(&px->table, static_table_key);
|
||||
if (!ts) {
|
||||
/* don't delete an entry which is currently referenced */
|
||||
appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
stktable_store(&px->table, ts, 1);
|
||||
}
|
||||
|
||||
for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
|
||||
if (strncmp(args[cur_arg], "data.", 5) != 0) {
|
||||
appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
data_type = stktable_get_data_type(args[cur_arg] + 5);
|
||||
if (data_type < 0) {
|
||||
appctx->ctx.cli.msg = "Unknown data type\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!px->table.data_ofs[data_type]) {
|
||||
appctx->ctx.cli.msg = "Data type not stored in this table\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
|
||||
appctx->ctx.cli.msg = "Require a valid integer value to store\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ptr = stktable_data_ptr(&px->table, ts, data_type);
|
||||
|
||||
switch (stktable_data_types[data_type].std_type) {
|
||||
case STD_T_SINT:
|
||||
stktable_data_cast(ptr, std_t_sint) = value;
|
||||
break;
|
||||
case STD_T_UINT:
|
||||
stktable_data_cast(ptr, std_t_uint) = value;
|
||||
break;
|
||||
case STD_T_ULL:
|
||||
stktable_data_cast(ptr, std_t_ull) = value;
|
||||
break;
|
||||
case STD_T_FRQP:
|
||||
/* We set both the current and previous values. That way
|
||||
* the reported frequency is stable during all the period
|
||||
* then slowly fades out. This allows external tools to
|
||||
* push measures without having to update them too often.
|
||||
*/
|
||||
frqp = &stktable_data_cast(ptr, std_t_frqp);
|
||||
frqp->curr_tick = now_ms;
|
||||
frqp->prev_ctr = 0;
|
||||
frqp->curr_ctr = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
appctx->ctx.cli.msg = "Unknown action\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Prepares the appctx fields with the data-based filters from the command line.
|
||||
* Returns 0 if the dump can proceed, 1 if has ended processing.
|
||||
*/
|
||||
static int table_prepare_data_request(struct appctx *appctx, char **args)
|
||||
{
|
||||
int action = (long)appctx->private;
|
||||
|
||||
if (action != STK_CLI_ACT_SHOW && action != STK_CLI_ACT_CLR) {
|
||||
appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* condition on stored data value */
|
||||
appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
|
||||
if (appctx->ctx.table.data_type < 0) {
|
||||
appctx->ctx.cli.msg = "Unknown data type\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
|
||||
appctx->ctx.cli.msg = "Data type not stored in this table\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
appctx->ctx.table.data_op = get_std_op(args[4]);
|
||||
if (appctx->ctx.table.data_op < 0) {
|
||||
appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
|
||||
appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* OK we're done, all the fields are set */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int action = (long)private;
|
||||
|
||||
appctx->private = private;
|
||||
appctx->ctx.table.data_type = -1;
|
||||
appctx->st2 = STAT_ST_INIT;
|
||||
appctx->ctx.table.target = NULL;
|
||||
appctx->ctx.table.proxy = NULL;
|
||||
appctx->ctx.table.entry = NULL;
|
||||
|
||||
if (*args[2]) {
|
||||
appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
|
||||
if (!appctx->ctx.table.target) {
|
||||
appctx->ctx.cli.msg = "No such table\n";
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (action != STK_CLI_ACT_SHOW)
|
||||
goto err_args;
|
||||
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])
|
||||
goto err_args;
|
||||
|
||||
return 0;
|
||||
|
||||
err_args:
|
||||
switch (action) {
|
||||
case STK_CLI_ACT_SHOW:
|
||||
appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
|
||||
break;
|
||||
case STK_CLI_ACT_CLR:
|
||||
appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
|
||||
break;
|
||||
case STK_CLI_ACT_SET:
|
||||
appctx->ctx.cli.msg = "Required arguments: <table> key <key> [data.<store_data_type> <value>]*\n";
|
||||
break;
|
||||
default:
|
||||
appctx->ctx.cli.msg = "Unknown action\n";
|
||||
break;
|
||||
}
|
||||
appctx->st0 = STAT_CLI_PRINT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This function is used to deal with table operations (dump or clear depending
|
||||
* on the action stored in appctx->private). It returns 0 if the output buffer is
|
||||
* full and it needs to be called again, otherwise non-zero.
|
||||
*/
|
||||
static int cli_io_handler_table(struct appctx *appctx)
|
||||
{
|
||||
struct stream_interface *si = appctx->owner;
|
||||
int action = (long)appctx->private;
|
||||
struct stream *s = si_strm(si);
|
||||
struct ebmb_node *eb;
|
||||
int dt;
|
||||
int skip_entry;
|
||||
int show = action == STK_CLI_ACT_SHOW;
|
||||
|
||||
/*
|
||||
* We have 3 possible states in appctx->st2 :
|
||||
* - STAT_ST_INIT : the first call
|
||||
* - STAT_ST_INFO : the proxy pointer points to the next table to
|
||||
* dump, the entry pointer is NULL ;
|
||||
* - STAT_ST_LIST : the proxy pointer points to the current table
|
||||
* and the entry pointer points to the next entry to be dumped,
|
||||
* and the refcount on the next entry is held ;
|
||||
* - STAT_ST_END : nothing left to dump, the buffer may contain some
|
||||
* data though.
|
||||
*/
|
||||
|
||||
if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
|
||||
/* in case of abort, remove any refcount we might have set on an entry */
|
||||
if (appctx->st2 == STAT_ST_LIST) {
|
||||
appctx->ctx.table.entry->ref_cnt--;
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
chunk_reset(&trash);
|
||||
|
||||
while (appctx->st2 != STAT_ST_FIN) {
|
||||
switch (appctx->st2) {
|
||||
case STAT_ST_INIT:
|
||||
appctx->ctx.table.proxy = appctx->ctx.table.target;
|
||||
if (!appctx->ctx.table.proxy)
|
||||
appctx->ctx.table.proxy = proxy;
|
||||
|
||||
appctx->ctx.table.entry = NULL;
|
||||
appctx->st2 = STAT_ST_INFO;
|
||||
break;
|
||||
|
||||
case STAT_ST_INFO:
|
||||
if (!appctx->ctx.table.proxy ||
|
||||
(appctx->ctx.table.target &&
|
||||
appctx->ctx.table.proxy != appctx->ctx.table.target)) {
|
||||
appctx->st2 = STAT_ST_END;
|
||||
break;
|
||||
}
|
||||
|
||||
if (appctx->ctx.table.proxy->table.size) {
|
||||
if (show && !table_dump_head_to_buffer(&trash, si, appctx->ctx.table.proxy, appctx->ctx.table.target))
|
||||
return 0;
|
||||
|
||||
if (appctx->ctx.table.target &&
|
||||
strm_li(s)->bind_conf->level >= ACCESS_LVL_OPER) {
|
||||
/* dump entries only if table explicitly requested */
|
||||
eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
|
||||
if (eb) {
|
||||
appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
||||
appctx->ctx.table.entry->ref_cnt++;
|
||||
appctx->st2 = STAT_ST_LIST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
|
||||
break;
|
||||
|
||||
case STAT_ST_LIST:
|
||||
skip_entry = 0;
|
||||
|
||||
if (appctx->ctx.table.data_type >= 0) {
|
||||
/* we're filtering on some data contents */
|
||||
void *ptr;
|
||||
long long data;
|
||||
|
||||
dt = appctx->ctx.table.data_type;
|
||||
ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
|
||||
appctx->ctx.table.entry,
|
||||
dt);
|
||||
|
||||
data = 0;
|
||||
switch (stktable_data_types[dt].std_type) {
|
||||
case STD_T_SINT:
|
||||
data = stktable_data_cast(ptr, std_t_sint);
|
||||
break;
|
||||
case STD_T_UINT:
|
||||
data = stktable_data_cast(ptr, std_t_uint);
|
||||
break;
|
||||
case STD_T_ULL:
|
||||
data = stktable_data_cast(ptr, std_t_ull);
|
||||
break;
|
||||
case STD_T_FRQP:
|
||||
data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
|
||||
appctx->ctx.table.proxy->table.data_arg[dt].u);
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip the entry if the data does not match the test and the value */
|
||||
if ((data < appctx->ctx.table.value &&
|
||||
(appctx->ctx.table.data_op == STD_OP_EQ ||
|
||||
appctx->ctx.table.data_op == STD_OP_GT ||
|
||||
appctx->ctx.table.data_op == STD_OP_GE)) ||
|
||||
(data == appctx->ctx.table.value &&
|
||||
(appctx->ctx.table.data_op == STD_OP_NE ||
|
||||
appctx->ctx.table.data_op == STD_OP_GT ||
|
||||
appctx->ctx.table.data_op == STD_OP_LT)) ||
|
||||
(data > appctx->ctx.table.value &&
|
||||
(appctx->ctx.table.data_op == STD_OP_EQ ||
|
||||
appctx->ctx.table.data_op == STD_OP_LT ||
|
||||
appctx->ctx.table.data_op == STD_OP_LE)))
|
||||
skip_entry = 1;
|
||||
}
|
||||
|
||||
if (show && !skip_entry &&
|
||||
!table_dump_entry_to_buffer(&trash, si, appctx->ctx.table.proxy, appctx->ctx.table.entry))
|
||||
return 0;
|
||||
|
||||
appctx->ctx.table.entry->ref_cnt--;
|
||||
|
||||
eb = ebmb_next(&appctx->ctx.table.entry->key);
|
||||
if (eb) {
|
||||
struct stksess *old = appctx->ctx.table.entry;
|
||||
appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
||||
if (show)
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
|
||||
else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
|
||||
stksess_kill(&appctx->ctx.table.proxy->table, old);
|
||||
appctx->ctx.table.entry->ref_cnt++;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (show)
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
|
||||
stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
|
||||
appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
|
||||
appctx->st2 = STAT_ST_INFO;
|
||||
break;
|
||||
|
||||
case STAT_ST_END:
|
||||
appctx->st2 = STAT_ST_FIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cli_release_show_table(struct appctx *appctx)
|
||||
{
|
||||
if (appctx->st2 == STAT_ST_LIST) {
|
||||
appctx->ctx.table.entry->ref_cnt--;
|
||||
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* register cli keywords */
|
||||
static struct cli_kw_list cli_kws = {{ },{
|
||||
{ { "clear", "table", NULL }, "clear table : remove an entry from a table", cli_parse_table_req, cli_io_handler_table, cli_release_show_table, (void *)STK_CLI_ACT_CLR },
|
||||
{ { "set", "table", NULL }, "set table [id] : update or create a table entry's data", cli_parse_table_req, cli_io_handler_table, NULL, (void *)STK_CLI_ACT_SET },
|
||||
{ { "show", "table", NULL }, "show table [id]: report table usage stats or dump this table's contents", cli_parse_table_req, cli_io_handler_table, cli_release_show_table, (void *)STK_CLI_ACT_SHOW },
|
||||
{{},}
|
||||
}};
|
||||
|
||||
|
||||
static struct action_kw_list tcp_conn_kws = { { }, {
|
||||
{ "sc-inc-gpc0", parse_inc_gpc0, 1 },
|
||||
{ "sc-set-gpt0", parse_set_gpt0, 1 },
|
||||
@ -1587,4 +2155,5 @@ static void __stick_table_init(void)
|
||||
|
||||
/* register sample fetch and format conversion keywords */
|
||||
sample_register_convs(&sample_conv_kws);
|
||||
cli_register_kw(&cli_kws);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user