1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-27 18:04:05 +03:00

Merge pull request #22951 from keszybz/fix-entry-selection-bootctl-status

Fix entry selection in bootctl status
This commit is contained in:
Yu Watanabe 2022-04-05 13:42:43 +09:00 committed by GitHub
commit 20742f92c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 201 additions and 85 deletions

View File

@ -46,33 +46,35 @@
<varlistentry>
<term><option>status</option></term>
<listitem><para>Shows brief information about the system firmware, the boot loader that was used to boot the
system, the boot loaders currently available in the ESP, the boot loaders listed in the firmware's list of boot
loaders and the current default boot loader entry. If no command is specified, this is the implied
default.</para></listitem>
<listitem><para>Shows brief information about the system firmware, the boot loader that was used to
boot the system, the boot loaders currently available in the ESP, the boot loaders listed in the
firmware's list of boot loaders and the current default boot loader entry. If no command is
specified, this is the implied default.</para>
<para>See the example below for details of the output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>reboot-to-firmware</option> <optional><replaceable>BOOL</replaceable></optional></term>
<listitem><para>Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
boolean argument which controls whether to show the firmware setup on next system reboot. If the
argument is omitted shows the current status of the flag, or whether the flag is supported. This
controls the same flag as <command>systemctl reboot --firmware-setup</command>, but is more low-level
and allows setting the flag independently from actually requesting a reboot.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>systemd-efi-options</option> <optional><replaceable>STRING</replaceable></optional></term>
<listitem><para>When called without the optional argument, prints the current value of the
<literal>SystemdOptions</literal> EFI variable. When called with an argument, sets the variable to
that value. See
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> for the
meaning of that variable.</para></listitem>
</varlistentry>
</variablelist>
<varlistentry>
<term><option>reboot-to-firmware</option> <optional><replaceable>BOOL</replaceable></optional></term>
<listitem><para>Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
boolean argument which controls whether to show the firmware setup on next system reboot. If the
argument is omitted shows the current status of the flag, or whether the flag is supported. This
controls the same flag as <command>systemctl reboot --firmware-setup</command>, but is more
low-level and allows setting the flag independently from actually requesting a
reboot.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>systemd-efi-options</option> <optional><replaceable>STRING</replaceable></optional></term>
<listitem><para>When called without the optional argument, prints the current value of the
<literal>SystemdOptions</literal> EFI variable. When called with an argument, sets the
variable to that value. See
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for the meaning of that variable.</para></listitem>
</varlistentry>
</refsect1>
<refsect1>
@ -84,17 +86,16 @@
<command>systemd-boot</command>.</para>
<variablelist>
<varlistentry>
<term><option>list</option></term>
<listitem><para>Shows all available boot loader entries implementing the <ulink
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, as well as any
other entries discovered or automatically generated by a boot loader implementing the <ulink
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader
Interface</ulink>.</para>
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>.
JSON output may be requested with <option>--json=</option>.</para>
<para>JSON output may be requested with <option>--json=</option>.</para>
<para>See the example below for details of the output.</para>
</listitem>
</varlistentry>
@ -104,9 +105,9 @@
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string or a glob
pattern as argument. The <option>set-oneshot</option> command will set the default entry only for the next boot,
the <option>set-default</option> will set it persistently for all future boots.</para></listitem>
the <option>set-default</option> will set it persistently for all future boots.</para>
<listitem><para>Optionally, the boot loader entry ID may be specified as one of: <option>@default</option>,
<para>Optionally, the boot loader entry ID may be specified as one of: <option>@default</option>,
<option>@oneshot</option> or <option>@current</option>, which correspond to the current default boot loader
entry for all future boots, the current default boot loader entry for the next boot, and the currently booted
boot loader entry. These special IDs are resolved to the current values of the EFI variables
@ -137,7 +138,6 @@
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>
@ -347,6 +347,111 @@
the Extended Boot Loader partition.</para>
</refsect1>
<refsect1>
<title>Examples</title>
<example>
<title>Output from <command>status</command> and <command>list</command></title>
<programlisting>$ <command>bootctl status</command>
System:
Firmware: UEFI 2.40 (<replaceable>firmware-version</replaceable>) ← firmware vendor and version
Secure Boot: disabled (setup) ← secure boot status
TPM2 Support: yes
Boot into FW: supported ← does the firmware support booting into itself
Current Boot Loader: ← details about sd-boot or another boot loader
Product: systemd-boot <replaceable>version</replaceable> implementing the <ulink
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>
Features: ✓ Boot counting
✓ Menu timeout control
✓ One-shot menu timeout control
✓ Default entry control
✓ One-shot entry control
✓ Support for XBOOTLDR partition
✓ Support for passing random seed to OS
✓ Load drop-in drivers
✓ Boot loader sets ESP information
ESP: /dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000
File: └─/EFI/systemd/systemd-bootx64.efi
Random Seed: ← random seed used for entropy in early boot
Passed to OS: yes
System Token: set
Exists: yes
Available Boot Loaders on ESP:
ESP: /boot/efi (/dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000)
File: └─/EFI/systemd/systemd-bootx64.efi (systemd-boot 251
File: └─/EFI/BOOT/BOOTX64.EFI (systemd-boot 251
Boot Loaders Listed in EFI Variables:
Title: Linux Boot Manager
ID: 0x0001
Status: active, boot-order
Partition: /dev/disk/by-partuuid/…
File: └─/EFI/systemd/systemd-bootx64.efi
Title: Fedora
ID: 0x0000
Status: active, boot-order
Partition: /dev/disk/by-partuuid/…
File: └─/EFI/fedora/shimx64.efi
Title: Linux-Firmware-Updater
ID: 0x0002
Status: active, boot-order
Partition: /dev/disk/by-partuuid/…
File: └─/EFI/fedora/fwupdx64.efi
Boot Loader Entries:
$BOOT: /boot/efi (/dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000)
Default Boot Loader Entry:
type: Boot Loader Specification Type #1 (.conf)
title: Fedora Linux 36 (Workstation Edition)
id: …
source: /boot/efi/loader/entries/<replaceable>entry-token</replaceable>-<replaceable>kernel-version</replaceable>.conf
version: <replaceable>kernel-version</replaceable>
machine-id: …
linux: /<replaceable>entry-token</replaceable>/<replaceable>kernel-version</replaceable>/linux
initrd: /<replaceable>entry-token</replaceable>/<replaceable>kernel-version</replaceable>/initrd
options: root=…
</programlisting>
<programlisting>$ <command>bootctl list</command>
Boot Loader Entries:
type: Boot Loader Specification Type #1 (.conf)
title: Fedora Linux 36 (Workstation Edition) (default) (selected)
id: …
source: /boot/efi/loader/entries/<replaceable>entry-token</replaceable>-<replaceable>kernel-version</replaceable>.conf
version: <replaceable>kernel-version</replaceable>
machine-id: …
linux: /<replaceable>entry-token</replaceable>/<replaceable>kernel-version</replaceable>/linux
initrd: /<replaceable>entry-token</replaceable>/<replaceable>kernel-version</replaceable>/initrd
options: root=…
type: Boot Loader Specification Type #2 (.efi)
title: Fedora Linux 35 (Workstation Edition)
id: …
source: /boot/efi/EFI/Linux/fedora-<replaceable>kernel-version</replaceable>.efi
version: <replaceable>kernel-version</replaceable>
machine-id: …
linux: /EFI/Linux/fedora-<replaceable>kernel-version</replaceable>.efi
options: root=…
type: Automatic
title: Reboot Into Firmware Interface
id: auto-reboot-to-firmware-setup
source: /sys/firmware/efi/efivars/LoaderEntries-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
</programlisting>
<para>In the listing, <literal>(default)</literal> specifies the entry that will be
used by default, and <literal>(selected)</literal> specifies the entry that was
selected the last time (i.e. is currently running).</para>
</example>
</refsect1>
<refsect1>
<title>See Also</title>
<para>

View File

@ -115,7 +115,7 @@ static int acquire_esp(
free_and_replace(arg_esp_path, np);
log_debug("Using EFI System Partition at %s.", arg_esp_path);
return 1;
return 0;
}
static int acquire_xbootldr(
@ -583,6 +583,35 @@ static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_type, BootEntryType);
static int boot_config_load_and_select(
BootConfig *config,
const char *esp_path,
dev_t esp_devid,
const char *xbootldr_path,
dev_t xbootldr_devid) {
_cleanup_strv_free_ char **efi_entries = NULL;
int r;
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
* find the same entries twice. */
bool same = esp_path && xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
if (r < 0)
return r;
r = efi_loader_get_entries(&efi_entries);
if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "Boot loader reported no entries.");
else if (r < 0)
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
else
(void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
return boot_config_select_special_entries(config);
}
static int boot_entry_show(
const BootEntry *e,
bool show_as_default,
@ -682,16 +711,17 @@ static int boot_entry_show(
}
static int status_entries(
const BootConfig *config,
const char *esp_path,
sd_id128_t esp_partition_uuid,
const char *xbootldr_path,
sd_id128_t xbootldr_partition_uuid) {
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
sd_id128_t dollar_boot_partition_uuid;
const char *dollar_boot_path;
int r;
assert(config);
assert(esp_path || xbootldr_path);
if (xbootldr_path) {
@ -709,21 +739,13 @@ static int status_entries(
SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
printf("\n\n");
r = boot_config_load(&config, esp_path, xbootldr_path);
if (r < 0)
return r;
r = boot_config_select_special_entries(&config);
if (r < 0)
return r;
if (config.default_entry < 0)
printf("%zu entries, no entry could be determined as default.\n", config.n_entries);
if (config->default_entry < 0)
printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
else {
printf("Default Boot Loader Entry:\n");
r = boot_entry_show(
boot_config_default_entry(&config),
boot_config_default_entry(config),
/* show_as_default= */ false,
/* show_as_selected= */ false,
/* show_discovered= */ false);
@ -1593,13 +1615,9 @@ static void print_yes_no_line(bool first, bool good, const char *name) {
name);
}
static int are_we_installed(void) {
static int are_we_installed(const char *esp_path) {
int r;
r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
/* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
* check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
* loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
@ -1614,7 +1632,7 @@ static int are_we_installed(void) {
* It specifically checks for systemd-boot, not for other boot loaders (which a check for
* /boot/loader/entries would do). */
_cleanup_free_ char *p = path_join(arg_esp_path, "/EFI/systemd/");
_cleanup_free_ char *p = path_join(esp_path, "/EFI/systemd/");
if (!p)
return log_oom();
@ -1645,7 +1663,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid, &xbootldr_devid);
if (arg_print_dollar_boot_path) {
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR location: %m");
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
if (r < 0)
return r;
@ -1659,8 +1677,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
if (arg_print_esp_path || arg_print_dollar_boot_path)
return 0;
r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
* can show */
r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
* show what we can show */
pager_open(arg_pager_flags);
@ -1776,16 +1794,20 @@ static int verb_status(int argc, char *argv[], void *userdata) {
}
if (arg_esp_path || arg_xbootldr_path) {
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would find the same entries twice */
bool same = arg_esp_path && arg_xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
k = status_entries(
arg_esp_path,
esp_uuid,
same ? NULL : arg_xbootldr_path,
same ? SD_ID128_NULL : xbootldr_uuid);
k = boot_config_load_and_select(&config,
arg_esp_path, esp_devid,
arg_xbootldr_path, xbootldr_devid);
if (k < 0)
r = k;
else {
k = status_entries(&config,
arg_esp_path, esp_uuid,
arg_xbootldr_path, xbootldr_uuid);
if (k < 0)
r = k;
}
}
return r;
@ -1793,17 +1815,17 @@ static int verb_status(int argc, char *argv[], void *userdata) {
static int verb_list(int argc, char *argv[], void *userdata) {
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
_cleanup_strv_free_ char **efi_entries = NULL;
dev_t esp_devid = 0, xbootldr_devid = 0;
int r;
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
* off logging about access errors and turn off potentially privileged device probing. Here we're interested in
* the latter but not the former, hence request the mode, and log about EACCES. */
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
* things: turn off logging about access errors and turn off potentially privileged device probing.
* Here we're interested in the latter but not the former, hence request the mode, and log about
* EACCES. */
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* 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: %m");
return log_error_errno(r, "Failed to determine ESP location: %m");
if (r < 0)
return r;
@ -1813,22 +1835,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would find the same entries twice */
bool same = arg_esp_path && arg_xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
r = boot_config_load(&config, arg_esp_path, same ? NULL : arg_xbootldr_path);
if (r < 0)
return r;
r = efi_loader_get_entries(&efi_entries);
if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "Boot loader reported no entries.");
else if (r < 0)
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
else
(void) boot_config_augment_from_loader(&config, efi_entries, /* only_auto= */ false);
r = boot_config_select_special_entries(&config);
r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
if (r < 0)
return r;
@ -2063,7 +2070,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
if (!install) {
/* If we are updating, don't do anything if sd-boot wasn't actually installed. */
r = are_we_installed();
r = are_we_installed(arg_esp_path);
if (r < 0)
return r;
if (r == 0) {
@ -2209,7 +2216,11 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
static int verb_is_installed(int argc, char *argv[], void *userdata) {
int r;
r = are_we_installed();
r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
r = are_we_installed(arg_esp_path);
if (r < 0)
return r;

View File

@ -71,7 +71,7 @@ typedef struct BootConfig {
BootEntry* boot_config_find_entry(BootConfig *config, const char *id);
static inline BootEntry* boot_config_default_entry(BootConfig *config) {
static inline const BootEntry* boot_config_default_entry(const BootConfig *config) {
assert(config);
if (config->default_entry < 0)