From 636f6678ed888b3fea31ebb74ad662df3ae9dd10 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Thu, 2 Jan 2020 14:54:03 +0100 Subject: [PATCH] =?UTF-8?q?Introducing=20Finit=20progress=20=F0=9D=93=9C?= =?UTF-8?q?=F0=9D=93=B8=F0=9D=93=AD=F0=9D=93=AE=F0=9D=93=BB=F0=9D=93=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Joachim Nilsson --- configure.ac | 21 +++++-- src/finit.c | 6 +- src/helpers.c | 157 ++++++++++++++++++++++++++++++++++++++++---------- src/helpers.h | 1 + 4 files changed, 144 insertions(+), 41 deletions(-) diff --git a/configure.ac b/configure.ac index b9a4e35b..16bf09cd 100644 --- a/configure.ac +++ b/configure.ac @@ -62,9 +62,13 @@ AC_ARG_ENABLE(rw-rootfs, enable_rw_rootfs=no]) AC_ARG_ENABLE(progress, - AS_HELP_STRING([--enable-progress], [Pre Finit v3 bootup progress and heading]),,[ + AS_HELP_STRING([--enable-progress], [Modern progress display at bootup]),,[ enable_progress=no]) +AC_ARG_ENABLE(progress-classic, + AS_HELP_STRING([--enable-progress-classic], [Pre Finit v3 bootup progress and heading]),,[ + enable_progress_classic=no]) + AC_ARG_ENABLE(watchdog, AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[ enable_watchdog=no]) @@ -154,9 +158,17 @@ AS_IF([test "x$enable_rw_rootfs" = "xyes"], [ AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])]) AS_IF([test "x$enable_progress" = "xyes"], [ - AC_DEFINE(SILENT_MODE, 0, [Pre Finit v3 bootup messages and heading]) + AC_DEFINE(PROGRESS_STYLE, 2, [Modern bootup messages, systemd inspired])]) + +AS_IF([test "x$enable_progress_classic" = "xyes"], [ + enable_progress=no + AC_DEFINE(PROGRESS_STYLE, 1, [Pre Finit v3 bootup messages and heading])]) + +AS_IF([test "x$enable_progress_classic" = "xyes" -o "x$enable_progress" = "xyes"], [ + AC_DEFINE(SILENT_MODE, 0, [Display bootup progress, see above for style]) ], [ - AC_DEFINE(SILENT_MODE, 1, [Completely silent until login prompt])]) + AC_DEFINE(PROGRESS_STYLE, 0, [Default progress style]) + AC_DEFINE(SILENT_MODE, 1, [Completely silent until login prompt (default)])]) AS_IF([test "x$enable_watchdog" = "xyes"], [ AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])]) @@ -275,7 +287,8 @@ Optional features: Emergency shell.......: $enable_emergency_shell Fallback shell........: $enable_fallback_shell Remount / RW at boot..: $enable_rw_rootfs - Traditional progress..: $enable_progress + Modern progress.......: $enable_progress + Classic progress......: $enable_progress_classic Default console dev...: $console Default hostname......: $hostname Default runlevel......: $runlevel diff --git a/src/finit.c b/src/finit.c index 9e34b09f..d4a710d8 100644 --- a/src/finit.c +++ b/src/finit.c @@ -73,9 +73,6 @@ static int udev = 0; /* Runtime detection of udev */ */ static void banner(void) { - char *buf = INIT_HEADING; - char separator[SCREEN_WIDTH]; - if (plugin_exists(HOOK_BANNER)) { plugin_run_hooks(HOOK_BANNER); return; @@ -84,8 +81,7 @@ static void banner(void) if (log_is_silent()) return; - memset(separator, '=', sizeof(separator)); - fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator); + print_banner(INIT_HEADING); } static int ismnt(char *file, char *dir) diff --git a/src/helpers.c b/src/helpers.c index 52ab145c..dd56c95e 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -39,6 +39,30 @@ #include "util.h" #include "utmp-api.h" +static int progress_style = PROGRESS_STYLE; + +/* + * Note: the pending status (⋯) must be last item. Also, ⋯ is not + * off-by-one, it's a multi-byte character. + */ +#define STATUS_CLASS { \ + CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \ + CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \ + CHOOSE("WARN", "WARN", "\e[1;31m"), /* Red */ \ + CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \ +} + +#define CHOOSE(x,y,z) x +static const char *status1[] = STATUS_CLASS; +#undef CHOOSE + +#define CHOOSE(x,y,z) y +static const char *status2[] = STATUS_CLASS; +#undef CHOOSE + +#define CHOOSE(x,y,z) z +static const char *color[] = STATUS_CLASS; + char *strip_line(char *line) { @@ -57,6 +81,74 @@ char *strip_line(char *line) return line; } +/* + * Return screen length of string, not counting escape chars, and + * accounting for unicode characters as only one screen byte wide + */ +size_t slen(char *string) +{ + unsigned char *buf = (unsigned char *)string; + size_t len = 0; + + while (*buf) { + /* Skip ANSI escape sequences */ + if (*buf == '\e') { + while (*buf && !isalpha(*buf)) + buf++; + continue; + } + /* Skip 3-byte unicode */ + if (*buf == 0xe2) { + for (int cnt = 3; *buf && cnt >= 0; cnt--) + buf++; + continue; + } + + len++; + buf++; + } + + return len; +} + +static char *pad(char *buf, size_t len, char ch, size_t width) +{ + size_t pos = strlen(buf); + size_t i = slen(buf); + + if (pos < len) + buf[pos++] = ' '; + + while (i < width - 8 && pos < len) { + buf[pos++] = ch; + i++; + } + + return buf; +} + +void print_banner(const char *heading) +{ + char buf[SCREEN_WIDTH + 10]; + + memset(buf, 0, sizeof(buf)); + strlcat(buf, "\r\e[2K", sizeof(buf)); + if (progress_style == 1) { + strlcat(buf, "\e[1m", sizeof(buf)); + strlcat(buf, heading, sizeof(buf)); + pad(buf, sizeof(buf), '=', SCREEN_WIDTH - 2); + } else { + size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH; + + strlcat(buf, "\e[1;31m⏺ \e[1;33m⏺ \e[1;32m⏺ \e[0m\e[1m ", sizeof(buf)); + strlcat(buf, heading, sizeof(buf)); + pad(buf, sizeof(buf), '=', wmax + 8); + } + strlcat(buf, "\e[0m\n", sizeof(buf)); + + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + static size_t print_timestamp(char *buf, size_t len) { #if defined(CONFIG_PRINTK_TIME) @@ -77,6 +169,26 @@ static size_t print_timestamp(char *buf, size_t len) #endif } +static char *status(int rc) +{ + static char buf[64]; + + if (rc < 0 || rc >= (int)NELEMS(status1)) + rc = NELEMS(status1) - 1; /* Default to "⋯" (pending) */ + + if (progress_style == 1) { + int hl = 1; + + if (rc == 1 || rc == 2) + hl = 7; + + snprintf(buf, sizeof(buf), "\e[%dm[%s]\e[0m", hl, status1[rc]); + } else + snprintf(buf, sizeof(buf), "\e[1m[%s%s\e[0m\e[1m]\e[0m ", color[rc], status2[rc]); + + return buf; +} + void printv(const char *fmt, va_list ap) { char buf[SCREEN_WIDTH]; @@ -91,51 +203,32 @@ void printv(const char *fmt, va_list ap) len = print_timestamp(buf, sizeof(buf)); vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap); - len = strlen(buf); - buf[len++] = ' '; - for (size_t i = len; i < (sizeof(buf) - 8); i++) - buf[i] = '.'; /* pad with dots. */ - - fprintf(stderr, "\r%s ", buf); + if (progress_style == 1) + fprintf(stderr, "\r%s ", pad(buf, sizeof(buf), '.', sizeof(buf))); + else + fprintf(stderr, "\r\e[2K%s%s", status(3), buf); } -void print(int action, const char *fmt, ...) +void print(int rc, const char *fmt, ...) { - va_list ap; - const char success[] = "\e[1m[ OK ]\e[0m\n"; - const char failure[] = "\e[7m[FAIL]\e[0m\n"; - const char warning[] = "\e[7m[WARN]\e[0m\n"; - const char pending[] = "\e[1m[ \\/ ]\e[0m\n"; - if (log_is_silent()) return; if (fmt) { + va_list ap; + va_start(ap, fmt); printv(fmt, ap); va_end(ap); } - switch (action) { - case -1: - break; + if (rc < 0) + return; - case 0: - write(STDERR_FILENO, success, sizeof(success)); - break; - - case 1: - write(STDERR_FILENO, failure, sizeof(failure)); - break; - - case 2: - write(STDERR_FILENO, warning, sizeof(warning)); - break; - - default: - write(STDERR_FILENO, pending, sizeof(pending)); - break; - } + if (progress_style == 1) + fprintf(stderr, "%s\n", status(rc)); + else + fprintf(stderr, ".\r%s\n", status(rc)); } void print_desc(char *action, char *desc) diff --git a/src/helpers.h b/src/helpers.h index 442bf88e..dceccf7b 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -57,6 +57,7 @@ int sh (char *tty); int stty (int fd, speed_t speed); speed_t stty_parse_speed(char *baud); +void print_banner (const char *heading); void printv (const char *fmt, va_list ap); void print (int action, const char *fmt, ...); void print_desc (char *action, char *desc);