main: set $COLUMNS/$ROWS for PID 1 based on /dev/console data
authorLennart Poettering <lennart@poettering.net>
Wed, 17 Jul 2024 15:47:41 +0000 (17:47 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 19 Jul 2024 09:44:04 +0000 (11:44 +0200)
In PID 1 we write status information to /dev/console regularly, but we
cannot keep it open continously, due to the kernel's SAK logic (which
would kill PID 1 if user hits SAK). But closing/reopening it all the
time really sucks for tty types that have no window size management
(such as serial terminals/hvc0 and suchlike), because it also means the
TTY is fully closed most of the time, and that resets the window sizes
to 0/0.

Now, we reinitialize the window size on every reopen, but that is a bit
expensive for simple status output. Hence, cache the window size in the
usualy $COLUMNS/$ROWS environment variables. We don't inherit these to
our payloads anyway, hence these are free to us to use.

src/core/main.c

index bc2476ed3a56908af861d4bea84b1db04fe50c71..e5c2dc914e53e2813665d078097395d554fe3a48 100644 (file)
@@ -204,6 +204,43 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) {
         return 0;
 }
 
+static int save_console_winsize_in_environment(int tty_fd) {
+        int r;
+
+        assert(tty_fd >= 0);
+
+        struct winsize ws = {};
+        if (ioctl(tty_fd, TIOCGWINSZ, &ws) < 0) {
+                log_debug_errno(errno, "Failed to acquire console window size, ignoring.");
+                goto unset;
+        }
+
+        if (ws.ws_col <= 0 && ws.ws_row <= 0) {
+                log_debug("No console window size set, ignoring.");
+                goto unset;
+        }
+
+        r = setenvf("COLUMNS", /* overwrite= */ true, "%u", ws.ws_col);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to set $COLUMNS, ignoring: %m");
+                goto unset;
+        }
+
+        r = setenvf("LINES", /* overwrite= */ true, "%u", ws.ws_row);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to set $LINES, ignoring: %m");
+                goto unset;
+        }
+
+        log_debug("Recorded console dimensions in environment: $COLUMNS=%u $LINES=%u.", ws.ws_col, ws.ws_row);
+        return 1;
+
+unset:
+        (void) unsetenv("COLUMNS");
+        (void) unsetenv("LINES");
+        return 0;
+}
+
 static int console_setup(void) {
 
         if (getpid_cached() != 1)
@@ -217,6 +254,9 @@ static int console_setup(void) {
 
         /* We don't want to force text mode. Plymouth may be showing pictures already from initrd. */
         reset_dev_console_fd(tty_fd, /* switch_to_text= */ false);
+
+        save_console_winsize_in_environment(tty_fd);
+
         return 0;
 }