From 8ae1df2067045d61691605d21473dc12fa9ee318 Mon Sep 17 00:00:00 2001
From: Alexander Stephan <alexander.stephan@sap.com>
Date: Fri, 23 Jan 2026 18:08:21 +0000
Subject: [PATCH] Update show proc to use reload counter

---
 src/mworker.c | 145 ++++++++++++++++++++++++++++----------------------
 1 file changed, 81 insertions(+), 64 deletions(-)

diff --git a/src/mworker.c b/src/mworker.c
index c4461cc10..78370a44b 100644
--- a/src/mworker.c
+++ b/src/mworker.c
@@ -560,6 +560,28 @@ void mworker_cleanup_proc()
 	}
 }
 
+struct cli_showproc_ctx {
+	int debug;
+	int next_reload; /* reload number to resume from, 0 = from the beginning  */
+};
+
+
+/* Append a single worker row to trash (shared between current/old sections) */
+static void cli_append_worker_row(struct cli_showproc_ctx *ctx, struct mworker_proc *child, time_t tv_sec)
+{
+	char *uptime = NULL;
+	int up = tv_sec - child->timestamp;
+
+	if (up < 0) /* must never be negative because of clock drift */
+		up = 0;
+
+	memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
+	chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s", child->pid, "worker", child->reloads, uptime, child->version ? child->version : "");
+	if (ctx->debug)
+		chunk_appendf(&trash, "\t\t %-15d %-15d", child->ipc_fd[0], child->ipc_fd[1]);
+	chunk_appendf(&trash, "\n");
+	ha_free(&uptime);
+}
 
 /*  Displays workers and processes  */
 static int cli_io_handler_show_proc(struct appctx *appctx)
@@ -567,6 +589,8 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 	struct mworker_proc *child;
 	int old = 0;
 	int up = date.tv_sec - proc_self->timestamp;
+	struct cli_showproc_ctx *ctx = appctx->svcctx;
+	
 	char *uptime = NULL;
 	char *reloadtxt = NULL;
 
@@ -575,20 +599,31 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 
 	chunk_reset(&trash);
 
-	memprintf(&reloadtxt, "%d [failed: %d]", proc_self->reloads, proc_self->failedreloads);
-	chunk_printf(&trash, "#%-14s %-15s %-15s %-15s %-15s\n", "<PID>", "<type>", "<reloads>", "<uptime>", "<version>");
-	memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
-	chunk_appendf(&trash, "%-15u %-15s %-15s %-15s %-15s\n", (unsigned int)getpid(), "master", reloadtxt, uptime, haproxy_version);
+	if (ctx->next_reload == 0) {
+		memprintf(&reloadtxt, "%d [failed: %d]", proc_self->reloads, proc_self->failedreloads);
+		chunk_printf(&trash, "#%-14s %-15s %-15s %-15s %-15s", "<PID>", "<type>", "<reloads>", "<uptime>", "<version>");
+		if (ctx->debug)
+			chunk_appendf(&trash, "\t\t %-15s %-15s", "<ipc_fd[0]>", "<ipc_fd[1]>");
+		chunk_appendf(&trash, "\n");
+
+		/* display the master only the first time */
+		memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
+		chunk_appendf(&trash, "%-15u %-15s %-15s %-15s %-15s", (unsigned int)getpid(), "master", reloadtxt, uptime, haproxy_version);
+		if (ctx->debug)
+			chunk_appendf(&trash, "\t\t %-15d %-15d", proc_self->ipc_fd[0], proc_self->ipc_fd[1]);
+		chunk_appendf(&trash, "\n");
+	}
 	ha_free(&reloadtxt);
 	ha_free(&uptime);
 
 	/* displays current processes */
-
-	chunk_appendf(&trash, "# workers\n");
+	if (ctx->next_reload == 0)
+		chunk_appendf(&trash, "# workers\n");
 	list_for_each_entry(child, &proc_list, list) {
-		up = date.tv_sec - child->timestamp;
-		if (up < 0) /* must never be negative because of clock drift */
-			up = 0;
+
+		/* don't display current worker if we only need the next ones */
+		if (ctx->next_reload != 0)
+			continue;
 
 		if (!(child->options & PROC_O_TYPE_WORKER))
 			continue;
@@ -597,82 +632,64 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 			old++;
 			continue;
 		}
-		memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
-		chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s\n", child->pid, "worker", child->reloads, uptime, child->version);
-		ha_free(&uptime);
+		cli_append_worker_row(ctx, child, date.tv_sec);
 	}
 
-	/* displays old processes */
-
-	if (old) {
-		char *msg = NULL;
+	if (applet_putchk(appctx, &trash) == -1)
+		return 0;
 
-		chunk_appendf(&trash, "# old workers\n");
+	/* displays old processes */
+	if (old || ctx->next_reload) { /* there's more */
+		if (ctx->next_reload == 0)
+			chunk_appendf(&trash, "# old workers\n");
 		list_for_each_entry(child, &proc_list, list) {
-			up = date.tv_sec - child->timestamp;
-			if (up <= 0) /* must never be negative because of clock drift */
-				up = 0;
+			/* If we're resuming, skip entries that were already printed (reload >= ctx->next_reload) */
+			if (ctx->next_reload && child->reloads < ctx->next_reload)
+				continue;
 
 			if (!(child->options & PROC_O_TYPE_WORKER))
 				continue;
 
 			if (child->options & PROC_O_LEAVING) {
-				memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
-				chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s\n", child->pid, "worker", child->reloads, uptime, child->version);
-				ha_free(&uptime);
+				cli_append_worker_row(ctx, child, date.tv_sec);
+
+				/* Try to flush so we can resume after this reload on next page if the buffer is full. */
+				if (applet_putchk(appctx, &trash) == -1) {
+					/* resume at this reload (exclude it on next pass) */
+					ctx->next_reload = child->reloads + 1; /* resume after entries >= this reload */
+					return 0;
+				}
+				chunk_reset(&trash);
 			}
 		}
-		free(msg);
 	}
 
-	/* displays external process */
-	chunk_appendf(&trash, "# programs\n");
-	old = 0;
-	list_for_each_entry(child, &proc_list, list) {
-		up = date.tv_sec - child->timestamp;
-		if (up < 0) /* must never be negative because of clock drift */
-			up = 0;
+	/* dump complete: reset resume cursor so next 'show proc' starts from the top */
+	ctx->next_reload = 0;
+	return 1;
+}
 
-		if (!(child->options & PROC_O_TYPE_PROG))
-			continue;
+/* reload the master process */
+static int cli_parse_show_proc(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct cli_showproc_ctx *ctx;
 
-		if (child->options & PROC_O_LEAVING) {
-			old++;
-			continue;
-		}
-		memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
-		chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s\n", child->pid, child->id, child->reloads, uptime, "-");
-		ha_free(&uptime);
-	}
+	ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
 
-	if (old) {
-		chunk_appendf(&trash, "# old programs\n");
-		list_for_each_entry(child, &proc_list, list) {
-			up = date.tv_sec - child->timestamp;
-			if (up < 0) /* must never be negative because of clock drift */
-				up = 0;
+	if (!cli_has_level(appctx, ACCESS_LVL_OPER))
+		return 1;
 
-			if (!(child->options & PROC_O_TYPE_PROG))
-				continue;
+	if (*args[2]) {
 
-			if (child->options & PROC_O_LEAVING) {
-				memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
-				chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s\n", child->pid, child->id, child->reloads, uptime, "-");
-				ha_free(&uptime);
-			}
-		}
+		if (strcmp(args[2], "debug") == 0)
+			ctx->debug = 1;
+		else
+			return cli_err(appctx, "'show proc' only supports 'debug' as argument\n");
 	}
 
-
-
-	if (applet_putchk(appctx, &trash) == -1)
-		return 0;
-
-	/* dump complete */
-	return 1;
+	return 0;
 }
 
-/* reload the master process */
 static int cli_parse_reload(char **args, char *payload, struct appctx *appctx, void *private)
 {
 	struct stconn *scb = NULL;
@@ -800,7 +817,7 @@ static struct cli_kw_list cli_kws = {{ },{
 	{ { "@<relative pid>", NULL }, "@<relative pid>                         : send a command to the <relative pid> process", NULL, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},
 	{ { "@!<pid>", NULL },         "@!<pid>                                 : send a command to the <pid> process", cli_parse_default, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
 	{ { "@master", NULL },         "@master                                 : send a command to the master process", cli_parse_default, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
-	{ { "show", "proc", NULL },    "show proc                               : show processes status", cli_parse_default, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},
+	{ { "show", "proc", NULL },    "show proc                               : show processes status", cli_parse_show_proc, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},
 	{ { "reload", NULL },          "reload                                  : achieve a soft-reload (-sf) of haproxy", cli_parse_reload, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
 	{ { "hard-reload", NULL },     "hard-reload                             : achieve a hard-reload (-st) of haproxy", cli_parse_reload, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
 	{ { "_loadstatus", NULL },     NULL,                                                             cli_parse_default, cli_io_handler_show_loadstatus, NULL, NULL, ACCESS_MASTER_ONLY},
-- 
2.43.7

