]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
execute: Add kernel cmdline arguments for tty term, rows and columns
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 19 Mar 2023 10:24:01 +0000 (11:24 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 21 Mar 2023 19:50:17 +0000 (20:50 +0100)
Let's allow configuring tty term and size using kernel cmdline arguments
so that when running in a VM we can communicate the terminal TERM and size
from the host via SMBIOS extra kernel cmdline arguments.

man/kernel-command-line.xml
src/core/execute.c

index 0528c4b67255abd6dae80d465ab72119fd6ff8e7..6f026318d8a2def6e32b91d0bae1dd14ddc4d11d 100644 (file)
         is set in <filename>/etc/hostname</filename>. Note that this does not bar later runtime changes to
         the hostname, it simply controls the initial hostname set during early boot.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.tty.term.<replaceable>tty</replaceable>=</varname></term>
+        <term><varname>systemd.tty.rows.<replaceable>tty</replaceable>=</varname></term>
+        <term><varname>systemd.tty.columns.<replaceable>tty</replaceable>=</varname></term>
+
+        <listitem><para>These arguments allow configuring default values for <varname>$TERM</varname>,
+        <varname>TTYRows=</varname>, and <varname>TTYColumns=</varname> for tty
+        <replaceable>tty</replaceable>. The tty name should be specified without the
+        <filename>/dev/</filename> prefix (e.g. <literal>systemd.tty.rows.ttyS0=80</literal>).
+        </para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 093d1ad5b474514cd96d69b180138ab3acf5e3b3..3f85b5f3cd6e54659589b667ba7b0dec7ce3a869 100644 (file)
@@ -80,6 +80,7 @@
 #include "namespace.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "proc-cmdline.h"
 #include "process-util.h"
 #include "psi-util.h"
 #include "random-util.h"
@@ -201,6 +202,66 @@ static const char *exec_context_tty_path(const ExecContext *context) {
         return "/dev/console";
 }
 
+static int exec_context_tty_size(const ExecContext *context, unsigned *ret_rows, unsigned *ret_cols) {
+        _cleanup_free_ char *rowskey = NULL, *rowsvalue = NULL, *colskey = NULL, *colsvalue = NULL;
+        unsigned rows, cols;
+        const char *tty;
+        int r;
+
+        assert(context);
+        assert(ret_rows);
+        assert(ret_cols);
+
+        rows = context->tty_rows;
+        cols = context->tty_cols;
+
+        tty = exec_context_tty_path(context);
+        if (!tty || (rows != UINT_MAX && cols != UINT_MAX)) {
+                *ret_rows = rows;
+                *ret_cols = cols;
+                return 0;
+        }
+
+        tty = skip_dev_prefix(tty);
+        if (!in_charset(tty, ALPHANUMERICAL)) {
+                log_debug("%s contains non-alphanumeric characters, ignoring", tty);
+                *ret_rows = rows;
+                *ret_cols = cols;
+                return 0;
+        }
+
+        rowskey = strjoin("systemd.tty.rows.", tty);
+        if (!rowskey)
+                return -ENOMEM;
+
+        colskey = strjoin("systemd.tty.columns.", tty);
+        if (!colskey)
+                return -ENOMEM;
+
+        r = proc_cmdline_get_key_many(/* flags = */ 0,
+                                      rowskey, &rowsvalue,
+                                      colskey, &colsvalue);
+        if (r < 0)
+                log_debug_errno(r, "Failed to read TTY size of %s from kernel cmdline, ignoring: %m", tty);
+
+        if (rows == UINT_MAX && rowsvalue) {
+                r = safe_atou(rowsvalue, &rows);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse %s=%s, ignoring: %m", rowskey, rowsvalue);
+        }
+
+        if (cols == UINT_MAX && colsvalue) {
+                r = safe_atou(colsvalue, &cols);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse %s=%s, ignoring: %m", colskey, colsvalue);
+        }
+
+        *ret_rows = rows;
+        *ret_cols = cols;
+
+        return 0;
+}
+
 static void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) {
         const char *path;
 
@@ -222,8 +283,12 @@ static void exec_context_tty_reset(const ExecContext *context, const ExecParamet
                         (void) reset_terminal(path);
         }
 
-        if (p && p->stdin_fd >= 0)
-                (void) terminal_set_size_fd(p->stdin_fd, path, context->tty_rows, context->tty_cols);
+        if (p && p->stdin_fd >= 0) {
+                unsigned rows = context->tty_rows, cols = context->tty_cols;
+
+                (void) exec_context_tty_size(context, &rows, &cols);
+                (void) terminal_set_size_fd(p->stdin_fd, path, rows, cols);
+        }
 
         if (context->tty_vt_disallocate && path)
                 (void) vt_disallocate(path);
@@ -481,9 +546,12 @@ static int setup_input(
 
                 /* Try to make this the controlling tty, if it is a tty, and reset it */
                 if (isatty(STDIN_FILENO)) {
+                        unsigned rows = context->tty_rows, cols = context->tty_cols;
+
+                        (void) exec_context_tty_size(context, &rows, &cols);
                         (void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
                         (void) reset_terminal_fd(STDIN_FILENO, true);
-                        (void) terminal_set_size_fd(STDIN_FILENO, NULL, context->tty_rows, context->tty_cols);
+                        (void) terminal_set_size_fd(STDIN_FILENO, NULL, rows, cols);
                 }
 
                 return STDIN_FILENO;
@@ -499,6 +567,7 @@ static int setup_input(
         case EXEC_INPUT_TTY:
         case EXEC_INPUT_TTY_FORCE:
         case EXEC_INPUT_TTY_FAIL: {
+                unsigned rows, cols;
                 int fd;
 
                 fd = acquire_terminal(exec_context_tty_path(context),
@@ -509,7 +578,11 @@ static int setup_input(
                 if (fd < 0)
                         return fd;
 
-                r = terminal_set_size_fd(fd, exec_context_tty_path(context), context->tty_rows, context->tty_cols);
+                r = exec_context_tty_size(context, &rows, &cols);
+                if (r < 0)
+                        return r;
+
+                r = terminal_set_size_fd(fd, exec_context_tty_path(context), rows, cols);
                 if (r < 0)
                         return r;
 
@@ -772,6 +845,7 @@ static int setup_confirm_stdio(
                 int *ret_saved_stdout) {
 
         _cleanup_close_ int fd = -EBADF, saved_stdin = -EBADF, saved_stdout = -EBADF;
+        unsigned rows, cols;
         int r;
 
         assert(ret_saved_stdin);
@@ -797,7 +871,11 @@ static int setup_confirm_stdio(
         if (r < 0)
                 return r;
 
-        r = terminal_set_size_fd(fd, vc, context->tty_rows, context->tty_cols);
+        r = exec_context_tty_size(context, &rows, &cols);
+        if (r < 0)
+                return r;
+
+        r = terminal_set_size_fd(fd, vc, rows, cols);
         if (r < 0)
                 return r;
 
@@ -1837,6 +1915,7 @@ static int build_environment(
         _cleanup_strv_free_ char **our_env = NULL;
         size_t n_env = 0;
         char *x;
+        int r;
 
         assert(u);
         assert(c);
@@ -1927,6 +2006,7 @@ static int build_environment(
         }
 
         if (exec_context_needs_term(c)) {
+                _cleanup_free_ char *cmdline = NULL;
                 const char *tty_path, *term = NULL;
 
                 tty_path = exec_context_tty_path(c);
@@ -1937,6 +2017,19 @@ static int build_environment(
 
                 if (path_equal_ptr(tty_path, "/dev/console") && getppid() == 1)
                         term = getenv("TERM");
+                else if (tty_path && in_charset(skip_dev_prefix(tty_path), ALPHANUMERICAL)) {
+                        _cleanup_free_ char *key = NULL;
+
+                        key = strjoin("systemd.tty.term.", skip_dev_prefix(tty_path));
+                        if (!key)
+                                return -ENOMEM;
+
+                        r = proc_cmdline_get_key(key, 0, &cmdline);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to read %s from kernel cmdline, ignoring: %m", key);
+                        else if (r > 0)
+                                term = cmdline;
+                }
 
                 if (!term)
                         term = default_term_for_tty(tty_path);