From aa27bec1943ac9e87e1b2e51d43e0b91e6105940 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Sep 2025 15:42:32 +0200 Subject: [PATCH] firstboot: optionally, don't query for keymap unless connected to a real VT The keymap only really matters if there's local access to a system, i.e. if there's actually a physical kbd directly connected to it, to apply it to. If during firstboot we are not talked to via a VT (but via SSH, container, or hypervisor console or so instead), then it's very unlikely we ever are. Hence, don't ask for a keymap, and let#s shortcut the questions asked at boot. --- man/systemd-firstboot.xml | 11 +++++++++ src/firstboot/firstboot.c | 29 +++++++++++++++++++++-- test/units/TEST-74-AUX-UTILS.firstboot.sh | 4 ++++ units/systemd-firstboot.service | 2 +- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index cc4ddc0cf6..26ce757174 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -271,6 +271,17 @@ + + + + If invoked from a virtual terminal TTY equivalent to + , otherwise has no effect. Or in other words, only prompts + interactively for a keymap if a local keyboard is used for interactivity that requires it. + + + + + diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 9637d9f8f6..9f237dc266 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -71,6 +71,7 @@ static char *arg_root_shell = NULL; static char *arg_kernel_cmdline = NULL; static bool arg_prompt_locale = false; static bool arg_prompt_keymap = false; +static bool arg_prompt_keymap_auto = false; static bool arg_prompt_timezone = false; static bool arg_prompt_hostname = false; static bool arg_prompt_root_password = false; @@ -415,7 +416,21 @@ static int prompt_keymap(int rfd, sd_varlink **mute_console_link) { return 0; } - if (!arg_prompt_keymap) { + bool b; + if (arg_prompt_keymap_auto) { + _cleanup_free_ char *ttyname = NULL; + + r = getttyname_harder(STDOUT_FILENO, &ttyname); + if (r < 0) { + log_debug_errno(r, "Cannot determine TTY we are connected, ignoring: %m"); + b = false; /* if we can't resolve this, it's probably not a VT */ + } else { + b = tty_is_vc_resolve(ttyname); + log_debug("Detected connection to local console: %s", yes_no(b)); + } + } else + b = arg_prompt_keymap; + if (!b) { log_debug("Prompting for keymap was not requested."); return 0; } @@ -1234,6 +1249,8 @@ static int help(void) { " Set kernel command line\n" " --prompt-locale Prompt the user for locale settings\n" " --prompt-keymap Prompt the user for keymap settings\n" + " --prompt-keymap-auto Prompt the user for keymap settings if invoked\n" + " on local console\n" " --prompt-timezone Prompt the user for timezone\n" " --prompt-hostname Prompt the user for hostname\n" " --prompt-root-password Prompt the user for root password\n" @@ -1284,6 +1301,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PROMPT, ARG_PROMPT_LOCALE, ARG_PROMPT_KEYMAP, + ARG_PROMPT_KEYMAP_AUTO, ARG_PROMPT_TIMEZONE, ARG_PROMPT_HOSTNAME, ARG_PROMPT_ROOT_PASSWORD, @@ -1323,6 +1341,7 @@ static int parse_argv(int argc, char *argv[]) { { "prompt", no_argument, NULL, ARG_PROMPT }, { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, + { "prompt-keymap-auto", no_argument, NULL, ARG_PROMPT_KEYMAP_AUTO }, { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, @@ -1480,6 +1499,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_PROMPT: arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = true; + arg_prompt_keymap_auto = false; break; case ARG_PROMPT_LOCALE: @@ -1488,6 +1508,11 @@ static int parse_argv(int argc, char *argv[]) { case ARG_PROMPT_KEYMAP: arg_prompt_keymap = true; + arg_prompt_keymap_auto = false; + break; + + case ARG_PROMPT_KEYMAP_AUTO: + arg_prompt_keymap_auto = true; break; case ARG_PROMPT_TIMEZONE: @@ -1668,7 +1693,7 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); if (r > 0 && !enabled) { log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts."); - arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false; + arg_prompt_locale = arg_prompt_keymap = arg_prompt_keymap_auto = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false; } } diff --git a/test/units/TEST-74-AUX-UTILS.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh index 5f7209231a..d6946ba4ee 100755 --- a/test/units/TEST-74-AUX-UTILS.firstboot.sh +++ b/test/units/TEST-74-AUX-UTILS.firstboot.sh @@ -221,6 +221,10 @@ grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then echo -ne "foo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" + + rm "$ROOT/etc/vconsole.conf" + # this should be a NOP, given that stdout is connected to /dev/null, and hence not a VT + systemd-firstboot --root="$ROOT" --prompt-keymap-auto > /dev/null fi echo -ne "Europe/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" diff --git a/units/systemd-firstboot.service b/units/systemd-firstboot.service index 5ba9c8ba03..fb1c59ad6f 100644 --- a/units/systemd-firstboot.service +++ b/units/systemd-firstboot.service @@ -32,7 +32,7 @@ Before=shutdown.target [Service] Type=oneshot RemainAfterExit=yes -ExecStart=systemd-firstboot --prompt-locale --prompt-keymap --prompt-timezone --prompt-root-password --mute-console=yes +ExecStart=systemd-firstboot --prompt-locale --prompt-keymap-auto --prompt-timezone --prompt-root-password --mute-console=yes StandardOutput=tty StandardInput=tty StandardError=tty