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:
commit
20742f92c1
169
man/bootctl.xml
169
man/bootctl.xml
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user