diff options
author | Emanuele Giuseppe Esposito <eesposit@redhat.com> | 2023-09-21 08:21:42 +0200 |
---|---|---|
committer | Emanuele Giuseppe Esposito <eesposit@redhat.com> | 2024-02-14 10:58:20 +0100 |
commit | 122650b4a0f9af4727513ca7f21b1321e3d06a88 (patch) | |
tree | 263031fc7250d49d42610a8af633d9190e163611 /src/shared/bootspec.c | |
parent | bootspec: refactor find_sections (diff) | |
download | systemd-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.c | 260 |
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, §ions, &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. */ |