]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: cli: add "kill" CLI command
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 4 Nov 2026 14:10:15 +0000 (15:10 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 9 Nov 2026 16:24:37 +0000 (17:24 +0100)
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.

doc/management.txt
include/haproxy/cli-t.h
src/cli.c

index 45d94176ccfa32b866f6d239d270879024aeae99..9450addd840cdb0e93b1fabc730090f9819576b7 100644 (file)
@@ -2541,6 +2541,13 @@ httpclient [--htx] <method> <URI>
   The --htx option allow to use the haproxy internal htx representation using
   the htx_dump() function, mainly used for debugging.
 
+kill <target>
+  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 <cafile>
   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
index 833d73a5a73e6dd5fefe5d61a0b5e896c08597e2..4bb343e8d5998a824c84026711607063028ec35c 100644 (file)
@@ -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 {
index e486b313a29a6c88ffba83e6a585339ee876d179..8863bea217c8f4908f51f2fa9943a656f14e797a 100644 (file)
--- 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] <delay_ms>} 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 <target>                           : kill matching CLI jobs",                                  cli_parse_kill, NULL },
        { { "_send_status", NULL },              NULL,                                                                                                _send_status, NULL, NULL, NULL, ACCESS_MASTER_ONLY },
        {{},}
 }};