diff --git a/src/basic/efivars.c b/src/basic/efivars.c index a655382af8..0eeee1038e 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -319,20 +319,26 @@ int efi_set_variable_string(const char *variable, const char *value) { return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t)); } -bool is_efi_boot(void) { - static int cache = -1; +static int cache_efi_boot = -1; - if (cache < 0) { - if (detect_container() > 0) - cache = false; - else { - cache = access("/sys/firmware/efi/", F_OK) >= 0; - if (!cache && errno != ENOENT) - log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m"); - } +bool set_efi_boot(bool b) { + return (cache_efi_boot = b); +} + +bool is_efi_boot(void) { + if (cache_efi_boot >= 0) + return cache_efi_boot; + + if (detect_container() > 0) + return (cache_efi_boot = false); + + if (access("/sys/firmware/efi/", F_OK) < 0) { + if (errno != ENOENT) + log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m"); + return (cache_efi_boot = false); } - return cache; + return (cache_efi_boot = true); } static int read_flag(const char *variable) { diff --git a/src/basic/efivars.h b/src/basic/efivars.h index eeccbb1806..22e0eab9a0 100644 --- a/src/basic/efivars.h +++ b/src/basic/efivars.h @@ -45,6 +45,7 @@ int efi_get_variable_path(const char *variable, char **ret); int efi_set_variable(const char *variable, const void *value, size_t size) _nonnull_if_nonzero_(2, 3); int efi_set_variable_string(const char *variable, const char *value); +bool set_efi_boot(bool b); bool is_efi_boot(void); bool is_efi_secure_boot(void); SecureBootMode efi_get_secure_boot_mode(void); @@ -71,6 +72,10 @@ static inline int efi_set_variable_string(const char *variable, const char *p) { return -EOPNOTSUPP; } +static inline bool set_efi_boot(bool b) { + return false; +} + static inline bool is_efi_boot(void) { return false; } diff --git a/src/bootctl/bootctl-random-seed.c b/src/bootctl/bootctl-random-seed.c index f6f41dc226..62ff8fa07f 100644 --- a/src/bootctl/bootctl-random-seed.c +++ b/src/bootctl/bootctl-random-seed.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "bootctl.h" #include "bootctl-random-seed.h" +#include "bootctl-util.h" #include "efivars.h" #include "env-util.h" #include "fd-util.h" diff --git a/src/bootctl/bootctl-reboot-to-firmware.c b/src/bootctl/bootctl-reboot-to-firmware.c index 9b85f02232..be20a4a132 100644 --- a/src/bootctl/bootctl-reboot-to-firmware.c +++ b/src/bootctl/bootctl-reboot-to-firmware.c @@ -6,6 +6,7 @@ #include "sd-varlink.h" #include "bootctl-reboot-to-firmware.h" +#include "bootctl-util.h" #include "efi-api.h" #include "errno-util.h" #include "log.h" @@ -14,6 +15,10 @@ int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) { int r; + r = verify_touch_variables_allowed(argv[0]); + if (r < 0) + return r; + if (argc < 2) { r = efi_get_reboot_to_firmware(); if (r > 0) { diff --git a/src/bootctl/bootctl-set-efivar.c b/src/bootctl/bootctl-set-efivar.c index 81eb354aea..bb853d65af 100644 --- a/src/bootctl/bootctl-set-efivar.c +++ b/src/bootctl/bootctl-set-efivar.c @@ -1,18 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include "alloc-util.h" #include "bootctl.h" #include "bootctl-set-efivar.h" +#include "bootctl-util.h" #include "efi-loader.h" #include "efivars.h" #include "log.h" #include "stdio-util.h" #include "time-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 buf[DECIMAL_STR_MAX(usec_t)]; @@ -133,37 +132,9 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target int verb_set_efivar(int argc, char *argv[], void *userdata) { int r; - /* Note: changing EFI variables is the primary purpose of these verbs, hence unlike in the other - * verbs that might touch EFI variables where we skip things gracefully, here we fail loudly if we - * are not run on EFI or EFI variable modifications were turned off. */ - - if (arg_touch_variables < 0) { - if (arg_root) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "Acting on %s, refusing EFI variable setup.", - arg_image ? "image" : "root directory"); - - if (detect_container() > 0) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "'%s' operation not supported in a container.", - argv[0]); - if (!is_efi_boot()) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "Not booted with UEFI."); - - if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("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]); - } - - } else if (!arg_touch_variables) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "'%s' operation cannot be combined with --variables=no.", - argv[0]); + r = verify_touch_variables_allowed(argv[0]); + if (r < 0) + return r; const char *variable; int (* arg_parser)(const char *, char16_t **, size_t *); diff --git a/src/bootctl/bootctl-status.c b/src/bootctl/bootctl-status.c index 1b471816cf..0804070d24 100644 --- a/src/bootctl/bootctl-status.c +++ b/src/bootctl/bootctl-status.c @@ -38,38 +38,51 @@ static int status_entries( printf("%sBoot Loader Entry Locations:%s\n", ansi_underline(), ansi_normal()); - printf(" ESP: %s (", esp_path); - if (!sd_id128_is_null(esp_partition_uuid)) - printf("/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "", + bool need_paren = false; + printf(" ESP: %s", esp_path); + if (!sd_id128_is_null(esp_partition_uuid)) { + printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "", SD_ID128_FORMAT_VAL(esp_partition_uuid)); - if (!xbootldr_path) + need_paren = true; + } + if (!xbootldr_path) { + if (!need_paren) { + fputs(" (", stdout); + need_paren = true; + } else + fputs(", ", stdout); + /* ESP is $BOOT if XBOOTLDR not present. */ - printf(", %s$BOOT%s", ansi_green(), ansi_normal()); - printf(")"); + printf("%s$BOOT%s", ansi_green(), ansi_normal()); + } + if (need_paren) + putchar(')'); + putchar('\n'); if (config->loader_conf_status != 0) { assert(esp_path); - printf("\n config: %s%s/%s%s", + printf(" config: %s%s/%s%s", ansi_grey(), esp_path, ansi_normal(), "/loader/loader.conf"); if (config->loader_conf_status < 0) printf(": %s%s%s", config->loader_conf_status == -ENOENT ? ansi_grey() : ansi_highlight_yellow(), STRERROR(config->loader_conf_status), ansi_normal()); + putchar('\n'); } if (xbootldr_path) { - printf("\n XBOOTLDR: %s (", xbootldr_path); + printf(" XBOOTLDR: %s (", xbootldr_path); if (!sd_id128_is_null(xbootldr_partition_uuid)) printf("/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ", ", SD_ID128_FORMAT_VAL(xbootldr_partition_uuid)); /* XBOOTLDR is always $BOOT if present. */ - printf("%s$BOOT%s)", ansi_green(), ansi_normal()); + printf("%s$BOOT%s)\n", ansi_green(), ansi_normal()); } if (settle_entry_token() >= 0) - printf("\n token: %s", arg_entry_token); - printf("\n\n"); + printf(" token: %s\n", arg_entry_token); + putchar('\n'); if (config->default_entry < 0) printf("%zu entries, no entry could be determined as default.\n", config->n_entries); @@ -318,6 +331,8 @@ int verb_status(int argc, char *argv[], void *userdata) { dev_t esp_devid = 0, xbootldr_devid = 0; int r, k; + bool has_efi = touch_variables(); + r = acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, /* ret_part= */ NULL, @@ -359,13 +374,14 @@ int verb_status(int argc, char *argv[], void *userdata) { pager_open(arg_pager_flags); - if (arg_root) - log_debug("Skipping 'System' section, operating offline."); - else if (!is_efi_boot()) - printf("%sSystem:%s\n" - "Not booted with EFI\n\n", - ansi_underline(), ansi_normal()); - else { + if (!has_efi) { + if (arg_root) + log_debug("Skipping 'System' section, operating offline."); + else + printf("%sSystem:%s\n" + "Not booted with EFI\n\n", + ansi_underline(), ansi_normal()); + } else { static const struct { uint64_t flag; const char *name; @@ -584,7 +600,7 @@ int verb_status(int argc, char *argv[], void *userdata) { if (arg_esp_path) RET_GATHER(r, status_binaries(arg_esp_path, esp_uuid)); - if (!arg_root && is_efi_boot()) + if (has_efi) RET_GATHER(r, status_variables()); if (arg_esp_path || arg_xbootldr_path) { @@ -615,6 +631,8 @@ int verb_list(int argc, char *argv[], void *userdata) { * Here we're interested in the latter but not the former, hence request the mode, and log about * EACCES. */ + (void) touch_variables(); + r = acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid); if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */ return log_error_errno(r, "Failed to determine ESP location: %m"); diff --git a/src/bootctl/bootctl-util.c b/src/bootctl/bootctl-util.c index 071accb77d..9ed31372bf 100644 --- a/src/bootctl/bootctl-util.c +++ b/src/bootctl/bootctl-util.c @@ -2,17 +2,86 @@ #include #include +#include #include "alloc-util.h" #include "boot-entry.h" #include "bootctl.h" #include "bootctl-util.h" +#include "efivars.h" #include "errno-util.h" #include "fileio.h" #include "log.h" #include "stat-util.h" #include "string-util.h" #include "sync-util.h" +#include "virt.h" + +bool touch_variables(void) { + /* If we run in a container or on a non-EFI system, automatically turn off EFI file system access, + * unless explicitly overridden. */ + + if (arg_touch_variables >= 0) + return set_efi_boot(arg_touch_variables); + + if (arg_root) { + log_once(LOG_NOTICE, + "Operating on %s, skipping EFI variable modifications.", + arg_image ? "image" : "root directory"); + return set_efi_boot(false); + } + + if (!is_efi_boot()) { /* NB: this internally checks if we run in a container */ + log_once(LOG_NOTICE, + "Not booted with EFI or running in a container, skipping EFI variable modifications."); + return false; + } + + return true; +} + +int verify_touch_variables_allowed(const char *command) { + /* Note: changing EFI variables is the primary purpose of these verbs, hence unlike in the other + * verbs that might touch EFI variables where we skip things gracefully, here we fail loudly if we + * are not run on EFI or EFI variable modifications were turned off. */ + + if (arg_touch_variables > 0) { + /* If we explicitly allowed to touch EFI variables, then skip the is_efi_boot() checks used + * at various places. */ + set_efi_boot(true); + return 0; + } + + if (arg_touch_variables == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "'%s' operation cannot be combined with --variables=no.", + command); + + if (arg_root) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Acting on %s, refusing EFI variable setup.", + arg_image ? "image" : "root directory"); + + if (detect_container() > 0) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "'%s' operation not supported in a container.", + command); + + if (!is_efi_boot()) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Not booted with UEFI."); + + if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("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", command); + } + + return 0; +} int sync_everything(void) { int r = 0, k; diff --git a/src/bootctl/bootctl-util.h b/src/bootctl/bootctl-util.h index 4abd4a303b..25b4451d92 100644 --- a/src/bootctl/bootctl-util.h +++ b/src/bootctl/bootctl-util.h @@ -1,6 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + +bool touch_variables(void); +int verify_touch_variables_allowed(const char *command); + int sync_everything(void); const char* get_efi_arch(void); diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c index b69720d66c..da4592a724 100644 --- a/src/bootctl/bootctl.c +++ b/src/bootctl/bootctl.c @@ -145,8 +145,8 @@ int acquire_xbootldr( int r; r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid); - if (r == -ENOKEY) { - log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT."); + if (r == -ENOKEY || path_equal(np, arg_esp_path)) { + log_debug("Didn't find an XBOOTLDR partition, using the ESP as $BOOT."); arg_xbootldr_path = mfree(arg_xbootldr_path); if (ret_uuid) @@ -229,29 +229,6 @@ static int print_loader_or_stub_path(void) { return 0; } -bool touch_variables(void) { - /* If we run in a container or on a non-EFI system, automatically turn off EFI file system access, - * unless explicitly overridden. */ - - if (arg_touch_variables >= 0) - return arg_touch_variables; - - if (arg_root) { - log_once(LOG_NOTICE, - "Operating on %s, skipping EFI variable modifications.", - arg_image ? "image" : "root directory"); - return false; - } - - if (!is_efi_boot()) { /* NB: this internally checks if we run in a container */ - log_once(LOG_NOTICE, - "Not booted with EFI or running in a container, skipping EFI variable modifications."); - return false; - } - - return true; -} - GracefulMode arg_graceful(void) { static bool chroot_checked = false; @@ -524,6 +501,11 @@ static int parse_argv(int argc, char *argv[]) { r = parse_tristate_argument_with_auto("--variables=", optarg, &arg_touch_variables); if (r < 0) return r; +#if !ENABLE_EFI + if (arg_touch_variables > 0) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Compiled without support for EFI, --variables=%s cannot be specified.", optarg); +#endif break; case ARG_NO_VARIABLES: diff --git a/src/bootctl/bootctl.h b/src/bootctl/bootctl.h index 93a1ca733b..d979411c78 100644 --- a/src/bootctl/bootctl.h +++ b/src/bootctl/bootctl.h @@ -59,8 +59,6 @@ GracefulMode arg_graceful(void); int acquire_esp(int unprivileged_mode, bool graceful, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid); int acquire_xbootldr(int unprivileged_mode, sd_id128_t *ret_uuid, dev_t *ret_devid); -bool touch_variables(void); - /* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description * stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this * string, but we limit the length to something reasonable to prevent from the firmware