]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: optionally include backing disk name in efi boot option description
authorLennart Poettering <lennart@amutable.com>
Sat, 20 Sep 2025 06:38:51 +0000 (08:38 +0200)
committerLennart Poettering <lennart@amutable.com>
Fri, 6 Feb 2026 22:16:49 +0000 (23:16 +0100)
man/bootctl.xml
src/bootctl/bootctl-install.c
src/bootctl/bootctl.c
src/bootctl/bootctl.h

index b317d840209d88ad6fff80fbd9ab81669d1d10dd..1e98b5ca7a697b58cc8dbb3c5dcd51e144b4d776 100644 (file)
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--efi-boot-option-description-with-device=</option></term>
+        <listitem><para>Takes a boolean, defaults to false. Controls whether to append disk model information
+        to the firmware boot option item description (as configured with
+        <option>--efi-boot-option-description=</option> 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.</para>
+
+        <xi:include href="version-info.xml" xpointer="v260"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--dry-run</option></term>
         <listitem><para>Dry run for <option>unlink</option> and <option>cleanup</option>.</para>
index d6ce3471b11942fac972708782b64202e234cb19..976c5380e1f167cbfd07f39a7e02ce0dbefdae1c 100644 (file)
@@ -3,9 +3,11 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#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);
index dee65d51d7f23bbd0b160d253df69bc2f99724a6..8d3cabc86501a98c6e842fef08f42e5e5fa42e86 100644 (file)
 #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;
index f4d6bb40ba88c25e4935c28b563e088a7a96b954..93a1ca733b68f5323d68427ffca174487a4a9e66 100644 (file)
@@ -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)