MINOR: stats: protect proxy iteration via watcher

Define a new <px_watch> watcher member in stats applet context. It is
used to register the applet on a proxy when iterating over the proxies
list. <obj1> is automatically updated via the watcher interaction.
Watcher is first initialized prior to stats_dump_proxies() invocation.

This guarantees that stats dump is safe even if applet yields and a
backend is removed in parallel.
This commit is contained in:
Amaury Denoyelle 2026-02-10 11:20:12 +01:00
parent 4bcfc09acf
commit 20376c54e2
5 changed files with 27 additions and 10 deletions

View File

@ -578,6 +578,7 @@ struct show_stat_ctx {
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
int st_code; /* the status code returned by an action */
struct buffer chunk; /* temporary buffer which holds a single-line output */
struct watcher px_watch; /* watcher to automatically update obj1 on backend deletion */
struct watcher srv_watch; /* watcher to automatically update obj2 on server deletion */
enum stat_state state; /* phase of output production */
};

View File

@ -4089,6 +4089,8 @@ static int http_handle_stats(struct stream *s, struct channel *req, struct proxy
ctx->flags |= STAT_F_FMT_HTML; /* assume HTML mode by default */
if ((msg->flags & HTTP_MSGF_VER_11) && (txn->meth != HTTP_METH_HEAD))
ctx->flags |= STAT_F_CHUNKED;
watcher_init(&ctx->px_watch, &ctx->obj1, offsetof(struct proxy, watcher_list));
watcher_init(&ctx->srv_watch, &ctx->obj2, offsetof(struct server, watcher_list));
htx = htxbuf(&req->buf);

View File

@ -2116,6 +2116,8 @@ static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf,
static void http_stats_release(struct appctx *appctx)
{
struct show_stat_ctx *ctx = appctx->svcctx;
if (ctx->domain == STATS_DOMAIN_PROXY && ctx->obj1)
watcher_detach(&ctx->px_watch);
if (ctx->px_st == STAT_PX_ST_SV && ctx->obj2)
watcher_detach(&ctx->srv_watch);
}

View File

@ -1616,11 +1616,13 @@ int stats_dump_proxies(struct stconn *sc, struct buffer *buf, struct htx *htx)
struct proxy *px;
/* dump proxies */
while (ctx->obj1) {
/* obj1 is updated and returned through watcher_next() */
for (px = ctx->obj1; px;
px = watcher_next(&ctx->px_watch, px->next)) {
if (stats_is_full(appctx, buf, htx))
goto full;
px = ctx->obj1;
/* Skip the global frontend proxies and non-networked ones.
* Also skip proxies that were disabled in the configuration
* This change allows retrieving stats from "old" proxies after a reload.
@ -1631,7 +1633,6 @@ int stats_dump_proxies(struct stconn *sc, struct buffer *buf, struct htx *htx)
return 0;
}
ctx->obj1 = px->next;
ctx->px_st = STAT_PX_ST_INIT;
ctx->field = 0;
}

View File

@ -590,12 +590,13 @@ int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx
goto full;
}
if (domain == STATS_DOMAIN_PROXY)
ctx->obj1 = proxies_list;
ctx->px_st = STAT_PX_ST_INIT;
ctx->field = 0;
ctx->state = STAT_STATE_LIST;
/* Update ctx->obj1 via watcher to point on the first proxy. */
if (domain == STATS_DOMAIN_PROXY)
watcher_attach(&ctx->px_watch, proxies_list);
__fallthrough;
case STAT_STATE_LIST:
@ -944,6 +945,8 @@ static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx
ctx->scope_len = 0;
ctx->http_px = NULL; // not under http context
ctx->flags = STAT_F_SHNODE | STAT_F_SHDESC;
watcher_init(&ctx->px_watch, &ctx->obj1, offsetof(struct proxy, watcher_list));
watcher_init(&ctx->srv_watch, &ctx->obj2, offsetof(struct server, watcher_list));
if ((strm_li(appctx_strm(appctx))->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER)
@ -1028,8 +1031,12 @@ static int cli_io_handler_dump_stat(struct appctx *appctx)
static void cli_io_handler_release_stat(struct appctx *appctx)
{
struct show_stat_ctx *ctx = appctx->svcctx;
if (ctx->px_st == STAT_PX_ST_SV && ctx->obj2)
watcher_detach(&ctx->srv_watch);
if (ctx->state == STAT_STATE_LIST && ctx->domain == STATS_DOMAIN_PROXY) {
watcher_detach(&ctx->px_watch);
if (ctx->px_st == STAT_PX_ST_SV)
watcher_detach(&ctx->srv_watch);
}
}
static int cli_io_handler_dump_json_schema(struct appctx *appctx)
@ -1089,8 +1096,12 @@ static int cli_io_handler_dump_stat_file(struct appctx *appctx)
static void cli_io_handler_release_dump_stat_file(struct appctx *appctx)
{
struct show_stat_ctx *ctx = appctx->svcctx;
if (ctx->px_st == STAT_PX_ST_SV && ctx->obj2)
watcher_detach(&ctx->srv_watch);
if (ctx->state == STAT_STATE_LIST && ctx->domain == STATS_DOMAIN_PROXY) {
watcher_detach(&ctx->px_watch);
if (ctx->px_st == STAT_PX_ST_SV)
watcher_detach(&ctx->srv_watch);
}
}
int stats_allocate_proxy_counters_internal(struct extra_counters **counters,