summaryrefslogtreecommitdiffstats
path: root/src/shared/bootspec.c
diff options
context:
space:
mode:
authorEmanuele Giuseppe Esposito <eesposit@redhat.com>2023-09-21 08:21:42 +0200
committerEmanuele Giuseppe Esposito <eesposit@redhat.com>2024-02-14 10:58:20 +0100
commit122650b4a0f9af4727513ca7f21b1321e3d06a88 (patch)
tree263031fc7250d49d42610a8af633d9190e163611 /src/shared/bootspec.c
parentbootspec: refactor find_sections (diff)
downloadsystemd-122650b4a0f9af4727513ca7f21b1321e3d06a88.tar.xz
systemd-122650b4a0f9af4727513ca7f21b1321e3d06a88.zip
bootctl: discover local UKI PE addons
An UKI final command line is not just made of the content of .cmdline, but also from the addons that are inserted in /boot/efi/EFI/Linux/<UKI_NAME>.efi.extra.d (local addons) and /boot/efi/loader/addons (global addons). Therefore bootclt "status" and "list" should also include these addons when printing the UKI command line. Right now, discover addons present in /boot/efi/EFI/Linux/<UKI_NAME>.efi.extra.d. Example output (assume UKI_NAME=devel): $ bootctl ukiCmdline: console=tty0 console=ttyS0 localAddon: devel.efi.extra.d/rpm_addon.addon.efi cmdline: └─this is a normal addon finalCmdline: console=tty0 console=ttyS0 this is a normal addon
Diffstat (limited to 'src/shared/bootspec.c')
-rw-r--r--src/shared/bootspec.c260
1 files changed, 233 insertions, 27 deletions
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index 3e1e9c32b4..16986c7912 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -43,6 +43,15 @@ static const char* const boot_entry_type_json_table[_BOOT_ENTRY_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_type_json, BootEntryType);
+BootEntryAddon* boot_entry_addon_free(BootEntryAddon *addon) {
+ if (!addon)
+ return NULL;
+
+ free(addon->location);
+ free(addon->cmdline);
+ return mfree(addon);
+}
+
static void boot_entry_free(BootEntry *entry) {
assert(entry);
@@ -57,6 +66,7 @@ static void boot_entry_free(BootEntry *entry) {
free(entry->machine_id);
free(entry->architecture);
strv_free(entry->options);
+ free(entry->local_addons.items);
free(entry->kernel);
free(entry->efi);
strv_free(entry->initrd);
@@ -851,6 +861,121 @@ static int find_uki_sections(
return 0;
}
+static int find_addon_sections(
+ int fd,
+ const char *path,
+ char **ret_cmdline) {
+
+ _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
+ _cleanup_free_ PeHeader *pe_header = NULL;
+ int r;
+
+ r = find_sections(fd, path, &sections, &pe_header);
+ if (r < 0)
+ return r;
+
+ return find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
+}
+
+static int insert_boot_entry_addon(
+ BootEntryAddons *addons,
+ char *location,
+ char *cmdline) {
+
+ if (!GREEDY_REALLOC(addons->items, addons->count + 1))
+ return log_oom();
+
+ addons->items[addons->count] = (BootEntryAddon) {
+ .location = location,
+ .cmdline = cmdline,
+ };
+ addons->count++;
+
+ return 0;
+}
+
+static int boot_entries_find_unified_addons(
+ BootConfig *config,
+ int d_fd,
+ const char *addon_dir,
+ const char *root,
+ BootEntryAddons *addons) {
+
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *full = NULL;
+ int r;
+
+ assert(addons);
+ assert(config);
+
+ r = chase_and_opendirat(d_fd, addon_dir, CHASE_AT_RESOLVE_IN_ROOT, &full, &d);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to open '%s/%s': %m", root, addon_dir);
+
+ *addons = (BootEntryAddons) {};
+
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", full)) {
+ _cleanup_free_ char *j = NULL, *cmdline = NULL, *location = NULL;
+ _cleanup_close_ int fd = -EBADF;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (!endswith_no_case(de->d_name, ".addon.efi"))
+ continue;
+
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOFOLLOW|O_NOCTTY);
+ if (fd < 0) {
+ log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", full, de->d_name);
+ continue;
+ }
+
+ r = config_check_inode_relevant_and_unseen(config, fd, de->d_name);
+ if (r < 0)
+ return r;
+ if (r == 0) /* inode already seen or otherwise not relevant */
+ continue;
+
+ j = path_join(full, de->d_name);
+ if (!j)
+ return log_oom();
+
+ if (find_addon_sections(fd, j, &cmdline) < 0)
+ continue;
+
+ location = strdup(j);
+ if (!location)
+ return log_oom();
+
+ r = insert_boot_entry_addon(addons, TAKE_PTR(location), TAKE_PTR(cmdline));
+ if (r < 0) {
+ free(addons);
+ return r;
+ }
+ }
+ return 0;
+}
+
+static int boot_entries_find_unified_local_addons(
+ BootConfig *config,
+ int d_fd,
+ const char *d_name,
+ const char *root,
+ BootEntry *ret) {
+
+ _cleanup_free_ char *addon_dir = NULL;
+
+ assert(ret);
+
+ addon_dir = strjoin(d_name, ".extra.d");
+ if (!addon_dir)
+ return log_oom();
+
+ return boot_entries_find_unified_addons(config, d_fd, addon_dir, root, &ret->local_addons);
+}
+
static int boot_entries_find_unified(
BootConfig *config,
const char *root,
@@ -905,6 +1030,11 @@ static int boot_entries_find_unified(
if (r < 0)
continue;
+ /* look for .efi.extra.d */
+ r = boot_entries_find_unified_local_addons(config, dirfd(d), de->d_name, full, config->entries + config->n_entries);
+ if (r < 0)
+ continue;
+
config->n_entries++;
}
@@ -1297,13 +1427,108 @@ static void boot_entry_file_list(
*ret_status = status;
}
+static void print_addon(
+ BootEntryAddon *addon,
+ const char *addon_str) {
+
+ printf(" %s: %s\n", addon_str, addon->location);
+ printf(" cmdline: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), addon->cmdline);
+}
+
+static int print_cmdline(const BootEntry *e) {
+ _cleanup_free_ char *final_cmdline = NULL;
+
+ assert(e);
+
+ if (!strv_isempty(e->options)) {
+ _cleanup_free_ char *t = NULL, *t2 = NULL;
+ _cleanup_strv_free_ char **ts = NULL;
+
+ t = strv_join(e->options, " ");
+ if (!t)
+ return log_oom();
+
+ ts = strv_split_newlines(t);
+ if (!ts)
+ return log_oom();
+
+ t2 = strv_join(ts, "\n ");
+ if (!t2)
+ return log_oom();
+
+ printf(" ukiCmdline: %s\n", t2);
+ final_cmdline = TAKE_PTR(t2);
+ }
+
+ FOREACH_ARRAY(addon, e->local_addons.items, e->local_addons.count) {
+ /* Add space at the beginning of addon_str to align it correctly */
+ print_addon(addon, " localAddon");
+ if (!strextend(&final_cmdline, " ", addon->cmdline))
+ return log_oom();
+ }
+
+ if (final_cmdline)
+ printf(" finalCmdline: %s\n", final_cmdline);
+
+ return 0;
+}
+
+static int json_addon(
+ BootEntryAddon *addon,
+ const char *addon_str,
+ JsonVariant **array) {
+
+ int r;
+
+ r = json_variant_append_arrayb(array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(addon_str, JSON_BUILD_STRING(addon->location)),
+ JSON_BUILD_PAIR("cmdline", JSON_BUILD_STRING(addon->cmdline))));
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
+static int json_cmdline(const BootEntry *e, JsonVariant **v) {
+ _cleanup_free_ char *final_cmdline = NULL, *def_cmdline = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *addons_array = NULL;
+ int r;
+
+ assert(e);
+
+ if (!strv_isempty(e->options)) {
+ def_cmdline = strv_join(e->options, " ");
+ if (!def_cmdline)
+ return log_oom();
+ final_cmdline = TAKE_PTR(def_cmdline);
+ }
+
+ FOREACH_ARRAY(addon, e->local_addons.items, e->local_addons.count) {
+ r = json_addon(addon, "localAddon", &addons_array);
+ if (r < 0)
+ return r;
+ if (!strextend(&final_cmdline, " ", addon->cmdline))
+ return log_oom();
+ }
+
+ r = json_variant_merge_objectb(
+ v, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_CONDITION(def_cmdline, "ukiCmdline", JSON_BUILD_STRING(def_cmdline)),
+ JSON_BUILD_PAIR("addons", JSON_BUILD_VARIANT(addons_array)),
+ JSON_BUILD_PAIR_CONDITION(final_cmdline, "finalCmdline", JSON_BUILD_STRING(final_cmdline))));
+ if (r < 0)
+ return log_oom();
+ return 0;
+}
+
int show_boot_entry(
const BootEntry *e,
bool show_as_default,
bool show_as_selected,
bool show_reported) {
- int status = 0;
+ int status = 0, r = 0;
/* Returns 0 on success, negative on processing error, and positive if something is wrong with the
boot entry itself. */
@@ -1382,24 +1607,9 @@ int show_boot_entry(
*s,
&status);
- if (!strv_isempty(e->options)) {
- _cleanup_free_ char *t = NULL, *t2 = NULL;
- _cleanup_strv_free_ char **ts = NULL;
-
- t = strv_join(e->options, " ");
- if (!t)
- return log_oom();
-
- ts = strv_split_newlines(t);
- if (!ts)
- return log_oom();
-
- t2 = strv_join(ts, "\n ");
- if (!t2)
- return log_oom();
-
- printf(" options: %s\n", t2);
- }
+ r = print_cmdline(e);
+ if (r < 0)
+ return r;
if (e->device_tree)
boot_entry_file_list("devicetree", e->root, e->device_tree, &status);
@@ -1422,16 +1632,9 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
for (size_t i = 0; i < config->n_entries; i++) {
- _cleanup_free_ char *opts = NULL;
const BootEntry *e = config->entries + i;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- if (!strv_isempty(e->options)) {
- opts = strv_join(e->options, " ");
- if (!opts)
- return log_oom();
- }
-
r = json_variant_merge_objectb(
&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("type", JSON_BUILD_STRING(boot_entry_type_json_to_string(e->type))),
@@ -1444,7 +1647,6 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
JSON_BUILD_PAIR_CONDITION(e->version, "version", JSON_BUILD_STRING(e->version)),
JSON_BUILD_PAIR_CONDITION(e->machine_id, "machineId", JSON_BUILD_STRING(e->machine_id)),
JSON_BUILD_PAIR_CONDITION(e->architecture, "architecture", JSON_BUILD_STRING(e->architecture)),
- JSON_BUILD_PAIR_CONDITION(opts, "options", JSON_BUILD_STRING(opts)),
JSON_BUILD_PAIR_CONDITION(e->kernel, "linux", JSON_BUILD_STRING(e->kernel)),
JSON_BUILD_PAIR_CONDITION(e->efi, "efi", JSON_BUILD_STRING(e->efi)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", JSON_BUILD_STRV(e->initrd)),
@@ -1453,6 +1655,10 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
if (r < 0)
return log_oom();
+ r = json_cmdline(e, &v);
+ if (r < 0)
+ return log_oom();
+
/* Sanitizers (only memory sanitizer?) do not like function call with too many
* arguments and trigger false positive warnings. Let's not add too many json objects
* at once. */