]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: cli: implement uninterruptible mode for wait
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 29 Oct 2026 14:24:59 +0000 (15:24 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 9 Nov 2026 16:15:29 +0000 (17:15 +0100)
Define a new argument "-u" for the CLI wait command. This allows
executing wait in uninterruptible mode, which is convenient when using
utilities such as socat.

The new argument is parsed in cli_parse_wait(). If present, the command
will be started as an uninterruptible CLI job and will be accounted for
as such, unless the uninterruptible jobs limit is reached.

doc/management.txt
src/cli.c

index 8f0682fef856e53609b784789953291bbaab34e8..7632544d08feca0a335e9b09675295ed99eff670 100644 (file)
@@ -4789,11 +4789,13 @@ update ssl ocsp-response <certfile>
   local tree, its contents will be displayed on the standard output. The format
   is the same as the one described in "show ssl ocsp-response".
 
-wait { -h | <delay> } [<condition> [<args>...]]
+wait { -h | [-u] <delay> } [<condition> [<args>...]]
   In its simplest form without any condition, this simply waits for the
   requested delay before continuing. This can be used to collect metrics around
   a specific interval.
 
+  Passing "-h" as the first or second argument provides the command's usage.
+
   With a condition and optional arguments, the command will wait for the
   specified condition to be satisfied, to unrecoverably fail, or to remain
   unsatisfied for the whole <delay> duration. The supported conditions are:
@@ -4827,17 +4829,23 @@ wait { -h | <delay> } [<condition> [<args>...]]
     the delay.
 
   The default unit for the delay is milliseconds, though other units are
-  accepted if suffixed with the usual timer units (us, ms, s, m, h, d). When
-  used with the 'socat' utility, do not forget to extend socat's close timeout
-  to cover the wait time. Passing "-h" as the first or second argument provides
-  the command's usage.
+  accepted if suffixed with the usual timer units (us, ms, s, m, h, d).
+
+  By default, the command handler is interrupted as soon as the CLI input has
+  been closed which is the safest behavior. However, this causes issues with
+  utilities such as 'socat' which close their output channel only, as haproxy
+  is unable to differentiate between a full and a partial closure. Extra
+  argument '-u' can be specified to run the command in uninterruptible
+  execution mode. This is especially useful when using 'socat', at the
+  condition of the 'socat' close timeout being extended to cover the wait time.
+
   Example:
-    $ socat -t20 /path/to/socket - <<< "show activity; wait 10s; show activity"
+    $ socat -t20 /path/to/socket - <<< "show activity; wait -u 10s; show activity"
 
     $ socat -t5 /path/to/socket - <<< "
         disable server px/srv1
         shutdown sessions server px/srv1
-        wait 2s srv-removable px/srv1
+        wait -u 2s srv-removable px/srv1
         del server px/srv1"
 
 
index edf43b1857b91d14d616420d04d9058f8addbf29..e486b313a29a6c88ffba83e6a585339ee876d179 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -2526,12 +2526,17 @@ static int cli_parse_set_ratelimit(char **args, char *payload, struct appctx *ap
 static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, void *private)
 {
        struct cli_wait_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
-       uint wait_ms;
+       uint wait_ms, unintr = 0;
        const char *err;
 
        if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
                return 1;
 
+       if (strcmp(args[1], "-u") == 0) {
+               unintr = 1;
+               ++args;
+       }
+
        if (!*args[1])
                return cli_err(appctx, "Expects a duration in milliseconds.\n");
 
@@ -2558,8 +2563,9 @@ static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, voi
        }
        else if (*args[2]) {
                /* show the command's help either upon request (-h) or error */
-               err = "Usage: wait {-h|<duration>} [condition [args...]]\n"
+               err = "Usage: wait {-h|[-u] <duration>} [condition [args...]]\n"
                        "  - '-h' displays this help\n"
+                       "  - '-u' activates uninterruptible mode\n"
                        "  - <duration> is the maximum wait time, optionally suffixed by the unit among\n"
                        "    'us', 'ms', 's', 'm', 'h', and 'd'. ; the default unit is milliseconds.\n"
                        "  - <condition> indicates what to wait for, no longer than the specified\n"
@@ -2615,6 +2621,9 @@ static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, voi
        ctx->start = now_ms;
        ctx->deadline = tick_add(now_ms, wait_ms);
 
+       if (unintr && cli_inc_unintr(appctx))
+               return cli_err(appctx, "Cannot start a new uninterruptible job.\n");
+
        /* proceed with the I/O handler */
        return 0;
 }
@@ -2731,7 +2740,8 @@ static int cli_io_handler_wait(struct appctx *appctx)
 
  wait:
        /* Stop waiting upon close/abort/error */
-       if (unlikely(se_fl_test(appctx->sedesc, SE_FL_SHW)) && !b_data(&appctx->inbuf)) {
+       if (!(appctx->cli_ctx.cmd_flags & CLI_CMD_FL_UNINTR) &&
+           unlikely(se_fl_test(appctx->sedesc, SE_FL_SHW)) && !b_data(&appctx->inbuf)) {
                ctx->error = CLI_WAIT_ERR_INTR;
                return 1;
        }
@@ -2747,6 +2757,8 @@ static void cli_release_wait(struct appctx *appctx)
        char *msg = NULL;
        int i;
 
+       cli_dec_unintr(appctx);
+
        switch (ctx->error) {
        case CLI_WAIT_ERR_EXP:  memprintf(&msg, "Wait delay expired. %s\n", ctx->msg); break;
        case CLI_WAIT_ERR_INTR: memprintf(&msg, "Interrupted. %s\n", ctx->msg); break;
@@ -4631,7 +4643,7 @@ static struct cli_kw_list cli_kws = {{ },{
        { { "show", "version", NULL },           "show version                            : show version of the current process",                     cli_parse_show_version, NULL, NULL, NULL, ACCESS_MASTER },
        { { "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|<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 },
+       { { "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 },
        { { "_send_status", NULL },              NULL,                                                                                                _send_status, NULL, NULL, NULL, ACCESS_MASTER_ONLY },
        {{},}
 }};