From: Amaury Denoyelle Date: Thu, 29 Oct 2026 14:24:59 +0000 (+0100) Subject: MINOR: cli: implement uninterruptible mode for wait X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=c40d8abff078c151a2f14d695ccfbbd4dbc9ddc4;p=haproxy.git MINOR: cli: implement uninterruptible mode for wait 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. --- diff --git a/doc/management.txt b/doc/management.txt index 8f0682fef..7632544d0 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -4789,11 +4789,13 @@ update ssl ocsp-response 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 | } [ [...]] +wait { -h | [-u] } [ [...]] 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 duration. The supported conditions are: @@ -4827,17 +4829,23 @@ wait { -h | } [ [...]] 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" diff --git a/src/cli.c b/src/cli.c index edf43b185..e486b313a 100644 --- 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|} [condition [args...]]\n" + err = "Usage: wait {-h|[-u] } [condition [args...]]\n" " - '-h' displays this help\n" + " - '-u' activates uninterruptible mode\n" " - 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" " - 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|} 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] } 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 }, {{},} }};