From: Amaury Denoyelle Date: Wed, 4 Nov 2026 14:10:15 +0000 (+0100) Subject: MINOR: cli: add "kill" CLI command X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=2ffbf71d816ae62770ec3319a531ed7f57ebda13;p=haproxy.git MINOR: cli: add "kill" CLI command Implements a new "kill" admin CLI command. This allows stopping other running commands. This patch is implemented in two parts. First, the "kill" parser, which loops over CLI jobs and sets the CLI_CMD_FL_KILLED flag on the targeted ones. The other change is in cli_io_handler() so that each targeted job reacts to the CLI_CMD_FL_KILLED flag. If set, normal CLI flow is interrupted and the io_release handler is called. Any release pending message is suppressed and "Killed." is displayed to notify the user. Currently, "kill" can only stop uninterruptible commands. This must still be specified as a mandatory argument to support future extensions of the command. --- diff --git a/doc/management.txt b/doc/management.txt index 45d94176c..9450addd8 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -2541,6 +2541,13 @@ httpclient [--htx] The --htx option allow to use the haproxy internal htx representation using the htx_dump() function, mainly used for debugging. +kill + Interrupts execution of currently running CLI commands. The mandatory + argument acts as a filter on which jobs to stop. Only the value "unintr" is + supported, which selects commands running as an uninterruptible job. + + This command requires level "admin". + new ssl ca-file Create a new empty CA file tree entry to be filled with a set of CA certificates and added to a crt-list. This command should be used in diff --git a/include/haproxy/cli-t.h b/include/haproxy/cli-t.h index 833d73a5a..4bb343e8d 100644 --- a/include/haproxy/cli-t.h +++ b/include/haproxy/cli-t.h @@ -56,6 +56,7 @@ #define CLI_MAX_HELP_ENTRIES 1024 #define CLI_CMD_FL_UNINTR 0x0001 /* runs as uninterruptible */ +#define CLI_CMD_FL_KILLED 0x0002 /* immediate stop requested */ /* CLI states */ enum { diff --git a/src/cli.c b/src/cli.c index e486b313a..8863bea21 100644 --- a/src/cli.c +++ b/src/cli.c @@ -1320,6 +1320,35 @@ void cli_io_handler(struct appctx *appctx) goto out; } + if (unlikely(appctx->cli_ctx.cmd_flags & CLI_CMD_FL_KILLED)) { + if (appctx->cli_ctx.io_release) { + struct cli_print_ctx *ctx; + EXEC_CTX_NO_RET(appctx->cli_ctx.kw->exec_ctx, appctx->cli_ctx.io_release(appctx)); + + /* Clear any message allocated during io_release before replacing it with "Killed." */ + switch (appctx->st0) { + case CLI_ST_PRINT_DYN: + case CLI_ST_PRINT_DYNERR: + ctx = applet_reserve_svcctx(appctx, sizeof(*ctx)); + ha_free(&ctx->err); + break; + + case CLI_ST_PRINT_UMSG: + case CLI_ST_PRINT_UMSGERR: + usermsgs_clr(NULL); + break; + + default: + break; + } + } + + if (appctx->cli_ctx.kw) + cli_msg(appctx, LOG_INFO, "Killed.\n"); + cli_reset_cmd(appctx); + appctx->t->expire = TICK_ETERNITY; + } + if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL) || !appctx_get_buf(appctx, &appctx->outbuf)) { applet_wont_consume(appctx); @@ -2775,6 +2804,44 @@ static void cli_release_wait(struct appctx *appctx) cli_dynerr(appctx, msg); } +static int cli_parse_kill(char **args, char *payload, struct appctx *appctx, void *private) +{ + struct appctx *cli_session; + struct mt_list back; + char *msg = NULL; + int count = 0; + int unintr_fltr = 0; + + if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) + return 1; + + if (!*args[1]) + return cli_err(appctx, "Expects an argument.\n"); + + if (strcmp(args[1], "unintr") == 0) + unintr_fltr = 1; + else + return cli_err(appctx, "Unsupported target value.\n"); + + if (*args[2]) + return cli_err(appctx, "Invalid extra argument.\n"); + + thread_isolate_full(); + + MT_LIST_FOR_EACH_ENTRY_LOCKED(cli_session, &cli_session_list, cli_ctx.sess_el, back) { + if (unintr_fltr && cli_session->cli_ctx.cmd_flags & CLI_CMD_FL_UNINTR) { + cli_session->cli_ctx.cmd_flags |= CLI_CMD_FL_KILLED; + appctx_wakeup(cli_session); + ++count; + } + } + + thread_release(); + + memprintf(&msg, "Killed %d %s.\n", count, count == 1 ? "job" : "jobs"); + return cli_dynmsg(appctx, LOG_INFO, msg); +} + /* parse the "expose-fd" argument on the bind lines */ static int bind_parse_expose_fd(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) { @@ -4644,6 +4711,7 @@ static struct cli_kw_list cli_kws = {{ },{ { { "operator", NULL }, "operator : lower the level of the current CLI session to operator", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER}, { { "user", NULL }, "user : lower the level of the current CLI session to user", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER}, { { "wait", NULL }, "wait {-h|[-u] } cond [args...] : wait the specified delay or condition (-h to see list)", cli_parse_wait, cli_io_handler_wait, cli_release_wait, NULL }, + { { "kill", NULL }, "kill : kill matching CLI jobs", cli_parse_kill, NULL }, { { "_send_status", NULL }, NULL, _send_status, NULL, NULL, NULL, ACCESS_MASTER_ONLY }, {{},} }};