diff options
author | Jan Janssen <medhefgo@web.de> | 2021-10-04 11:25:50 +0200 |
---|---|---|
committer | Jan Janssen <medhefgo@web.de> | 2021-10-08 15:32:50 +0200 |
commit | 39ddc32a86d4aff15b07c8993d1cff0fe1fa4123 (patch) | |
tree | 101800490e350a4b88a8d0a03ef20acf25270204 | |
parent | sd-boot: Allow disabling timeout (diff) | |
download | systemd-39ddc32a86d4aff15b07c8993d1cff0fe1fa4123.tar.xz systemd-39ddc32a86d4aff15b07c8993d1cff0fe1fa4123.zip |
bootctl: Add set-timeout verb
Fixes: #18766
-rw-r--r-- | man/bootctl.xml | 15 | ||||
-rw-r--r-- | shell-completion/bash/bootctl | 2 | ||||
-rw-r--r-- | shell-completion/zsh/_bootctl | 2 | ||||
-rw-r--r-- | src/boot/bootctl.c | 71 |
4 files changed, 79 insertions, 11 deletions
diff --git a/man/bootctl.xml b/man/bootctl.xml index a958cde7df..73cacc6107 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -116,6 +116,21 @@ </para></listitem> </varlistentry> + <varlistentry> + <term><option>set-timeout</option> <replaceable>TIMEOUT</replaceable></term> + <term><option>set-timeout-oneshot</option> <replaceable>TIMEOUT</replaceable></term> + + <listitem><para>Sets the boot loader menu timeout in seconds. The <option>set-timeout-oneshot</option> + command will set the timeout only for the next boot. See + <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> + for details about the syntax of time spans.</para> + + <para>If this is set to <option>menu-hidden</option> or <option>0</option> no menu is shown and + the default entry will be booted immediately, while setting this to <option>menu-force</option> + disables the timeout while always showing the menu. When an empty string ("") is specified the + bootloader will revert to its default menu timeout.</para></listitem> + </varlistentry> + </variablelist> </refsect1> diff --git a/shell-completion/bash/bootctl b/shell-completion/bash/bootctl index e61188fee9..190e3d33f5 100644 --- a/shell-completion/bash/bootctl +++ b/shell-completion/bash/bootctl @@ -57,7 +57,7 @@ _bootctl() { local -A VERBS=( # systemd-efi-options takes an argument, but it is free-form, so we cannot complete it - [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list' + [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot' [BOOTENTRY]='set-default set-oneshot' [BOOLEAN]='reboot-to-firmware' ) diff --git a/shell-completion/zsh/_bootctl b/shell-completion/zsh/_bootctl index 2b50f307f1..87ecbe37c3 100644 --- a/shell-completion/zsh/_bootctl +++ b/shell-completion/zsh/_bootctl @@ -46,6 +46,8 @@ _bootctl_reboot-to-firmware() { "list:List boot loader entries" "set-default:Set the default boot loader entry" "set-oneshot:Set the default boot loader entry only for the next boot" + "set-timeout:Set the menu timeout" + "set-timeout-oneshot:Set the menu timeout for the next boot only" ) if (( CURRENT == 1 )); then _describe -t commands 'bootctl command' _bootctl_cmds || compadd "$@" diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index bb3627ed6e..99297fc072 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1100,6 +1100,9 @@ static int help(int argc, char *argv[], void *userdata) { " list List boot loader entries\n" " set-default ID Set default boot loader entry\n" " set-oneshot ID Set default boot loader entry, for next boot only\n" + " set-timeout SECONDS Set the menu timeout\n" + " set-timeout-oneshot SECONDS\n" + " Set the menu timeout for the next boot only\n" "\n%3$ssystemd-boot Commands:%4$s\n" " install Install systemd-boot to the ESP and EFI variables\n" " update Update systemd-boot in the ESP and EFI variables\n" @@ -1774,6 +1777,37 @@ static int verb_is_installed(int argc, char *argv[], void *userdata) { return EXIT_SUCCESS; } +static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) { + char utf8[DECIMAL_STR_MAX(usec_t)]; + char16_t *encoded; + usec_t timeout; + int r; + + assert(arg1); + assert(ret_timeout); + assert(ret_timeout_size); + + if (streq(arg1, "menu-force")) + timeout = USEC_INFINITY; + else if (streq(arg1, "menu-hidden")) + timeout = 0; + else { + r = parse_time(arg1, &timeout, USEC_PER_SEC); + if (r < 0) + return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1); + if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC) + log_warning("Timeout is too long and will be treated as 'menu-force' instead."); + } + + xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX)); + encoded = utf8_to_utf16(utf8, strlen(utf8)); + if (!encoded) + return log_oom(); + *ret_timeout = encoded; + *ret_timeout_size = char16_strlen(encoded) * 2 + 2; + return 0; +} + static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) { int r; if (streq(arg1, "@current")) { @@ -1799,7 +1833,7 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target return 0; } -static int verb_set_default(int argc, char *argv[], void *userdata) { +static int verb_set_efivar(int argc, char *argv[], void *userdata) { int r; if (!is_efi_boot()) @@ -1822,24 +1856,39 @@ static int verb_set_default(int argc, char *argv[], void *userdata) { if (!arg_touch_variables) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "'%s' operation cannot be combined with --touch-variables=no.", + "'%s' operation cannot be combined with --no-variables.", argv[0]); - const char *variable = streq(argv[0], "set-default") ? - EFI_LOADER_VARIABLE(LoaderEntryDefault) : EFI_LOADER_VARIABLE(LoaderEntryOneShot); + const char *variable; + int (* arg_parser)(const char *, char16_t **, size_t *); + + if (streq(argv[0], "set-default")) { + variable = EFI_LOADER_VARIABLE(LoaderEntryDefault); + arg_parser = parse_loader_entry_target_arg; + } else if (streq(argv[0], "set-oneshot")) { + variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot); + arg_parser = parse_loader_entry_target_arg; + } else if (streq(argv[0], "set-timeout")) { + variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout); + arg_parser = parse_timeout; + } else if (streq(argv[0], "set-timeout-oneshot")) { + variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot); + arg_parser = parse_timeout; + } else + assert_not_reached(); if (isempty(argv[1])) { r = efi_set_variable(variable, NULL, 0); if (r < 0 && r != -ENOENT) return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable); } else { - _cleanup_free_ char16_t *target = NULL; - size_t target_size = 0; + _cleanup_free_ char16_t *value = NULL; + size_t value_size = 0; - r = parse_loader_entry_target_arg(argv[1], &target, &target_size); + r = arg_parser(argv[1], &value, &value_size); if (r < 0) return r; - r = efi_set_variable(variable, target, target_size); + r = efi_set_variable(variable, value, value_size); if (r < 0) return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable); } @@ -1944,8 +1993,10 @@ static int bootctl_main(int argc, char *argv[]) { { "remove", VERB_ANY, 1, 0, verb_remove }, { "is-installed", VERB_ANY, 1, 0, verb_is_installed }, { "list", VERB_ANY, 1, 0, verb_list }, - { "set-default", 2, 2, 0, verb_set_default }, - { "set-oneshot", 2, 2, 0, verb_set_default }, + { "set-default", 2, 2, 0, verb_set_efivar }, + { "set-oneshot", 2, 2, 0, verb_set_efivar }, + { "set-timeout", 2, 2, 0, verb_set_efivar }, + { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar }, { "random-seed", VERB_ANY, 1, 0, verb_random_seed }, { "systemd-efi-options", VERB_ANY, 2, 0, verb_systemd_efi_options }, { "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware }, |