From 6d8ca2576629edb2f492f2479dfd0b97d37bac11 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 16 Dec 2022 18:51:10 +0100 Subject: [PATCH] bootctl: split out "set-efivar" verbs, too --- meson.build | 2 + src/boot/bootctl-set-efivar.c | 149 ++++++++++++++++++++++++++++++++++ src/boot/bootctl-set-efivar.h | 4 + src/boot/bootctl.c | 139 +------------------------------ 4 files changed, 156 insertions(+), 138 deletions(-) create mode 100644 src/boot/bootctl-set-efivar.c create mode 100644 src/boot/bootctl-set-efivar.h diff --git a/meson.build b/meson.build index d30331e72a9..9155ec4f789 100644 --- a/meson.build +++ b/meson.build @@ -2611,6 +2611,8 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1 'src/boot/bootctl-random-seed.h', 'src/boot/bootctl-reboot-to-firmware.c', 'src/boot/bootctl-reboot-to-firmware.h', + 'src/boot/bootctl-set-efivar.c', + 'src/boot/bootctl-set-efivar.h', 'src/boot/bootctl-systemd-efi-options.c', 'src/boot/bootctl-systemd-efi-options.h', 'src/boot/bootctl-util.c', diff --git a/src/boot/bootctl-set-efivar.c b/src/boot/bootctl-set-efivar.c new file mode 100644 index 00000000000..cbf92caaafd --- /dev/null +++ b/src/boot/bootctl-set-efivar.c @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "bootctl.h" +#include "bootctl-set-efivar.h" +#include "efivars.h" +#include "stdio-util.h" +#include "utf8.h" +#include "virt.h" + +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) { + char16_t *encoded = NULL; + int r; + + assert(arg1); + assert(ret_target); + assert(ret_target_size); + + if (streq(arg1, "@current")) { + r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size); + if (r < 0) + return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m"); + + } else if (streq(arg1, "@oneshot")) { + r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size); + if (r < 0) + return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m"); + + } else if (streq(arg1, "@default")) { + r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size); + if (r < 0) + return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m"); + + } else if (arg1[0] == '@' && !streq(arg1, "@saved")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1); + else { + encoded = utf8_to_utf16(arg1, strlen(arg1)); + if (!encoded) + return log_oom(); + + *ret_target = encoded; + *ret_target_size = char16_strlen(encoded) * 2 + 2; + } + + return 0; +} + +int verb_set_efivar(int argc, char *argv[], void *userdata) { + int r; + + if (arg_root) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Acting on %s, skipping EFI variable setup.", + arg_image ? "image" : "root directory"); + + if (!is_efi_boot()) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Not booted with UEFI."); + + if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) { + if (errno == ENOENT) { + log_error_errno(errno, "Not booted with a supported boot loader."); + return -EOPNOTSUPP; + } + + return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]); + } + + if (detect_container() > 0) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "'%s' operation not supported in a container.", + argv[0]); + + if (!arg_touch_variables) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "'%s' operation cannot be combined with --no-variables.", + argv[0]); + + 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 *value = NULL; + size_t value_size = 0; + + r = arg_parser(argv[1], &value, &value_size); + if (r < 0) + return r; + r = efi_set_variable(variable, value, value_size); + if (r < 0) + return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable); + } + + return 0; +} diff --git a/src/boot/bootctl-set-efivar.h b/src/boot/bootctl-set-efivar.h new file mode 100644 index 00000000000..6441681081a --- /dev/null +++ b/src/boot/bootctl-set-efivar.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_set_efivar(int argc, char *argv[], void *userdata); diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 52f4200e387..f1e05067073 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -18,6 +18,7 @@ #include "bootctl-random-seed.h" #include "bootctl-reboot-to-firmware.h" #include "bootctl-systemd-efi-options.h" +#include "bootctl-set-efivar.h" #include "bootctl-util.h" #include "bootspec.h" #include "build.h" @@ -2181,144 +2182,6 @@ static int verb_is_installed(int argc, char *argv[], void *userdata) { } } -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) { - char16_t *encoded = NULL; - int r; - - assert(arg1); - assert(ret_target); - assert(ret_target_size); - - if (streq(arg1, "@current")) { - r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size); - if (r < 0) - return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m"); - - } else if (streq(arg1, "@oneshot")) { - r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size); - if (r < 0) - return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m"); - - } else if (streq(arg1, "@default")) { - r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size); - if (r < 0) - return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m"); - - } else if (arg1[0] == '@' && !streq(arg1, "@saved")) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1); - else { - encoded = utf8_to_utf16(arg1, strlen(arg1)); - if (!encoded) - return log_oom(); - - *ret_target = encoded; - *ret_target_size = char16_strlen(encoded) * 2 + 2; - } - - return 0; -} - -static int verb_set_efivar(int argc, char *argv[], void *userdata) { - int r; - - if (arg_root) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "Acting on %s, skipping EFI variable setup.", - arg_image ? "image" : "root directory"); - - if (!is_efi_boot()) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "Not booted with UEFI."); - - if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) { - if (errno == ENOENT) { - log_error_errno(errno, "Not booted with a supported boot loader."); - return -EOPNOTSUPP; - } - - return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]); - } - - if (detect_container() > 0) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "'%s' operation not supported in a container.", - argv[0]); - - if (!arg_touch_variables) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "'%s' operation cannot be combined with --no-variables.", - argv[0]); - - 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 *value = NULL; - size_t value_size = 0; - - r = arg_parser(argv[1], &value, &value_size); - if (r < 0) - return r; - r = efi_set_variable(variable, value, value_size); - if (r < 0) - return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable); - } - - return 0; -} - static int bootctl_main(int argc, char *argv[]) { static const Verb verbs[] = { { "help", VERB_ANY, VERB_ANY, 0, help },