From: Lennart Poettering Date: Sat, 20 Sep 2025 06:38:51 +0000 (+0200) Subject: bootctl: optionally include backing disk name in efi boot option description X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91b3620b07f29342261a3cbdaaaa3f83f21895e1;p=thirdparty%2Fsystemd.git bootctl: optionally include backing disk name in efi boot option description --- diff --git a/man/bootctl.xml b/man/bootctl.xml index b317d840209..1e98b5ca7a6 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -524,6 +524,18 @@ + + + Takes a boolean, defaults to false. Controls whether to append disk model information + to the firmware boot option item description (as configured with + above). This is useful when installing multiple + operating systems on separate disks on the same system, as it ensures the firmware boot options are discernable + and give a hint which disk is booted into. Note that this uses hardware model information, and hence + might not be too useful in case multiple disks of an identical model are used. + + + + Dry run for and . diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index d6ce3471b11..976c5380e1f 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -3,9 +3,11 @@ #include #include +#include "sd-device.h" #include "sd-varlink.h" #include "alloc-util.h" +#include "blockdev-util.h" #include "boot-entry.h" #include "bootctl.h" #include "bootctl-install.h" @@ -1321,8 +1323,56 @@ static int remove_from_order(uint16_t slot) { return 0; } -static const char *pick_efi_boot_option_description(void) { - return arg_efi_boot_option_description ?: "Linux Boot Manager"; +static int pick_efi_boot_option_description(int esp_fd, char **ret) { + int r; + + assert(esp_fd >= 0); + assert(ret); + + /* early declarations, so that they are definitely initialized even if we follow any of the gotos */ + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *j = NULL; + + const char *b = arg_efi_boot_option_description ?: "Linux Boot Manager"; + if (!arg_efi_boot_option_description_with_device) + goto fallback; + + r = block_device_new_from_fd( + esp_fd, + BLOCK_DEVICE_LOOKUP_WHOLE_DISK|BLOCK_DEVICE_LOOKUP_BACKING, + &d); + if (r < 0) { + log_debug_errno(r, "Failed to find backing device of ESP: %m"); + goto fallback; + } + + const char *serial; + r = sd_device_get_property_value(d, "ID_SERIAL", &serial); + if (r < 0) { + log_debug_errno(r, "Unable to read ID_SERIAL field of backing device of ESP: %m"); + goto fallback; + } + + j = strjoin(b, " (", serial, ")"); + if (!j) + return log_oom(); + + if (strlen(j) > EFI_BOOT_OPTION_DESCRIPTION_MAX) { + log_debug("Boot option string suffixed with device serial would be too long, skipping: %s", j); + j = mfree(j); + goto fallback; + } + + *ret = TAKE_PTR(j); + return 0; + +fallback: + j = strdup(b); + if (!j) + return log_oom(); + + *ret = TAKE_PTR(j); + return 0; } static int install_variables( @@ -1369,9 +1419,15 @@ static int install_variables( bool existing = r > 0; if (c->operation == INSTALL_NEW || !existing) { + _cleanup_free_ char *description = NULL; + + r = pick_efi_boot_option_description(esp_fd, &description); + if (r < 0) + return r; + r = efi_add_boot_option( slot, - pick_efi_boot_option_description(), + description, c->esp_part, c->esp_pstart, c->esp_psize, @@ -1388,7 +1444,7 @@ static int install_variables( log_info("%s EFI boot entry \"%s\".", existing ? "Updated" : "Created", - pick_efi_boot_option_description()); + description); } return insert_into_order(c, slot); diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c index dee65d51d7f..8d3cabc8650 100644 --- a/src/bootctl/bootctl.c +++ b/src/bootctl/bootctl.c @@ -40,12 +40,6 @@ #include "verbs.h" #include "virt.h" -/* 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 - * having to deal with a potentially too long string. */ -#define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255) - static GracefulMode _arg_graceful = ARG_GRACEFUL_NO; char *arg_esp_path = NULL; @@ -70,6 +64,7 @@ char *arg_root = NULL; char *arg_image = NULL; InstallSource arg_install_source = INSTALL_SOURCE_AUTO; char *arg_efi_boot_option_description = NULL; +bool arg_efi_boot_option_description_with_device = false; bool arg_dry_run = false; ImagePolicy *arg_image_policy = NULL; bool arg_varlink = false; @@ -349,6 +344,8 @@ static int help(int argc, char *argv[], void *userdata) { " Install all supported EFI architectures\n" " --efi-boot-option-description=DESCRIPTION\n" " Description of the entry in the boot option list\n" + " --efi-boot-option-description-with-device=yes\n" + " Suffix description with disk vendor/model/serial\n" " --dry-run Dry run (unlink and cleanup)\n" " --secure-boot-auto-enroll=yes|no\n" " Set up secure boot auto-enrollment\n" @@ -398,6 +395,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_JSON, ARG_ARCH_ALL, ARG_EFI_BOOT_OPTION_DESCRIPTION, + ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE, ARG_DRY_RUN, ARG_PRINT_LOADER_PATH, ARG_PRINT_STUB_PATH, @@ -409,39 +407,40 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "esp-path", required_argument, NULL, ARG_ESP_PATH }, - { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ - { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, - { "root", required_argument, NULL, ARG_ROOT }, - { "image", required_argument, NULL, ARG_IMAGE }, - { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, - { "install-source", required_argument, NULL, ARG_INSTALL_SOURCE }, - { "print-esp-path", no_argument, NULL, 'p' }, - { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ - { "print-boot-path", no_argument, NULL, 'x' }, - { "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH }, - { "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH }, - { "print-root-device", no_argument, NULL, 'R' }, - { "variables", required_argument, NULL, ARG_VARIABLES }, - { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, /* Compatibility alias */ - { "random-seed", required_argument, NULL, ARG_RANDOM_SEED }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "graceful", no_argument, NULL, ARG_GRACEFUL }, - { "quiet", no_argument, NULL, 'q' }, - { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, - { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ - { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, - { "json", required_argument, NULL, ARG_JSON }, - { "all-architectures", no_argument, NULL, ARG_ARCH_ALL }, - { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION }, - { "dry-run", no_argument, NULL, ARG_DRY_RUN }, - { "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL }, - { "certificate", required_argument, NULL, ARG_CERTIFICATE }, - { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, - { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, - { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "esp-path", required_argument, NULL, ARG_ESP_PATH }, + { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ + { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, + { "root", required_argument, NULL, ARG_ROOT }, + { "image", required_argument, NULL, ARG_IMAGE }, + { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, + { "install-source", required_argument, NULL, ARG_INSTALL_SOURCE }, + { "print-esp-path", no_argument, NULL, 'p' }, + { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ + { "print-boot-path", no_argument, NULL, 'x' }, + { "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH }, + { "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH }, + { "print-root-device", no_argument, NULL, 'R' }, + { "variables", required_argument, NULL, ARG_VARIABLES }, + { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, /* Compatibility alias */ + { "random-seed", required_argument, NULL, ARG_RANDOM_SEED }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "graceful", no_argument, NULL, ARG_GRACEFUL }, + { "quiet", no_argument, NULL, 'q' }, + { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, + { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ + { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, + { "json", required_argument, NULL, ARG_JSON }, + { "all-architectures", no_argument, NULL, ARG_ARCH_ALL }, + { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION }, + { "efi-boot-option-description-with-device", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE }, + { "dry-run", no_argument, NULL, ARG_DRY_RUN }, + { "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL }, + { "certificate", required_argument, NULL, ARG_CERTIFICATE }, + { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, + { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, + { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, {} }; @@ -577,9 +576,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_EFI_BOOT_OPTION_DESCRIPTION: if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) { - _cleanup_free_ char *escaped = NULL; - - escaped = cescape(optarg); + _cleanup_free_ char *escaped = cescape(optarg); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --efi-boot-option-description=: %s", strna(escaped)); } @@ -592,6 +589,13 @@ static int parse_argv(int argc, char *argv[]) { return r; break; + case ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE: + r = parse_boolean_argument("--efi-boot-option-description-with-device=", optarg, &arg_efi_boot_option_description_with_device); + if (r < 0) + return r; + + break; + case ARG_DRY_RUN: arg_dry_run = true; break; diff --git a/src/bootctl/bootctl.h b/src/bootctl/bootctl.h index f4d6bb40ba8..93a1ca733b6 100644 --- a/src/bootctl/bootctl.h +++ b/src/bootctl/bootctl.h @@ -37,6 +37,7 @@ extern char *arg_root; extern char *arg_image; extern InstallSource arg_install_source; extern char *arg_efi_boot_option_description; +extern bool arg_efi_boot_option_description_with_device; extern bool arg_dry_run; extern ImagePolicy *arg_image_policy; extern bool arg_varlink; @@ -59,3 +60,9 @@ int acquire_esp(int unprivileged_mode, bool graceful, uint32_t *ret_part, uint64 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 + * having to deal with a potentially too long string. */ +#define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255)