MEDIUM: map: dynamic manipulation of maps

This patch adds map manipulation commands to the socket interface.

add map <map> <key> <value>
  Add the value <value> in the map <map>, at the entry corresponding to
  the key <key>. This command does not verify if the entry already
  exists.

clear map <map>
  Remove entries from the map <map>

del map <map> <key>
  Delete all the map entries corresponding to the <key> value in the map
  <map>.

set map <map> <key> <value>
  Modify the value corresponding to each key <key> in a map <map>. The
  new value is <value>.

show map [<map>]
  Dump info about map converters. Without argument, the list of all
  available maps are returned. If a <map> is specified, is content is
  dumped.
This commit is contained in:
Thierry FOURNIER 2013-12-11 16:55:52 +01:00 committed by Willy Tarreau
parent 48bcfdab24
commit c0e0d7b7cf
4 changed files with 680 additions and 13 deletions

View File

@ -12292,6 +12292,11 @@ If an unknown command is sent, haproxy displays the usage message which reminds
all supported commands. Some commands support a more complex syntax, generally
it will explain what part of the command is invalid when this happens.
add map <map> <key> <value>
Add an entry into the map <map> to associate the value <value> to the key
<key>. This command does not verify if the entry already exists. It is
mainly used to fill a map after a clear operation.
clear counters
Clear the max values of the statistics counters in each proxy (frontend &
backend) and in each server. The cumulated counters are not affected. This
@ -12304,6 +12309,9 @@ clear counters all
server. This has the same effect as restarting. This command is restricted
and can only be issued on sockets configured for level "admin".
clear map <map>
Remove all entries from the map <map>.
clear table <table> [ data.<type> <operator> <value> ] | [ key <key> ]
Remove entries from the stick-table <table>.
@ -12374,6 +12382,9 @@ enable agent <backend>/<server>
This command is restricted and can only be issued on sockets configured for
level "admin".
del map <map> <key>
Delete all the map entries from the map <map> corresponding to the key <key>.
disable frontend <frontend>
Mark the frontend as temporarily stopped. This corresponds to the mode which
is used during a soft restart : the frontend releases the port but can be
@ -12462,6 +12473,10 @@ prompt
quit
Close the connection when in interactive mode.
set map <map> <key> <value>
Modify the value corresponding to each key <key> in a map <map>. The new value
is <value>.
set maxconn frontend <frontend> <value>
Dynamically change the specified frontend's maxconn setting. Any positive
value is allowed including zero, but setting values larger than the global
@ -12577,6 +12592,10 @@ show errors [<iid>]
show info
Dump info about haproxy status on current process.
show map [<map>]
Dump info about map converters. Without argument, the list of all available
maps is returned. If a <map> is specified, its contents are dumped.
show sess
Dump all known sessions. Avoid doing this on slow connections as this can
be huge. This command is restricted and can only be issued on sockets

View File

@ -39,20 +39,24 @@
#define STATS_TYPE_SO 3
/* unix stats socket states */
#define STAT_CLI_INIT 0 /* initial state, must leave to zero ! */
#define STAT_CLI_END 1 /* final state, let's close */
#define STAT_CLI_GETREQ 2 /* wait for a request */
#define STAT_CLI_OUTPUT 3 /* all states after this one are responses */
#define STAT_CLI_PROMPT 3 /* display the prompt (first output, same code) */
#define STAT_CLI_PRINT 4 /* display message in cli->msg */
#define STAT_CLI_INIT 0 /* initial state, must leave to zero ! */
#define STAT_CLI_END 1 /* final state, let's close */
#define STAT_CLI_GETREQ 2 /* wait for a request */
#define STAT_CLI_OUTPUT 3 /* all states after this one are responses */
#define STAT_CLI_PROMPT 3 /* display the prompt (first output, same code) */
#define STAT_CLI_PRINT 4 /* display message in cli->msg */
#define STAT_CLI_O_INFO 5 /* dump info */
#define STAT_CLI_O_SESS 6 /* dump sessions */
#define STAT_CLI_O_ERR 7 /* dump errors */
#define STAT_CLI_O_TAB 8 /* dump tables */
#define STAT_CLI_O_CLR 9 /* clear tables */
#define STAT_CLI_O_SET 10 /* set entries in tables */
#define STAT_CLI_O_STAT 11 /* dump stats */
#define STAT_CLI_O_INFO 5 /* dump info */
#define STAT_CLI_O_SESS 6 /* dump sessions */
#define STAT_CLI_O_ERR 7 /* dump errors */
#define STAT_CLI_O_TAB 8 /* dump tables */
#define STAT_CLI_O_CLR 9 /* clear tables */
#define STAT_CLI_O_SET 10 /* set entries in tables */
#define STAT_CLI_O_STAT 11 /* dump stats */
#define STAT_CLI_O_MAPS 12 /* list all maps */
#define STAT_CLI_O_MAP 13 /* list all map entries of a map */
#define STAT_CLI_O_MLOOK 14 /* lookup a map entry */
/* HTTP stats : applet.st0 */
enum {

View File

@ -141,6 +141,12 @@ struct appctx {
struct {
void *ptr; /* multi-purpose pointer for peers */
} peers;
struct {
struct map_reference *ref;
struct map_entry *ent;
struct map_descriptor *desc;
struct chunk chunk;
} map;
} ctx; /* used by stats I/O handlers to dump the stats */
};

View File

@ -46,8 +46,10 @@
#include <proto/fd.h>
#include <proto/freq_ctr.h>
#include <proto/log.h>
#include <proto/pattern.h>
#include <proto/pipe.h>
#include <proto/listener.h>
#include <proto/map.h>
#include <proto/proto_http.h>
#include <proto/proto_uxst.h>
#include <proto/proxy.h>
@ -68,6 +70,9 @@ static int stats_dump_errors_to_buffer(struct stream_interface *si);
static int stats_table_request(struct stream_interface *si, int show);
static int stats_dump_proxy_to_buffer(struct stream_interface *si, struct proxy *px, struct uri_auth *uri);
static int stats_dump_stat_to_buffer(struct stream_interface *si, struct uri_auth *uri);
static int stats_maps_list(struct stream_interface *si);
static int stats_map_list(struct stream_interface *si);
static int stats_map_lookup(struct stream_interface *si);
/*
* cli_io_handler()
@ -103,6 +108,7 @@ 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"
" clear map [id] : clear the content of this map\n"
" help : this message\n"
" prompt : toggle interactive mode with prompt\n"
" quit : disconnect\n"
@ -111,12 +117,16 @@ static const char stats_sock_usage_msg[] =
" show errors : report last request and response errors for each proxy\n"
" show sess [id] : report the list of current sessions or dump this session\n"
" show table [id]: report table usage stats or dump this table's contents\n"
" show map [id] : report avalaible maps or dump this map's contents\n"
" get weight : report a server's current weight\n"
" set weight : change a server's weight\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"
" set map [id] [key] [value] : modify map entry\n"
" add map [id] [key] [value] : add map entry\n"
" del map [id] [key] : delete map entry\n"
" disable : put a server or frontend in maintenance mode\n"
" enable : re-enable a server or frontend which is in maintenance mode\n"
" shutdown : kill a session or a frontend (eg:to release listening ports)\n"
@ -914,6 +924,42 @@ static struct server *expect_server_admin(struct session *s, struct stream_inter
return sv;
}
/* This function is used with map management. It permits to browse each
* really allocated descriptors of one map reference. The variable
* <appctx->ctx.map.ref> must contain the map reference to browse.
* The variable <appctx->ctx.map.desc> contain the descriptor of the
* current allocated map descriptor. This variable must be initialized
* to NULL.
*/
static inline void stats_map_lookup_next(struct stream_interface *si)
{
struct appctx *appctx = __objt_appctx(si->end);
/* search the next allocated map */
while (1) {
/* get next descriptor */
if (!appctx->ctx.map.desc)
appctx->ctx.map.desc = LIST_NEXT(&appctx->ctx.map.ref->maps,
struct map_descriptor *, list);
else
appctx->ctx.map.desc = LIST_NEXT(&appctx->ctx.map.desc->list,
struct map_descriptor *, list);
/* detect end of list */
if (&appctx->ctx.map.desc->list == &appctx->ctx.map.ref->maps) {
appctx->ctx.map.desc = NULL;
return;
}
/* do not lookup this entry */
if (!appctx->ctx.map.desc->do_free)
continue;
/* avalaible descriptor */
return;
}
}
/* Processes the stats interpreter on the statistics socket. This function is
* called from an applet running in a stream interface. The function returns 1
* if the request was understood, otherwise zero. It sets appctx->st0 to a value
@ -1021,6 +1067,25 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
else if (strcmp(args[1], "table") == 0) {
stats_sock_table_request(si, args, STAT_CLI_O_TAB);
}
else if (strcmp(args[1], "map") == 0) {
/* no parameter: display all map avalaible */
if (!*args[2]) {
appctx->st2 = STAT_ST_INIT;
appctx->st0 = STAT_CLI_O_MAPS;
return 1;
}
/* lookup into the maps */
appctx->ctx.map.ref = map_get_reference(args[2]);
if (!appctx->ctx.map.ref) {
appctx->ctx.cli.msg = "Unknown map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
appctx->st2 = STAT_ST_INIT;
appctx->st0 = STAT_CLI_O_MAP;
}
else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
return 0;
}
@ -1088,6 +1153,43 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
/* end of processing */
return 1;
}
else if (strcmp(args[1], "map") == 0) {
struct map_reference *mref;
struct map_descriptor *mdesc;
struct map_entry *ent, *nent;
/* no parameter */
if (!*args[2]) {
appctx->ctx.cli.msg = "Expect map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* lookup into the maps */
mref = map_get_reference(args[2]);
if (!mref) {
appctx->ctx.cli.msg = "Unknown map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* clear all maps */
list_for_each_entry(mdesc, &mref->maps, list)
if (mdesc->do_free)
pattern_prune_expr(mdesc->pat);
/* clear map reference */
list_for_each_entry_safe(ent, nent, &mref->entries, list) {
LIST_DEL(&ent->list);
free(ent->key);
free(ent->value);
free(ent);
}
/* return response */
appctx->ctx.cli.msg = "Done.\n";
appctx->st0 = STAT_CLI_PRINT;
}
else {
/* unknown "clear" argument */
return 0;
@ -1122,6 +1224,37 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
bi_putstr(si->ib, trash.str);
return 1;
}
else if (strcmp(args[1], "map") == 0) {
/* no parameter */
if (!*args[2] || !*args[3]) {
appctx->ctx.cli.msg = "Expect map reference and required key.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* lookup into the maps */
appctx->ctx.map.ref = map_get_reference(args[2]);
if (!appctx->ctx.map.ref) {
appctx->ctx.cli.msg = "Unknown map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* copy input string */
appctx->ctx.map.chunk.len = strlen(args[3]);
appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
appctx->ctx.map.chunk.str = strdup(args[3]);
if (!appctx->ctx.map.chunk.str) {
appctx->ctx.cli.msg = "Out of memory error.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* prepare response */
appctx->st2 = STAT_ST_INIT;
appctx->st0 = STAT_CLI_O_MLOOK;
}
else { /* not "get weight" */
return 0;
}
@ -1319,6 +1452,70 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
else if (strcmp(args[1], "table") == 0) {
stats_sock_table_request(si, args, STAT_CLI_O_SET);
}
else if (strcmp(args[1], "map") == 0) {
struct pattern *pat_elt;
struct pat_idx_elt *idx_elt;
char *value;
/* Expect three parameters: map name, key and new value. */
if (!*args[2] || !*args[3] || !*args[4]) {
appctx->ctx.cli.msg = "'set map' expect three parameters: map name, key and value.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Lookup the reference in the maps. */
appctx->ctx.map.ref = map_get_reference(args[2]);
if (!appctx->ctx.map.ref) {
appctx->ctx.cli.msg = "Unknown map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Lookup the entry in the reference values. */
list_for_each_entry(appctx->ctx.map.ent, &appctx->ctx.map.ref->entries, list)
if (strcmp(args[3], appctx->ctx.map.ent->key) == 0)
break;
if (&appctx->ctx.map.ent->list == &appctx->ctx.map.ref->entries) {
appctx->ctx.cli.msg = "Entry not found.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Update each reference entries. */
list_for_each_entry(appctx->ctx.map.ent, &appctx->ctx.map.ref->entries, list) {
if (strcmp(args[3], appctx->ctx.map.ent->key) == 0) {
value = strdup(args[4]);
if (!value) {
appctx->ctx.cli.msg = "Out of memory error.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
free(appctx->ctx.map.ent->value);
appctx->ctx.map.ent->value = value;
}
}
/* Change the sample. The lookup juste return the first entry, other
* entries are not changed, but are never matched.
*/
appctx->ctx.map.desc = NULL;
for (stats_map_lookup_next(si);
appctx->ctx.map.desc;
stats_map_lookup_next(si)) {
pattern_lookup(args[3], appctx->ctx.map.desc->pat, &pat_elt, &idx_elt, NULL);
if (pat_elt != NULL)
appctx->ctx.map.desc->parse(appctx->ctx.map.ent->value, pat_elt->smp);
if (idx_elt != NULL)
appctx->ctx.map.desc->parse(appctx->ctx.map.ent->value, idx_elt->smp);
}
/* The set is done, send message. */
appctx->ctx.cli.msg = "Done.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
else { /* unknown "set" parameter */
return 0;
}
@ -1536,6 +1733,190 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
return 1;
}
}
else if (strcmp(args[0], "del") == 0) {
if (strcmp(args[1], "map") == 0) {
struct pattern *pat_elt;
struct pat_idx_elt *idx_elt;
struct map_entry *ent;
/* Expect two parameters: map name and key. */
if (!*args[2] || !*args[3]) {
appctx->ctx.cli.msg = "'del map' expect two parameters: map name and key.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Lookup the reference in the maps. */
appctx->ctx.map.ref = map_get_reference(args[2]);
if (!appctx->ctx.map.ref) {
appctx->ctx.cli.msg = "Unknown map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Lookup the entry in the reference values.
* If the entry is not found in the reference, return error message.
*/
list_for_each_entry(appctx->ctx.map.ent, &appctx->ctx.map.ref->entries, list)
if (strcmp(args[3], appctx->ctx.map.ent->key) == 0)
break;
if (&appctx->ctx.map.ent->list == &appctx->ctx.map.ref->entries) {
appctx->ctx.cli.msg = "Entry not found.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Delete each enties from reference. */
list_for_each_entry_safe(appctx->ctx.map.ent, ent, &appctx->ctx.map.ref->entries, list) {
if (strcmp(args[3], appctx->ctx.map.ent->key) == 0) {
LIST_DEL(&appctx->ctx.map.ent->list);
free(appctx->ctx.map.ent->key);
free(appctx->ctx.map.ent->value);
free(appctx->ctx.map.ent);
}
}
/* Delete all matching entries for each map descritor. */
appctx->ctx.map.desc = NULL;
stats_map_lookup_next(si);
while (appctx->ctx.map.desc) {
while (pattern_lookup(args[3], appctx->ctx.map.desc->pat, &pat_elt, &idx_elt, NULL)) {
if (pat_elt != NULL) {
LIST_DEL(&pat_elt->list);
pattern_free(pat_elt);
}
if (idx_elt != NULL) {
ebmb_delete(&idx_elt->node);
free(idx_elt);
}
}
stats_map_lookup_next(si);
}
/* The deletion is done, send message. */
appctx->ctx.cli.msg = "Done.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
else { /* unknown "del" parameter */
appctx->ctx.cli.msg = "'del' only supports 'map'.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
}
else if (strcmp(args[0], "add") == 0) {
if (strcmp(args[1], "map") == 0) {
const char *params[2];
struct pattern *pat;
struct map_entry *ent;
struct sample_storage *smp;
/* Expect three parameters: map name, key and new value. */
if (!*args[2] || !*args[3] || !*args[4]) {
appctx->ctx.cli.msg = "'add map' expect three parameters: map name, key and value.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
params[0] = args[3];
params[1] = "";
/* Lookup the reference in the maps. */
appctx->ctx.map.ref = map_get_reference(args[2]);
if (!appctx->ctx.map.ref) {
appctx->ctx.cli.msg = "Unknown map reference.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Prepare and link the new map_entry element. If out of memory
* error the action is cancelled and the descriptor are left
* coherents.
*/
ent = malloc(sizeof(*ent));
if (!ent) {
appctx->ctx.cli.msg = "Out of memory error.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
ent->key = strdup(args[3]);
if (!ent->key) {
free(ent);
appctx->ctx.cli.msg = "Out of memory error.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
ent->value = strdup(args[4]);
if (!ent->value) {
free(ent->key);
free(ent);
appctx->ctx.cli.msg = "Out of memory error.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
LIST_ADDQ(&appctx->ctx.map.ref->entries, &ent->list);
/* Browse each map descritor and try to insert this new value. */
appctx->ctx.map.desc = NULL;
for (stats_map_lookup_next(si);
appctx->ctx.map.desc;
stats_map_lookup_next(si)) {
/* Create new sample. Return out of memory error
* if the memory cannot be allocated. The 'add' process
* is aborted, but the already inserted entries are not
* deleted.
*/
smp = calloc(1, sizeof(*smp));
if (!smp) {
appctx->ctx.cli.msg = "Out of memory error. The value is not added in all maps.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
/* Create sample. If this function fails, the insertion
* is canceled for this 'descriptor', but continue, for
* the other descriptors.
*/
if (!appctx->ctx.map.desc->parse(ent->value, smp)) {
free(smp);
continue;
}
/* If the value can be indexed, get the first pattern. If
* the return entry is not the indexed entry, new 'pattern' is
* created by the function pattern_register(). If the 'pattern'
* is NULL, new entry is created. This is ugly because the
* following code interfers with the own code of the function
* pattern_register().
*/
if (appctx->ctx.map.desc->pat->match == pat_match_str ||
appctx->ctx.map.desc->pat->match == pat_match_ip) {
pat = LIST_NEXT(&appctx->ctx.map.desc->pat->patterns, struct pattern *, list);
if (&pat->list == &appctx->ctx.map.desc->pat->patterns)
pat = NULL;
}
else
pat = NULL;
if (!pattern_register(appctx->ctx.map.desc->pat, params, smp, &pat, 0, NULL)) {
free(smp);
continue;
}
}
/* The add is done, send message. */
appctx->ctx.cli.msg = "Done.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
else { /* unknown "del" parameter */
appctx->ctx.cli.msg = "'add' only supports 'map'.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
}
else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
return 0;
}
@ -1676,6 +2057,17 @@ static void cli_io_handler(struct stream_interface *si)
if (stats_table_request(si, appctx->st0))
appctx->st0 = STAT_CLI_PROMPT;
break;
case STAT_CLI_O_MAPS:
if (stats_maps_list(si))
appctx->st0 = STAT_CLI_PROMPT;
break;
case STAT_CLI_O_MAP:
if (stats_map_list(si))
appctx->st0 = STAT_CLI_PROMPT;
break;
case STAT_CLI_O_MLOOK:
if (stats_map_lookup(si))
appctx->st0 = STAT_CLI_PROMPT;
default: /* abnormal state */
appctx->st0 = STAT_CLI_PROMPT;
break;
@ -4222,6 +4614,252 @@ static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct se
return 1;
}
static int stats_maps_list(struct stream_interface *si)
{
struct appctx *appctx = __objt_appctx(si->end);
switch (appctx->st2) {
case STAT_ST_INIT:
/* Init to the first entry. The list cannot be change */
appctx->ctx.map.ref = LIST_NEXT(&maps, struct map_reference *, list);
appctx->st2 = STAT_ST_LIST;
/* fall through */
case STAT_ST_LIST:
while (appctx->ctx.map.ref) {
chunk_reset(&trash);
/* build messages */
chunk_appendf(&trash, "%s\n", appctx->ctx.map.ref->reference);
if (bi_putchk(si->ib, &trash) == -1) {
/* let's try again later from this session. We add ourselves into
* this session's users so that it can remove us upon termination.
*/
return 0;
}
/* get next list entry and check the end of the list */
appctx->ctx.map.ref = LIST_NEXT(&appctx->ctx.map.ref->list,
struct map_reference *, list);
if (&appctx->ctx.map.ref->list == &maps)
break;
}
appctx->st2 = STAT_ST_FIN;
/* fall through */
default:
appctx->st2 = STAT_ST_FIN;
return 1;
}
}
static const char *smp_to_type[SMP_TYPES] = {
[SMP_T_BOOL] = "bool",
[SMP_T_UINT] = "uint",
[SMP_T_SINT] = "sint",
[SMP_T_ADDR] = "addr",
[SMP_T_IPV4] = "ipv4",
[SMP_T_IPV6] = "ipv6",
[SMP_T_STR] = "str",
[SMP_T_BIN] = "bin",
[SMP_T_CSTR] = "cstr",
[SMP_T_CBIN] = "cbin",
};
static int stats_map_lookup(struct stream_interface *si)
{
struct appctx *appctx = __objt_appctx(si->end);
struct sample_storage *smp;
struct sample sample;
struct pattern *pat;
struct pat_idx_elt *elt;
enum pat_match_res res;
struct sockaddr_in addr;
char s_addr[INET_ADDRSTRLEN];
switch (appctx->st2) {
case STAT_ST_INIT:
appctx->ctx.map.desc = NULL;
stats_map_lookup_next(si);
appctx->st2 = STAT_ST_LIST;
/* fall through */
case STAT_ST_LIST:
/* for each lookup type */
while (appctx->ctx.map.desc) {
/* initialise chunk to build new message */
chunk_reset(&trash);
/* execute pattern matching */
sample.type = SMP_T_CSTR;
sample.data.str.len = appctx->ctx.map.chunk.len;
sample.data.str.str = appctx->ctx.map.chunk.str;
pat = NULL;
elt = NULL;
res = pattern_exec_match(appctx->ctx.map.desc->pat, &sample, &smp, &pat, &elt);
/* build return message: set type of match */
/**/ if (appctx->ctx.map.desc->pat->match == NULL)
chunk_appendf(&trash, "found, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_nothing)
chunk_appendf(&trash, "bool, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_int)
chunk_appendf(&trash, "int, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_ip)
chunk_appendf(&trash, "ip, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_bin)
chunk_appendf(&trash, "bin, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_len)
chunk_appendf(&trash, "len, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_str)
chunk_appendf(&trash, "str, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_beg)
chunk_appendf(&trash, "beg, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_sub)
chunk_appendf(&trash, "sub, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_dir)
chunk_appendf(&trash, "dir, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_dom)
chunk_appendf(&trash, "dom, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_end)
chunk_appendf(&trash, "end, ");
else if (appctx->ctx.map.desc->pat->match == pat_match_reg)
chunk_appendf(&trash, "reg, ");
else /* The never appens case */
chunk_appendf(&trash, "unknown(%p), ", appctx->ctx.map.desc->pat->match);
/* Display no match, and set default value */
if (res == PAT_NOMATCH) {
chunk_appendf(&trash, "no-match, ");
smp = appctx->ctx.map.desc->def;
}
/* Display match and match info */
else {
/* display match */
chunk_appendf(&trash, "match, ");
/* display search mode */
if (elt)
chunk_appendf(&trash, "tree, ");
else
chunk_appendf(&trash, "list, ");
/* display search options */
if (pat) {
/* case sensitive */
if (pat->flags & PAT_F_IGNORE_CASE)
chunk_appendf(&trash, "case-insensitive, ");
else
chunk_appendf(&trash, "case-sensitive, ");
/* display source */
if (pat->flags & PAT_F_FROM_FILE)
chunk_appendf(&trash, "from-file, ");
}
/* display match expresion */
if (elt) {
if (appctx->ctx.map.desc->pat->match == pat_match_str) {
chunk_appendf(&trash, "match=\"%s\", ", elt->node.key);
}
/* only IPv4 */
else if (appctx->ctx.map.desc->pat->match == pat_match_ip) {
/* convert ip */
memcpy(&addr.sin_addr, elt->node.key, 4);
addr.sin_family = AF_INET;
if (addr_to_str((struct sockaddr_storage *)&addr, s_addr, INET_ADDRSTRLEN))
chunk_appendf(&trash, "match=\"%s/%d\", ", s_addr, elt->node.node.pfx);
}
}
}
/* display return value */
if (!smp) {
chunk_appendf(&trash, "return=nothing\n");
}
else {
memcpy(&sample.data, &smp->data, sizeof(sample.data));
sample.type = smp->type;
if (sample_casts[sample.type][SMP_T_CSTR] &&
sample_casts[sample.type][SMP_T_CSTR](&sample))
chunk_appendf(&trash, "return=\"%s\", type=\"%s\"\n",
sample.data.str.str, smp_to_type[smp->type]);
else
chunk_appendf(&trash, "return=cannot-display, type=\"%s\"\n",
smp_to_type[smp->type]);
}
/* display response */
if (bi_putchk(si->ib, &trash) == -1) {
/* let's try again later from this session. We add ourselves into
* this session's users so that it can remove us upon termination.
*/
return 0;
}
/* get next entry */
stats_map_lookup_next(si);
}
appctx->st2 = STAT_ST_FIN;
/* fall through */
default:
appctx->st2 = STAT_ST_FIN;
free(appctx->ctx.map.chunk.str);
return 1;
}
}
static int stats_map_list(struct stream_interface *si)
{
struct appctx *appctx = __objt_appctx(si->end);
switch (appctx->st2) {
case STAT_ST_INIT:
/* Init to the first entry. The list cannot be change */
appctx->ctx.map.ent = LIST_NEXT(&appctx->ctx.map.ref->entries,
struct map_entry *, list);
if (&appctx->ctx.map.ent->list == &appctx->ctx.map.ref->entries)
appctx->ctx.map.ent = NULL;
appctx->st2 = STAT_ST_LIST;
/* fall through */
case STAT_ST_LIST:
while (appctx->ctx.map.ent) {
chunk_reset(&trash);
/* build messages */
chunk_appendf(&trash, "%s %s\n", appctx->ctx.map.ent->key, appctx->ctx.map.ent->value);
if (bi_putchk(si->ib, &trash) == -1) {
/* let's try again later from this session. We add ourselves into
* this session's users so that it can remove us upon termination.
*/
return 0;
}
/* get next list entry and check the end of the list */
appctx->ctx.map.ent = LIST_NEXT(&appctx->ctx.map.ent->list,
struct map_entry *, list);
if (&appctx->ctx.map.ent->list == &appctx->ctx.map.ref->entries)
break;
}
appctx->st2 = STAT_ST_FIN;
/* fall through */
default:
appctx->st2 = STAT_ST_FIN;
return 1;
}
}
/* This function dumps all sessions' states 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. It is designed to be called