From: William Lallemand Date: Thu, 13 Oct 2016 15:57:55 +0000 (+0200) Subject: MEDIUM: cli: register CLI keywords with cli_register_kw() X-Git-Tag: v1.7-dev5~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1e08cd819aa9b622fae60923252ee357fe26db07;p=thirdparty%2Fhaproxy.git MEDIUM: cli: register CLI keywords with cli_register_kw() To register a new cli keyword, you need to declare a cli_kw_list structure in your source file: static struct cli_kw_list cli_kws = {{ },{ { { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL }, { { NULL }, NULL, NULL, NULL, NULL } }}; And then register it: cli_register_kw(&cli_kws); The first field is an array of 5 elements, where you declare the keywords combination which will match, it must be ended by a NULL element. The second field is used as a usage message, it will appear in the help of the cli, you can set it to NULL if you don't want to show it, it's a good idea if you want to overwrite some existing keywords. The two last fields are callbacks. The first one is used at parsing time, you can use it to parse the arguments of your keywords and print small messages. The function must return 1 in case of a failure, otherwise 0: #include static int test_parsing(char **args, struct appctx *appctx) { struct chunk out; if (!*args[2]) { appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !"; appctx->st0 = STAT_CLI_PRINT; return 1; } chunk_reset(&trash); chunk_printf(&trash, "arg[3]: %s\n", args[2]); chunk_init(&out, NULL, 0); chunk_dup(&out, &trash); appctx->ctx.cli.err = out.str; appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */ return 0; } The last field is the IO handler callback, it can be set to NULL if you want to use the default cli_io_handler() otherwise you can write your own. You can use the private pointer in the appctx if you need to store a context or some data. stats_dump_sess_to_buffer() is a good example of IO handler, IO handlers often use the appctx->st2 variable for the state machine. The handler must return 0 in case it have to be recall later otherwise 1. --- diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index c446722f65..eba753d297 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -407,6 +407,108 @@ int stats_emit_typed_data_field(struct chunk *out, const struct field *f); int stats_emit_field_tags(struct chunk *out, const struct field *f, char delim); +struct cli_kw { + const char *str_kw[5]; /* keywords ended by NULL, limited to 5 + separated keywords combination */ + const char *usage; /* usage message */ + int (*parse)(char **args, struct appctx *appctx); + int (*io_handler)(struct appctx *appctx); +}; + +struct cli_kw_list { + struct list list; + struct cli_kw kw[VAR_ARRAY]; +}; + +struct cli_kw* cli_find_kw(char **args); +void cli_register_kw(struct cli_kw_list *kw_list); + +/* stats socket states */ +enum { + STAT_CLI_INIT = 0, /* initial state, must leave to zero ! */ + STAT_CLI_END, /* final state, let's close */ + STAT_CLI_GETREQ, /* wait for a request */ + STAT_CLI_OUTPUT, /* all states after this one are responses */ + STAT_CLI_PROMPT, /* display the prompt (first output, same code) */ + 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_INFO, /* dump info */ + STAT_CLI_O_SESS, /* dump streams */ + 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_STAT, /* dump stats */ + STAT_CLI_O_PATS, /* list all pattern reference available */ + STAT_CLI_O_PAT, /* list all entries of a pattern */ + STAT_CLI_O_MLOOK, /* lookup a map entry */ + STAT_CLI_O_POOLS, /* dump memory pools */ + STAT_CLI_O_TLSK, /* list all TLS ticket keys references */ + STAT_CLI_O_TLSK_ENT, /* list all TLS ticket keys entries for a reference */ + 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 */ + STAT_CLI_O_CUSTOM, /* custom callback pointer */ +}; + +/* Actions available for the stats admin forms */ +enum { + ST_ADM_ACTION_NONE = 0, + + /* enable/disable health checks */ + ST_ADM_ACTION_DHLTH, + ST_ADM_ACTION_EHLTH, + + /* force health check status */ + ST_ADM_ACTION_HRUNN, + ST_ADM_ACTION_HNOLB, + ST_ADM_ACTION_HDOWN, + + /* enable/disable agent checks */ + ST_ADM_ACTION_DAGENT, + ST_ADM_ACTION_EAGENT, + + /* force agent check status */ + ST_ADM_ACTION_ARUNN, + ST_ADM_ACTION_ADOWN, + + /* set admin state */ + ST_ADM_ACTION_READY, + ST_ADM_ACTION_DRAIN, + ST_ADM_ACTION_MAINT, + ST_ADM_ACTION_SHUTDOWN, + /* these are the ancient actions, still available for compatibility */ + ST_ADM_ACTION_DISABLE, + ST_ADM_ACTION_ENABLE, + ST_ADM_ACTION_STOP, + ST_ADM_ACTION_START, +}; + + +/* data transmission states for the stats responses */ +enum { + STAT_ST_INIT = 0, + STAT_ST_HEAD, + STAT_ST_INFO, + STAT_ST_LIST, + STAT_ST_END, + STAT_ST_FIN, +}; + +/* data transmission states for the stats responses inside a proxy */ +enum { + STAT_PX_ST_INIT = 0, + STAT_PX_ST_TH, + STAT_PX_ST_FE, + STAT_PX_ST_LI, + STAT_PX_ST_SV, + STAT_PX_ST_BE, + STAT_PX_ST_END, + STAT_PX_ST_FIN, +}; + + #endif /* _PROTO_DUMPSTATS_H */ /* diff --git a/include/types/applet.h b/include/types/applet.h index 10f49f1f2b..91d7d8fecd 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -54,6 +54,8 @@ struct appctx { struct applet *applet; /* applet this context refers to */ void *owner; /* pointer to upper layer's entity (eg: stream interface) */ struct act_rule *rule; /* rule associated with the applet. */ + int (*io_handler)(struct appctx *appctx); /* used within the cli_io_handler when st0 = STAT_CLI_O_CUSTOM */ + void *private; union { struct { diff --git a/src/dumpstats.c b/src/dumpstats.c index fe342231f4..de8c69e0a2 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -70,68 +70,6 @@ #include #endif -/* stats socket states */ -enum { - STAT_CLI_INIT = 0, /* initial state, must leave to zero ! */ - STAT_CLI_END, /* final state, let's close */ - STAT_CLI_GETREQ, /* wait for a request */ - STAT_CLI_OUTPUT, /* all states after this one are responses */ - STAT_CLI_PROMPT, /* display the prompt (first output, same code) */ - 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_INFO, /* dump info */ - STAT_CLI_O_SESS, /* dump streams */ - 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_STAT, /* dump stats */ - STAT_CLI_O_PATS, /* list all pattern reference available */ - STAT_CLI_O_PAT, /* list all entries of a pattern */ - STAT_CLI_O_MLOOK, /* lookup a map entry */ - STAT_CLI_O_POOLS, /* dump memory pools */ - STAT_CLI_O_TLSK, /* list all TLS ticket keys references */ - STAT_CLI_O_TLSK_ENT, /* list all TLS ticket keys entries for a reference */ - 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 */ -enum { - ST_ADM_ACTION_NONE = 0, - - /* enable/disable health checks */ - ST_ADM_ACTION_DHLTH, - ST_ADM_ACTION_EHLTH, - - /* force health check status */ - ST_ADM_ACTION_HRUNN, - ST_ADM_ACTION_HNOLB, - ST_ADM_ACTION_HDOWN, - - /* enable/disable agent checks */ - ST_ADM_ACTION_DAGENT, - ST_ADM_ACTION_EAGENT, - - /* force agent check status */ - ST_ADM_ACTION_ARUNN, - ST_ADM_ACTION_ADOWN, - - /* set admin state */ - ST_ADM_ACTION_READY, - ST_ADM_ACTION_DRAIN, - ST_ADM_ACTION_MAINT, - ST_ADM_ACTION_SHUTDOWN, - /* these are the ancient actions, still available for compatibility */ - ST_ADM_ACTION_DISABLE, - ST_ADM_ACTION_ENABLE, - ST_ADM_ACTION_STOP, - ST_ADM_ACTION_START, -}; - - /* These are the field names for each INF_* field position. Please pay attention * to always use the exact same name except that the strings for new names must * be lower case or CamelCase while the enum entries must be upper case. @@ -388,30 +326,84 @@ static const char stats_permission_denied_msg[] = "Permission denied\n" ""; -/* data transmission states for the stats responses */ -enum { - STAT_ST_INIT = 0, - STAT_ST_HEAD, - STAT_ST_INFO, - STAT_ST_LIST, - STAT_ST_END, - STAT_ST_FIN, -}; -/* data transmission states for the stats responses inside a proxy */ -enum { - STAT_PX_ST_INIT = 0, - STAT_PX_ST_TH, - STAT_PX_ST_FE, - STAT_PX_ST_LI, - STAT_PX_ST_SV, - STAT_PX_ST_BE, - STAT_PX_ST_END, - STAT_PX_ST_FIN, +static char *dynamic_usage_msg = NULL; + +/* List head of cli keywords */ +struct cli_kw_list cli_keywords = { + .list = LIST_HEAD_INIT(cli_keywords.list) }; extern const char *stat_status_codes[]; +char *cli_gen_usage_msg() +{ + struct cli_kw_list *kw_list; + struct cli_kw *kw; + struct chunk *tmp = get_trash_chunk(); + struct chunk out; + + free(dynamic_usage_msg); + dynamic_usage_msg = NULL; + + if (LIST_ISEMPTY(&cli_keywords.list)) + return NULL; + + chunk_reset(tmp); + chunk_strcat(tmp, stats_sock_usage_msg); + list_for_each_entry(kw_list, &cli_keywords.list, list) { + kw = &kw_list->kw[0]; + while (kw->usage) { + chunk_appendf(tmp, " %s\n", kw->usage); + kw++; + } + } + chunk_init(&out, NULL, 0); + chunk_dup(&out, tmp); + dynamic_usage_msg = out.str; + return dynamic_usage_msg; +} + +struct cli_kw* cli_find_kw(char **args) +{ + struct cli_kw_list *kw_list; + struct cli_kw *kw;/* current cli_kw */ + char **tmp_args; + const char **tmp_str_kw; + int found = 0; + + if (LIST_ISEMPTY(&cli_keywords.list)) + return NULL; + + list_for_each_entry(kw_list, &cli_keywords.list, list) { + kw = &kw_list->kw[0]; + while (*kw->str_kw) { + tmp_args = args; + tmp_str_kw = kw->str_kw; + while (*tmp_str_kw) { + if (strcmp(*tmp_str_kw, *tmp_args) == 0) { + found = 1; + } else { + found = 0; + break; + } + tmp_args++; + tmp_str_kw++; + } + if (found) + return (kw); + kw++; + } + } + return NULL; +} + +void cli_register_kw(struct cli_kw_list *kw_list) +{ + LIST_ADDQ(&cli_keywords.list, &kw_list->list); +} + + /* allocate a new stats frontend named , and return it * (or NULL in case of lack of memory). */ @@ -1260,6 +1252,7 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) struct stream *s = si_strm(si); struct appctx *appctx = __objt_appctx(si->end); char *args[MAX_STATS_ARGS + 1]; + struct cli_kw *kw; int arg; int i, j; @@ -1308,7 +1301,14 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) appctx->ctx.stats.scope_str = 0; appctx->ctx.stats.scope_len = 0; appctx->ctx.stats.flags = 0; - if (strcmp(args[0], "show") == 0) { + if ((kw = cli_find_kw(args))) { + if (kw->parse) { + if (kw->parse(args, appctx) == 0 && kw->io_handler) { + appctx->st0 = STAT_CLI_O_CUSTOM; + appctx->io_handler = kw->io_handler; + } + } + } else if (strcmp(args[0], "show") == 0) { if (strcmp(args[1], "backend") == 0) { appctx->ctx.be.px = NULL; appctx->st2 = STAT_ST_INIT; @@ -2729,7 +2729,11 @@ static void cli_io_handler(struct appctx *appctx) appctx->st1 = !appctx->st1; else if (strcmp(trash.str, "help") == 0 || !stats_sock_parse_request(si, trash.str)) { - appctx->ctx.cli.msg = stats_sock_usage_msg; + cli_gen_usage_msg(); + if (dynamic_usage_msg) + appctx->ctx.cli.msg = dynamic_usage_msg; + else + appctx->ctx.cli.msg = stats_sock_usage_msg; appctx->st0 = STAT_CLI_PRINT; } /* NB: stats_sock_parse_request() may have put @@ -2741,7 +2745,11 @@ static void cli_io_handler(struct appctx *appctx) * so that the user at least knows how to enable * prompt and find help. */ - appctx->ctx.cli.msg = stats_sock_usage_msg; + cli_gen_usage_msg(); + if (dynamic_usage_msg) + appctx->ctx.cli.msg = dynamic_usage_msg; + else + appctx->ctx.cli.msg = stats_sock_usage_msg; appctx->st0 = STAT_CLI_PRINT; } @@ -2830,6 +2838,11 @@ static void cli_io_handler(struct appctx *appctx) if (stats_dump_env_to_buffer(si)) appctx->st0 = STAT_CLI_PROMPT; break; + case STAT_CLI_O_CUSTOM: /* use custom pointer */ + if (appctx->io_handler) + if (appctx->io_handler(appctx)) + appctx->st0 = STAT_CLI_PROMPT; + break; default: /* abnormal state */ si->flags |= SI_FL_ERR; break;