Merge pull request #22487 from poettering/bootspec-source-flags

bootctl: show all discovered entries, but show state + type in details
This commit is contained in:
Yu Watanabe
2022-02-15 04:45:59 +09:00
committed by GitHub
7 changed files with 323 additions and 115 deletions

2
TODO
View File

@@ -78,6 +78,8 @@ Janitorial Clean-ups:
Features:
* bootspec: remove tries counter from boot entry ids
* automatically ignore threaded cgroups in cg_xyz().
* add linker script that implicitly adds symbol for build ID and new coredump

View File

@@ -115,3 +115,9 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
struct new_statx nsx; \
} var
#endif
static inline bool devid_set_and_equal(dev_t a, dev_t b) {
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
* know" and we'll return false */
return a == b && a != 0;
}

View File

@@ -14,6 +14,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
#include "stat-util.h"
#include "sync-util.h"
#include "terminal-util.h"
#include "util.h"
@@ -98,17 +99,18 @@ static int parse_argv(int argc, char *argv[]) {
static int acquire_path(void) {
_cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL;
dev_t esp_devid = 0, xbootldr_devid = 0;
char **a;
int r;
if (!strv_isempty(arg_path))
return 0;
r = find_esp_and_warn(NULL, false, &esp_path, NULL, NULL, NULL, NULL);
r = find_esp_and_warn(NULL, /* unprivileged_mode= */ false, &esp_path, NULL, NULL, NULL, NULL, &esp_devid);
if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */
return r;
r = find_xbootldr_and_warn(NULL, false, &xbootldr_path, NULL);
r = find_xbootldr_and_warn(NULL, /* unprivileged_mode= */ false, &xbootldr_path, NULL, &xbootldr_devid);
if (r < 0 && r != -ENOKEY)
return r;
@@ -117,8 +119,10 @@ static int acquire_path(void) {
"Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n"
"Alternatively, use --path= to specify path to mount point.");
if (esp_path)
if (esp_path && xbootldr_path && !devid_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */
a = strv_new(esp_path, xbootldr_path);
else if (esp_path)
a = strv_new(esp_path);
else
a = strv_new(xbootldr_path);
if (!a)
@@ -130,7 +134,7 @@ static int acquire_path(void) {
_cleanup_free_ char *j = NULL;
j = strv_join(arg_path, ":");
log_debug("Using %s as boot loader drop-in search path.", j);
log_debug("Using %s as boot loader drop-in search path.", strna(j));
}
return 0;

View File

@@ -36,6 +36,7 @@
#include "rm-rf.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "sync-util.h"
@@ -74,7 +75,8 @@ static int acquire_esp(
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
char *np;
int r;
@@ -85,7 +87,7 @@ static int acquire_esp(
* we simply eat up the error here, so that --list and --status work too, without noise about
* this). */
r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid);
r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
if (r == -ENOKEY) {
if (graceful)
return log_info_errno(r, "Couldn't find EFI system partition, skipping.");
@@ -103,16 +105,23 @@ static int acquire_esp(
return 1;
}
static int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid) {
static int acquire_xbootldr(
bool unprivileged_mode,
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
char *np;
int r;
r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid);
r = find_xbootldr_and_warn(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.");
arg_xbootldr_path = mfree(arg_xbootldr_path);
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
arg_xbootldr_path = mfree(arg_xbootldr_path);
if (ret_devid)
*ret_devid = 0;
return 0;
}
if (r < 0)
@@ -411,7 +420,21 @@ static void boot_entry_file_list(const char *field, const char *root, const char
*ret_status = status;
}
static int boot_entry_show(const BootEntry *e, bool show_as_default) {
static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = {
[BOOT_ENTRY_CONF] = "Boot Loader Specification Type #1 (.conf)",
[BOOT_ENTRY_UNIFIED] = "Boot Loader Specification Type #2 (.efi)",
[BOOT_ENTRY_LOADER] = "Reported by Boot Loader",
[BOOT_ENTRY_LOADER_AUTO] = "Automatic",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_type, BootEntryType);
static int boot_entry_show(
const BootEntry *e,
bool show_as_default,
bool show_as_selected,
bool show_reported) {
int status = 0;
/* Returns 0 on success, negative on processing error, and positive if something is wrong with the
@@ -419,9 +442,30 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) {
assert(e);
printf(" title: %s%s%s" "%s%s%s\n",
ansi_highlight(), boot_entry_title(e), ansi_normal(),
ansi_highlight_green(), show_as_default ? " (default)" : "", ansi_normal());
printf(" type: %s\n",
boot_entry_type_to_string(e->type));
printf(" title: %s%s%s",
ansi_highlight(), boot_entry_title(e), ansi_normal());
if (show_as_default)
printf(" %s(default)%s",
ansi_highlight_green(), ansi_normal());
if (show_as_selected)
printf(" %s(selected)%s",
ansi_highlight_magenta(), ansi_normal());
if (show_reported) {
if (e->type == BOOT_ENTRY_LOADER)
printf(" %s(reported/absent)%s",
ansi_highlight_red(), ansi_normal());
else if (!e->reported_by_loader && e->type != BOOT_ENTRY_LOADER_AUTO)
printf(" %s(not reported/new)%s",
ansi_highlight_green(), ansi_normal());
}
putchar('\n');
if (e->id)
printf(" id: %s\n", e->id);
@@ -450,6 +494,7 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) {
e->root,
*s,
&status);
if (!strv_isempty(e->options)) {
_cleanup_free_ char *t = NULL, *t2 = NULL;
_cleanup_strv_free_ char **ts = NULL;
@@ -468,9 +513,16 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) {
printf(" options: %s\n", t2);
}
if (e->device_tree)
boot_entry_file_list("devicetree", e->root, e->device_tree, &status);
STRV_FOREACH(s, e->device_tree_overlay)
boot_entry_file_list(s == e->device_tree_overlay ? "devicetree-overlay" : NULL,
e->root,
*s,
&status);
return -status;
}
@@ -511,7 +563,11 @@ static int status_entries(
else {
printf("Default Boot Loader Entry:\n");
r = boot_entry_show(config.entries + config.default_entry, false);
r = boot_entry_show(
config.entries + config.default_entry,
/* show_as_default= */ false,
/* show_as_selected= */ false,
/* show_discovered= */ false);
if (r > 0)
/* < 0 is already logged by the function itself, let's just emit an extra warning if
the default entry is broken */
@@ -1388,7 +1444,7 @@ static void print_yes_no_line(bool first, bool good, const char *name) {
static int are_we_installed(void) {
int r;
r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL);
r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
@@ -1420,9 +1476,10 @@ static int are_we_installed(void) {
static int verb_status(int argc, char *argv[], void *userdata) {
sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
dev_t esp_devid = 0, xbootldr_devid = 0;
int r, k;
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid);
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid, &esp_devid);
if (arg_print_esp_path) {
if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
* error the find_esp_and_warn() won't log on its own) */
@@ -1433,7 +1490,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
puts(arg_esp_path);
}
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid);
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");
@@ -1567,7 +1624,14 @@ static int verb_status(int argc, char *argv[], void *userdata) {
}
if (arg_esp_path || arg_xbootldr_path) {
k = status_entries(arg_esp_path, esp_uuid, arg_xbootldr_path, xbootldr_uuid);
/* 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);
k = status_entries(
arg_esp_path,
esp_uuid,
same ? NULL : arg_xbootldr_path,
same ? SD_ID128_NULL : xbootldr_uuid);
if (k < 0)
r = k;
}
@@ -1578,25 +1642,29 @@ 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 = {};
_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. */
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL);
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");
if (r < 0)
return r;
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL);
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL, &xbootldr_devid);
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
if (r < 0)
return r;
r = boot_entries_load_config(arg_esp_path, arg_xbootldr_path, &config);
/* 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_entries_load_config(arg_esp_path, same ? NULL : arg_xbootldr_path, &config);
if (r < 0)
return r;
@@ -1606,7 +1674,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
else if (r < 0)
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
else
(void) boot_entries_augment_from_loader(&config, efi_entries);
(void) boot_entries_augment_from_loader(&config, efi_entries, /* only_auto= */ false);
if (config.n_entries == 0)
log_info("No boot loader entries found.");
@@ -1616,7 +1684,11 @@ static int verb_list(int argc, char *argv[], void *userdata) {
printf("Boot Loader Entries:\n");
for (size_t n = 0; n < config.n_entries; n++) {
r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry);
r = boot_entry_show(
config.entries + n,
/* show_as_default= */ n == (size_t) config.default_entry,
/* show_as_selected= */ n == (size_t) config.selected_entry,
/* show_discovered= */ true);
if (r < 0)
return r;
@@ -1788,7 +1860,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
install = streq(argv[0], "install");
graceful = !install && arg_graceful; /* support graceful mode for updates */
r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid);
r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid, NULL);
if (graceful && r == -ENOKEY)
return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
if (r < 0)
@@ -1805,7 +1877,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
}
}
r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL);
r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL, NULL);
if (r < 0)
return r;
@@ -1865,11 +1937,11 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
int r, q;
r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid);
r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid, NULL);
if (r < 0)
return r;
r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL);
r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL, NULL);
if (r < 0)
return r;
@@ -2078,7 +2150,7 @@ static int verb_set_efivar(int argc, char *argv[], void *userdata) {
static int verb_random_seed(int argc, char *argv[], void *userdata) {
int r;
r = find_esp_and_warn(arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL);
r = find_esp_and_warn(arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL);
if (r == -ENOKEY) {
/* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
if (!arg_graceful)

View File

@@ -2933,9 +2933,9 @@ static int boot_loader_entry_exists(Manager *m, const char *id) {
r = manager_read_efi_boot_loader_entries(m);
if (r >= 0)
(void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries);
(void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, /* auto_only= */ true);
return boot_config_has_entry(&config, id);
return !!boot_config_find_entry(&config, id);
}
static int method_set_reboot_to_boot_loader_entry(
@@ -3091,7 +3091,7 @@ static int property_get_boot_loader_entries(
r = manager_read_efi_boot_loader_entries(m);
if (r >= 0)
(void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries);
(void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, /* auto_only= */ true);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)

View File

@@ -53,6 +53,7 @@ static void boot_entry_free(BootEntry *entry) {
free(entry->efi);
strv_free(entry->initrd);
free(entry->device_tree);
strv_free(entry->device_tree_overlay);
}
static int boot_entry_load(
@@ -66,26 +67,28 @@ static int boot_entry_load(
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
char *b, *c;
char *c;
int r;
assert(root);
assert(path);
assert(entry);
c = endswith_no_case(path, ".conf");
if (!c)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path);
r = path_extract_filename(path, &tmp.id);
if (r < 0)
return log_error_errno(r, "Failed to extract file name from path '%s': %m", path);
b = basename(path);
tmp.id = strdup(b);
tmp.id_old = strndup(b, c - b);
if (!tmp.id || !tmp.id_old)
return log_oom();
c = endswith_no_case(tmp.id, ".conf");
if (!c)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", tmp.id);
if (!efi_loader_entry_name_valid(tmp.id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id);
tmp.id_old = strndup(tmp.id, c - tmp.id);
if (!tmp.id_old)
return log_oom();
tmp.path = strdup(path);
if (!tmp.path)
return log_oom();
@@ -144,7 +147,15 @@ static int boot_entry_load(
r = strv_extend(&tmp.initrd, p);
else if (streq(field, "devicetree"))
r = free_and_strdup(&tmp.device_tree, p);
else {
else if (streq(field, "devicetree-overlay")) {
_cleanup_strv_free_ char **l = NULL;
l = strv_split(p, NULL);
if (!l)
return log_oom();
r = strv_extend_strv(&tmp.device_tree_overlay, l, false);
} else {
log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
continue;
}
@@ -169,9 +180,11 @@ void boot_config_free(BootConfig *config) {
free(config->auto_firmware);
free(config->console_mode);
free(config->random_seed_mode);
free(config->beep);
free(config->entry_oneshot);
free(config->entry_default);
free(config->entry_selected);
for (i = 0; i < config->n_entries; i++)
boot_entry_free(config->entries + i);
@@ -236,6 +249,8 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) {
r = free_and_strdup(&config->console_mode, p);
else if (streq(field, "random-seed-mode"))
r = free_and_strdup(&config->random_seed_mode, p);
else if (streq(field, "beep"))
r = free_and_strdup(&config->beep, p);
else {
log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
continue;
@@ -548,18 +563,17 @@ static int boot_entries_find_unified(
return 0;
}
static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
size_t i, j;
static bool find_nonunique(const BootEntry *entries, size_t n_entries, bool arr[]) {
bool non_unique = false;
assert(entries || n_entries == 0);
assert(arr || n_entries == 0);
for (i = 0; i < n_entries; i++)
for (size_t i = 0; i < n_entries; i++)
arr[i] = false;
for (i = 0; i < n_entries; i++)
for (j = 0; j < n_entries; j++)
for (size_t i = 0; i < n_entries; i++)
for (size_t j = 0; j < n_entries; j++)
if (i != j && streq(boot_entry_title(entries + i),
boot_entry_title(entries + j)))
non_unique = arr[i] = arr[j] = true;
@@ -568,22 +582,26 @@ static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
}
static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
_cleanup_free_ bool *arr = NULL;
char *s;
size_t i;
int r;
bool arr[n_entries];
assert(entries || n_entries == 0);
if (n_entries == 0)
return 0;
arr = new(bool, n_entries);
if (!arr)
return -ENOMEM;
/* Find _all_ non-unique titles */
if (!find_nonunique(entries, n_entries, arr))
return 0;
/* Add version to non-unique titles */
for (i = 0; i < n_entries; i++)
for (size_t i = 0; i < n_entries; i++)
if (arr[i] && entries[i].version) {
r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
if (r < 0)
if (asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version) < 0)
return -ENOMEM;
free_and_replace(entries[i].show_title, s);
@@ -593,10 +611,9 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
return 0;
/* Add machine-id to non-unique titles */
for (i = 0; i < n_entries; i++)
for (size_t i = 0; i < n_entries; i++)
if (arr[i] && entries[i].machine_id) {
r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
if (r < 0)
if (asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id) < 0)
return -ENOMEM;
free_and_replace(entries[i].show_title, s);
@@ -606,10 +623,9 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
return 0;
/* Add file name to non-unique titles */
for (i = 0; i < n_entries; i++)
for (size_t i = 0; i < n_entries; i++)
if (arr[i]) {
r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
if (r < 0)
if (asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id) < 0)
return -ENOMEM;
free_and_replace(entries[i].show_title, s);
@@ -657,6 +673,55 @@ static int boot_entries_select_default(const BootConfig *config) {
return config->n_entries - 1;
}
static int boot_entries_select_selected(const BootConfig *config) {
assert(config);
assert(config->entries || config->n_entries == 0);
if (!config->entry_selected || config->n_entries == 0)
return -1;
for (int i = config->n_entries - 1; i >= 0; i--)
if (streq(config->entry_selected, config->entries[i].id))
return i;
return -1;
}
static int boot_load_efi_entry_pointers(BootConfig *config) {
int r;
assert(config);
if (!is_efi_boot())
return 0;
/* Loads the three "pointers" to boot loader entries from their EFI variables */
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &config->entry_oneshot);
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m");
if (r == -ENOMEM)
return r;
}
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryDefault), &config->entry_default);
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m");
if (r == -ENOMEM)
return r;
}
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntrySelected), &config->entry_selected);
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntrySelected\": %m");
if (r == -ENOMEM)
return r;
}
return 1;
}
int boot_entries_load_config(
const char *esp_path,
const char *xbootldr_path,
@@ -702,23 +767,13 @@ int boot_entries_load_config(
if (r < 0)
return log_error_errno(r, "Failed to uniquify boot entries: %m");
if (is_efi_boot()) {
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &config->entry_oneshot);
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m");
if (r == -ENOMEM)
return r;
}
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryDefault), &config->entry_default);
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m");
if (r == -ENOMEM)
return r;
}
}
r = boot_load_efi_entry_pointers(config);
if (r < 0)
return r;
config->default_entry = boot_entries_select_default(config);
config->selected_entry = boot_entries_select_selected(config);
return 0;
}
@@ -728,6 +783,7 @@ int boot_entries_load_config_auto(
BootConfig *config) {
_cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
dev_t esp_devid = 0, xbootldr_devid = 0;
int r;
assert(config);
@@ -748,20 +804,25 @@ int boot_entries_load_config_auto(
"Failed to determine whether /run/boot-loader-entries/ exists: %m");
}
r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
r = find_esp_and_warn(override_esp_path, /* unprivileged_mode= */ false, &esp_where, NULL, NULL, NULL, NULL, &esp_devid);
if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
return r;
r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL);
r = find_xbootldr_and_warn(override_xbootldr_path, /* unprivileged_mode= */ false, &xbootldr_where, NULL, &xbootldr_devid);
if (r < 0 && r != -ENOKEY)
return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
/* If both paths actually refer to the same inode, suppress the xbootldr path */
if (esp_where && xbootldr_where && devid_set_and_equal(esp_devid, xbootldr_devid))
xbootldr_where = mfree(xbootldr_where);
return boot_entries_load_config(esp_where, xbootldr_where, config);
}
int boot_entries_augment_from_loader(
BootConfig *config,
char **found_by_loader) {
char **found_by_loader,
bool only_auto) {
static const char *const title_table[] = {
/* Pretty names for a few well-known automatically discovered entries. */
@@ -780,18 +841,17 @@ int boot_entries_augment_from_loader(
* already included there. */
STRV_FOREACH(i, found_by_loader) {
BootEntry *existing;
_cleanup_free_ char *c = NULL, *t = NULL, *p = NULL;
char **a, **b;
if (boot_config_has_entry(config, *i))
existing = boot_config_find_entry(config, *i);
if (existing) {
existing->reported_by_loader = true;
continue;
}
/*
* consider the 'auto-' entries only, because the others
* ones are detected scanning the 'esp' and 'xbootldr'
* directories by boot_entries_load_config()
*/
if (!startswith(*i, "auto-"))
if (only_auto && !startswith(*i, "auto-"))
continue;
c = strdup(*i);
@@ -814,10 +874,11 @@ int boot_entries_augment_from_loader(
return log_oom();
config->entries[config->n_entries++] = (BootEntry) {
.type = BOOT_ENTRY_LOADER,
.type = startswith(*i, "auto-") ? BOOT_ENTRY_LOADER_AUTO : BOOT_ENTRY_LOADER,
.id = TAKE_PTR(c),
.title = TAKE_PTR(t),
.path = TAKE_PTR(p),
.reported_by_loader = true,
};
}
@@ -1109,7 +1170,8 @@ static int verify_esp(
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
bool relax_checks;
dev_t devid;
@@ -1159,9 +1221,16 @@ static int verify_esp(
* however blkid can't work if we have no privileges to access block devices directly, which is why
* we use udev in that case. */
if (unprivileged_mode)
return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
else
return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
if (r < 0)
return r;
if (ret_devid)
*ret_devid = devid;
return 0;
finish:
if (ret_part)
@@ -1172,6 +1241,8 @@ finish:
*ret_psize = 0;
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
if (ret_devid)
*ret_devid = 0;
return 0;
}
@@ -1183,7 +1254,8 @@ int find_esp_and_warn(
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
int r;
@@ -1194,7 +1266,7 @@ int find_esp_and_warn(
*/
if (path) {
r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
if (r < 0)
return r;
@@ -1203,19 +1275,39 @@ int find_esp_and_warn(
path = getenv("SYSTEMD_ESP_PATH");
if (path) {
struct stat st;
if (!path_is_valid(path) || !path_is_absolute(path))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
path);
/* Note: when the user explicitly configured things with an env var we won't validate the mount
* point. After all we want this to be useful for testing. */
/* Note: when the user explicitly configured things with an env var we won't validate the
* path beyond checking it refers to a directory. After all we want this to be useful for
* testing. */
if (stat(path, &st) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", path);
if (!S_ISDIR(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
if (ret_part)
*ret_part = 0;
if (ret_pstart)
*ret_pstart = 0;
if (ret_psize)
*ret_psize = 0;
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
if (ret_devid)
*ret_devid = st.st_dev;
goto found;
}
FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
if (r >= 0)
goto found;
if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
@@ -1382,7 +1474,8 @@ static int verify_xbootldr(
const char *p,
bool searching,
bool unprivileged_mode,
sd_id128_t *ret_uuid) {
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
bool relax_checks;
dev_t devid;
@@ -1400,13 +1493,22 @@ static int verify_xbootldr(
goto finish;
if (unprivileged_mode)
return verify_xbootldr_udev(devid, searching, ret_uuid);
r = verify_xbootldr_udev(devid, searching, ret_uuid);
else
return verify_xbootldr_blkid(devid, searching, ret_uuid);
r = verify_xbootldr_blkid(devid, searching, ret_uuid);
if (r < 0)
return r;
if (ret_devid)
*ret_devid = devid;
return 0;
finish:
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
if (ret_devid)
*ret_devid = 0;
return 0;
}
@@ -1415,14 +1517,15 @@ int find_xbootldr_and_warn(
const char *path,
bool unprivileged_mode,
char **ret_path,
sd_id128_t *ret_uuid) {
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
int r;
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
if (path) {
r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
if (r < 0)
return r;
@@ -1431,15 +1534,27 @@ int find_xbootldr_and_warn(
path = getenv("SYSTEMD_XBOOTLDR_PATH");
if (path) {
struct stat st;
if (!path_is_valid(path) || !path_is_absolute(path))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
path);
if (stat(path, &st) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", path);
if (!S_ISDIR(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
if (ret_devid)
*ret_devid = st.st_dev;
goto found;
}
r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
if (r >= 0) {
path = "/boot";
goto found;

View File

@@ -11,15 +11,17 @@
#include "string-util.h"
typedef enum BootEntryType {
BOOT_ENTRY_CONF, /* Type #1 entries: *.conf files */
BOOT_ENTRY_UNIFIED, /* Type #2 entries: *.efi files */
BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI var */
_BOOT_ENTRY_MAX,
_BOOT_ENTRY_INVALID = -EINVAL,
BOOT_ENTRY_CONF, /* Boot Loader Specification Type #1 entries: *.conf files */
BOOT_ENTRY_UNIFIED, /* Boot Loader Specification Type #2 entries: *.efi files */
BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI variable (regular entries) */
BOOT_ENTRY_LOADER_AUTO, /* Additional entries augmented from LoaderEntries EFI variable (special "automatic" entries) */
_BOOT_ENTRY_TYPE_MAX,
_BOOT_ENTRY_TYPE_INVALID = -EINVAL,
} BootEntryType;
typedef struct BootEntry {
BootEntryType type;
bool reported_by_loader;
char *id; /* This is the file basename (including extension!) */
char *id_old; /* Old-style ID, for deduplication purposes. */
char *path; /* This is the full path to the drop-in file */
@@ -34,6 +36,7 @@ typedef struct BootEntry {
char *efi;
char **initrd;
char *device_tree;
char **device_tree_overlay;
} BootEntry;
typedef struct BootConfig {
@@ -44,29 +47,33 @@ typedef struct BootConfig {
char *auto_firmware;
char *console_mode;
char *random_seed_mode;
char *beep;
char *entry_oneshot;
char *entry_default;
char *entry_selected;
BootEntry *entries;
size_t n_entries;
ssize_t default_entry;
ssize_t selected_entry;
} BootConfig;
static inline bool boot_config_has_entry(BootConfig *config, const char *id) {
size_t j;
static inline BootEntry* boot_config_find_entry(BootConfig *config, const char *id) {
assert(config);
assert(id);
for (j = 0; j < config->n_entries; j++) {
const char* entry_id_old = config->entries[j].id_old;
if (streq(config->entries[j].id, id) ||
(entry_id_old && streq(entry_id_old, id)))
return true;
}
for (size_t j = 0; j < config->n_entries; j++)
if (streq_ptr(config->entries[j].id, id) ||
streq_ptr(config->entries[j].id_old, id))
return config->entries + j;
return false;
return NULL;
}
static inline BootEntry* boot_config_default_entry(BootConfig *config) {
assert(config);
if (config->default_entry < 0)
return NULL;
@@ -76,11 +83,13 @@ static inline BootEntry* boot_config_default_entry(BootConfig *config) {
void boot_config_free(BootConfig *config);
int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config);
int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config);
int boot_entries_augment_from_loader(BootConfig *config, char **list);
int boot_entries_augment_from_loader(BootConfig *config, char **list, bool only_auto);
static inline const char* boot_entry_title(const BootEntry *entry) {
assert(entry);
return entry->show_title ?: entry->title ?: entry->id;
}
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid);
int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid);
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid, dev_t *ret_devid);