From 96f944b81615b39250faa88291db0d7c6b00645a Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 7 Apr 2026 11:36:19 +0200 Subject: [PATCH] Atomic print and re-print desc on status, flush before reboot Refactor print() to emit description + final status in a single call to cprintf(), preventing kernel messages from splitting the two parts. For two-phase print(-1,...) + print_result() sequences used by, e.g., run_interactive, save the last description and re-print it before the [ OK ] / [FAIL] output so the status is never left stranded on a blank line when command output or kernel messages have scrolled away the original description. Finally, add print_exit() which drains the console output buffer with tcdrain(2) and resets ANSI SGR attributes + cursor visibility before the kernel takes back the console on reboot/halt, preventing escape code leakage into bootloader or early-kernel output. Signed-off-by: Joachim Wiberg --- src/helpers.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++----- src/helpers.h | 1 + src/sig.c | 3 +++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/helpers.c b/src/helpers.c index 73a1e069..9eebf8c6 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -50,6 +50,7 @@ static pstyle_t progress_onoff = PROGRESS_DEFAULT; static pstyle_t progress_style = PROGRESS_DEFAULT; +static char last_desc[512]; /* saved description for re-print after interruption */ #ifndef HOSTNAME_PATH #define HOSTNAME_PATH "/etc/hostname" @@ -336,6 +337,8 @@ void printv(const char *fmt, va_list ap) len = print_timestamp(buf, sizeof(buf)); vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap); + strlcpy(last_desc, &buf[len], sizeof(last_desc)); /* save for re-print */ + if (progress_style == PROGRESS_CLASSIC) cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf))); else @@ -344,24 +347,65 @@ void printv(const char *fmt, va_list ap) void print(int rc, const char *fmt, ...) { + char buf[ttcols]; + size_t len; + if (progress_style == PROGRESS_SILENT) return; + buf[0] = 0; + len = print_timestamp(buf, sizeof(buf)); + if (fmt) { va_list ap; va_start(ap, fmt); - printv(fmt, ap); + vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap); va_end(ap); + strlcpy(last_desc, &buf[len], sizeof(last_desc)); + } else if (rc >= 0 && last_desc[0]) { + /* + * No new description, but we have one saved from an earlier + * print(-1, ...) call. Re-print it so the final status is not + * left stranded if command output or a kernel message scrolled + * away the original description line. + */ + strlcpy(&buf[len], last_desc, sizeof(buf) - len); + } else { + buf[0] = 0; } - if (rc < 0) + if (rc < 0) { + /* Pending state: show description with spinner, no final status yet */ + if (!buf[0]) + return; + delline(); + if (progress_style == PROGRESS_CLASSIC) + cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf))); + else + cprintf("\r\e[K%s%s", status(3), buf); return; + } - if (progress_style == PROGRESS_CLASSIC) - cprintf("%s\n", status(rc)); - else - cprintf("\r%s\n", status(rc)); + /* + * Final status. Emit description + status in a single cprintf() so + * both reach the console in one write(), preventing kernel messages or + * command output from splitting the description from its [ OK ]/[FAIL]. + */ + last_desc[0] = 0; + + if (buf[0]) { + delline(); + if (progress_style == PROGRESS_CLASSIC) + cprintf("\r%s %s\n", pad(buf, sizeof(buf), ".", sizeof(buf)), status(rc)); + else + cprintf("\r\e[K%s%s\r%s\n", status(3), buf, status(rc)); + } else { + if (progress_style == PROGRESS_CLASSIC) + cprintf("%s\n", status(rc)); + else + cprintf("\r%s\n", status(rc)); + } } void print_desc(char *action, char *desc) @@ -375,6 +419,18 @@ int print_result(int fail) return fail; } +/* + * Reset console state and drain the output buffer before kernel takeover. + * Called just before reboot()/halt() to prevent ANSI escape codes from + * leaking into bootloader or early-kernel output. + */ +void print_exit(void) +{ + tcdrain(STDERR_FILENO); + dprint(STDERR_FILENO, "\e[0m\e[?25h", 10); /* reset SGR, show cursor */ + tcdrain(STDERR_FILENO); +} + void set_hostname(char **hostname) { FILE *fp; diff --git a/src/helpers.h b/src/helpers.h index 2d380870..442d75dc 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -68,6 +68,7 @@ void printv (const char *fmt, va_list ap); void print (int action, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void print_desc (char *action, char *desc); int print_result (int fail); +void print_exit (void); void set_hostname (char **hostname); void networking (int updown); diff --git a/src/sig.c b/src/sig.c index 38f16608..204cefbc 100644 --- a/src/sig.c +++ b/src/sig.c @@ -424,14 +424,17 @@ void do_shutdown(shutop_t op) } print(0, "Rebooting ..."); + print_exit(); reboot(RB_AUTOBOOT); } else if (op == SHUT_OFF) { print(0, "Powering down ..."); + print_exit(); reboot(RB_POWER_OFF); } /* Also fallback if any of the other two fails */ print(0, "Halting ..."); + print_exit(); reboot(RB_HALT_SYSTEM); }