]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootspec: generalize "addon" logic for all kinds of extra sidecars
authorLennart Poettering <lennart@amutable.com>
Tue, 10 Feb 2026 14:13:02 +0000 (15:13 +0100)
committerLennart Poettering <lennart@amutable.com>
Fri, 1 May 2026 05:10:31 +0000 (07:10 +0200)
Let's pick up all kinds of sidecars and show them, not just addons

This also fixes some issues regarding "root" directory handling.
In one context we'd resolve a directory claiming it was a "root", but it
wasn't.

Implements: https://github.com/uapi-group/specifications/pull/212

src/shared/bootspec.c
src/shared/bootspec.h
src/shared/varlink-io.systemd.BootControl.c
test/units/TEST-87-AUX-UTILS-VM.bootctl.sh

index c0111d1f9c433762929b2e98c6e8aa1ae97b218d..a167e19a90b586bf7fea3f6ffcadd46d14128c30 100644 (file)
 #include "efi-loader.h"
 #include "efivars.h"
 #include "env-file.h"
+#include "errno-util.h"
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "find-esp.h"
+#include "json-util.h"
 #include "log.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -65,15 +67,65 @@ static const char* const boot_entry_source_table[_BOOT_ENTRY_SOURCE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_source, BootEntrySource);
 
-static void boot_entry_addons_done(BootEntryAddons *addons) {
-        assert(addons);
+static BootEntryExtraType boot_entry_extra_type_from_filename(const char *path) {
+        if (!path)
+                return _BOOT_ENTRY_EXTRA_TYPE_INVALID;
 
-        FOREACH_ARRAY(addon, addons->items, addons->n_items) {
-                free(addon->cmdline);
-                free(addon->location);
+        if (endswith_no_case(path, ".addon.efi"))
+                return BOOT_ENTRY_ADDON;
+        if (endswith_no_case(path, ".confext.raw"))
+                return BOOT_ENTRY_CONFEXT;
+        if (endswith_no_case(path, ".sysext.raw"))
+                return BOOT_ENTRY_SYSEXT;
+        if (endswith_no_case(path, ".cred"))
+                return BOOT_ENTRY_CREDENTIAL;
+
+        return _BOOT_ENTRY_EXTRA_TYPE_INVALID;
+}
+
+static void boot_entry_extras_done(BootEntryExtras *extras) {
+        assert(extras);
+
+        FOREACH_ARRAY(extra, extras->items, extras->n_items) {
+                free(extra->location);
+                free(extra->cmdline);
+        }
+        extras->items = mfree(extras->items);
+        extras->n_items = 0;
+}
+
+static int boot_entry_extras_add(
+                BootEntryExtras *extras,
+                BootEntryExtraType type,
+                const char *path,
+                const char *cmdline) {
+
+        assert(extras);
+        assert(type >= 0);
+        assert(type < _BOOT_ENTRY_EXTRA_TYPE_MAX);
+        assert(path);
+
+        _cleanup_free_ char *p = strdup(path);
+        if (!p)
+                return -ENOMEM;
+
+        _cleanup_free_ char *c = NULL;
+        if (cmdline) {
+                c = strdup(cmdline);
+                if (!c)
+                        return -ENOMEM;
         }
-        addons->items = mfree(addons->items);
-        addons->n_items = 0;
+
+        if (!GREEDY_REALLOC(extras->items, extras->n_items + 1))
+                return -ENOMEM;
+
+        extras->items[extras->n_items++] = (BootEntryExtra) {
+                .type = type,
+                .location = TAKE_PTR(p),
+                .cmdline = TAKE_PTR(c),
+        };
+
+        return 0;
 }
 
 static void boot_entry_free(BootEntry *entry) {
@@ -91,7 +143,7 @@ static void boot_entry_free(BootEntry *entry) {
         free(entry->machine_id);
         free(entry->architecture);
         strv_free(entry->options);
-        boot_entry_addons_done(&entry->local_addons);
+        boot_entry_extras_done(&entry->local_extras);
         free(entry->kernel);
         free(entry->efi);
         free(entry->uki);
@@ -213,6 +265,50 @@ static int parse_path_many(
         return strv_extend_strv_consume(s, TAKE_PTR(f), /* filter_duplicates= */ false);
 }
 
+static int parse_extra(
+                const char *fname,
+                unsigned line,
+                const char *field,
+                BootEntryExtras *extras,
+                const char *p) {
+
+        int r;
+
+        assert(extras);
+
+        _cleanup_strv_free_ char **l = strv_split(p, NULL);
+        if (!l)
+                return -ENOMEM;
+
+        STRV_FOREACH(i, l) {
+                _cleanup_free_ char *c = NULL;
+                r = mangle_path(fname, line, field, *i, &c);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                BootEntryExtraType type = boot_entry_extra_type_from_filename(c);
+                if (type < 0) {
+                        log_debug_errno(type, "Failed to determine boot entry extra type of '%s', skipping: %m", c);
+                        continue;
+                }
+
+                /* Let's filter out EFI addons for now. We have no protocol for passing them from sd-boot to
+                 * sd-stub, hence supporting them would require major plumbing first. */
+                if (type == BOOT_ENTRY_ADDON) {
+                        log_debug("EFI addons are currently not supported for Type #1 entries, skipping '%s'.", c);
+                        continue;
+                }
+
+                r = boot_entry_extras_add(extras, type, c, /* cmdline= */ NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int parse_tries(const char *fname, const char **p, unsigned *ret) {
         _cleanup_free_ char *d = NULL;
         unsigned tries;
@@ -421,6 +517,8 @@ static int boot_entry_load_type1(
                         r = parse_path_one(tmp.path, line, field, &tmp.device_tree, p);
                 else if (streq(field, "devicetree-overlay"))
                         r = parse_path_many(tmp.path, line, field, &tmp.device_tree_overlay, p);
+                else if (streq(field, "extra"))
+                        r = parse_extra(tmp.path, line, field, &tmp.local_extras, p);
                 else {
                         log_syntax(NULL, LOG_WARNING, tmp.path, line, 0, "Unknown line '%s', ignoring.", field);
                         continue;
@@ -458,7 +556,7 @@ int boot_config_load_type1(
                 return r;
         config->n_entries++;
 
-        entry->global_addons = &config->global_addons[source];
+        entry->global_extras = &config->global_extras[source];
 
         return 0;
 }
@@ -479,8 +577,8 @@ void boot_config_free(BootConfig *config) {
                 boot_entry_free(i);
         free(config->entries);
 
-        FOREACH_ELEMENT(i, config->global_addons)
-                boot_entry_addons_done(i);
+        FOREACH_ELEMENT(i, config->global_extras)
+                boot_entry_extras_done(i);
 
         set_free(config->inodes_seen);
 }
@@ -1188,126 +1286,140 @@ nothing:
         return 0;
 }
 
-static int insert_boot_entry_addon(
-                BootEntryAddons *addons,
-                char *location,
-                char *cmdline) {
-
-        assert(addons);
-
-        if (!GREEDY_REALLOC(addons->items, addons->n_items + 1))
-                return log_oom();
-
-        addons->items[addons->n_items++] = (BootEntryAddon) {
-                .location = location,
-                .cmdline = cmdline,
-        };
-
-        return 0;
-}
-
-static int boot_entries_find_unified_addons(
+static int boot_entries_find_unified_extras(
                 BootConfig *config,
                 int d_fd,
-                const char *addon_dir,
-                const char *root,
-                BootEntryAddons *ret_addons) {
+                const char *extra_dir,
+                BootEntryExtraType only_type,
+                const char *where,
+                bool suppress_seen,
+                BootEntryExtras *extras) {
 
-        _cleanup_closedir_ DIR *d = NULL;
-        _cleanup_free_ char *full = NULL;
-        _cleanup_(boot_entry_addons_done) BootEntryAddons addons = {};
         int r;
 
-        assert(ret_addons);
         assert(config);
+        assert(extras);
 
-        r = chase_and_opendirat(d_fd, d_fd, addon_dir, /* chase_flags= */ 0, &full, &d);
+        _cleanup_closedir_ DIR *d = NULL;
+        r = chase_and_opendirat(
+                        /* root_fd= */ d_fd,
+                        /* dir_fd= */ d_fd,
+                        extra_dir,
+                        /* chase_flags= */ 0,
+                        /* ret_path= */ NULL,
+                        &d);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
-                return log_error_errno(r, "Failed to open '%s/%s': %m", root, skip_leading_slash(addon_dir));
-
-        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;
+                return log_error_errno(r, "Failed to open '%s/%s': %m", where, skip_leading_slash(extra_dir));
 
+        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read '%s': %m", extra_dir)) {
                 if (!dirent_is_file(de))
                         continue;
 
-                if (!endswith_no_case(de->d_name, ".addon.efi"))
+                BootEntryExtraType type = boot_entry_extra_type_from_filename(de->d_name);
+                if (type < 0) {
+                        log_debug_errno(type, "Unrecognized extra file '%s', skipping.", de->d_name);
                         continue;
+                }
+                if (only_type >= 0 && type != only_type) {
+                        log_debug("Extra file '%s' type not permitted in '%s', skipping.", de->d_name, extra_dir);
+                        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);
+                _cleanup_free_ char *location = path_join(extra_dir, de->d_name);
+                if (!location)
+                        return log_oom();
+
+                _cleanup_close_ int pin_fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+                if (pin_fd < 0) {
+                        log_debug_errno(errno, "Failed to pin '%s', ignoring: %m", location);
                         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 */
+                r = fd_verify_regular(pin_fd);
+                if (r < 0) {
+                        log_debug_errno(r, "Unrecognized inode type of '%s', skipping.", location);
                         continue;
+                }
 
-                j = path_join(full, de->d_name);
-                if (!j)
-                        return log_oom();
+                if (suppress_seen) {
+                        r = config_check_inode_relevant_and_unseen(config, pin_fd, location);
+                        if (r < 0)
+                                return r;
+                        if (r == 0) /* inode already seen or otherwise not relevant */
+                                continue;
+                }
 
-                if (pe_find_addon_sections(fd, j, &cmdline) <= 0)
-                        continue;
+                _cleanup_free_ char *cmdline = NULL;
+                if (type == BOOT_ENTRY_ADDON) {
+                        _cleanup_close_ int fd = fd_reopen(pin_fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+                        if (fd < 0) {
+                                log_debug_errno(fd, "Failed to open '%s', ignoring: %m", location);
+                                continue;
+                        }
 
-                location = strdup(j);
-                if (!location)
-                        return log_oom();
+                        /* Try to extract the command line, but let's handle any failures gracefully, but
+                         * still mention the extra file exists. */
+                        (void) pe_find_addon_sections(fd, location, &cmdline);
+                }
 
-                r = insert_boot_entry_addon(&addons, location, cmdline);
+                r = boot_entry_extras_add(extras, type, location, cmdline);
                 if (r < 0)
                         return r;
-
-                TAKE_PTR(location);
-                TAKE_PTR(cmdline);
         }
 
-        *ret_addons = TAKE_STRUCT(addons);
         return 0;
 }
 
-static int boot_entries_find_unified_global_addons(
+static int boot_entries_find_unified_global_extras(
                 BootConfig *config,
-                const char *root,
-                const char *d_name,
-                BootEntryAddons *ret_addons) {
-
-        int r;
-        _cleanup_closedir_ DIR *d = NULL;
+                const char *where,
+                const char *extra_dir,
+                BootEntryExtraType only_type,
+                BootEntryExtras *extras) {
 
-        assert(ret_addons);
+        assert(extras);
 
-        r = chase_and_opendir(root, NULL, CHASE_PROHIBIT_SYMLINKS, NULL, &d);
-        if (r == -ENOENT)
+        _cleanup_close_ int where_fd = RET_NERRNO(open(where, O_DIRECTORY|O_CLOEXEC));
+        if (where_fd == -ENOENT)
                 return 0;
-        if (r < 0)
-                return log_error_errno(r, "Failed to open '%s/%s': %m", root, skip_leading_slash(d_name));
-
-        return boot_entries_find_unified_addons(config, dirfd(d), d_name, root, ret_addons);
+        if (where_fd < 0)
+                return log_error_errno(where_fd, "Failed to open '%s': %m", where);
+
+        return boot_entries_find_unified_extras(
+                        config,
+                        where_fd,
+                        extra_dir,
+                        only_type,
+                        where,
+                        /* suppress_seen= */ true,
+                        extras);
 }
 
-static int boot_entries_find_unified_local_addons(
+static int boot_entries_find_unified_local_extras(
                 BootConfig *config,
                 int d_fd,
-                const char *d_name,
-                const char *root,
+                const char *uki,
+                const char *where,
                 BootEntry *ret) {
 
-        _cleanup_free_ char *addon_dir = NULL;
+        _cleanup_free_ char *extra_dir = NULL;
 
         assert(ret);
 
-        addon_dir = strjoin(d_name, ".extra.d");
-        if (!addon_dir)
+        extra_dir = strjoin(uki, ".extra.d");
+        if (!extra_dir)
                 return log_oom();
 
-        return boot_entries_find_unified_addons(config, d_fd, addon_dir, root, &ret->local_addons);
+        return boot_entries_find_unified_extras(
+                        config,
+                        d_fd,
+                        extra_dir,
+                        /* only_type= */ _BOOT_ENTRY_EXTRA_TYPE_INVALID,
+                        where,
+                        /* suppress_seen= */ false,
+                        &ret->local_extras);
 }
 
 static int boot_entries_find_unified(
@@ -1369,11 +1481,11 @@ static int boot_entries_find_unified(
                         if (boot_entry_load_unified(root, source, j, p, osrelease, profile, cmdline, entry) < 0)
                                 continue;
 
-                        /* look for .efi.extra.d */
-                        (void) boot_entries_find_unified_local_addons(config, dirfd(d), de->d_name, full, entry);
+                        /* Look for .efi.extra.d/ */
+                        (void) boot_entries_find_unified_local_extras(config, dirfd(d), de->d_name, full, entry);
 
-                        /* Set up the backpointer, so that we can find the global addons */
-                        entry->global_addons = &config->global_addons[source];
+                        /* Set up the backpointer, so that we can find the global extras */
+                        entry->global_extras = &config->global_extras[source];
 
                         config->n_entries++;
                 }
@@ -1614,45 +1726,73 @@ int boot_config_finalize(BootConfig *config) {
         return 0;
 }
 
-int boot_config_load(
+static int boot_entries_load(
                 BootConfig *config,
-                const char *esp_path,
-                const char *xbootldr_path) {
+                BootEntrySource source,
+                const char *where) { /* Mount point of ESP/XBOOTLDR */
 
         int r;
 
         assert(config);
+        assert(source >= 0);
+        assert(source < _BOOT_ENTRY_SOURCE_MAX);
 
-        if (esp_path) {
-                r = boot_loader_read_conf_path(config, esp_path, "/loader/loader.conf");
-                if (r < 0)
-                        return r;
+        if (!where)
+                return 0;
 
-                r = boot_entries_find_type1(config, esp_path, BOOT_ENTRY_ESP, "/loader/entries");
-                if (r < 0)
-                        return r;
+        r = boot_entries_find_type1(config, where, source, "/loader/entries");
+        if (r < 0)
+                return r;
 
-                r = boot_entries_find_unified(config, esp_path, BOOT_ENTRY_ESP, "/EFI/Linux/");
-                if (r < 0)
-                        return r;
+        r = boot_entries_find_unified(config, where, source, "/EFI/Linux/");
+        if (r < 0)
+                return r;
 
-                r = boot_entries_find_unified_global_addons(config, esp_path, "/loader/addons/",
-                                                            &config->global_addons[BOOT_ENTRY_ESP]);
+        static const struct {
+                BootEntryExtraType extra_type;
+                const char *directory;
+        } table[] = {
+                { BOOT_ENTRY_ADDON,      "/loader/addons/"      },
+                { BOOT_ENTRY_CONFEXT,    "/loader/extensions/"  },
+                { BOOT_ENTRY_SYSEXT,     "/loader/extensions/"  },
+                { BOOT_ENTRY_CREDENTIAL, "/loader/credentials/" },
+        };
+
+        FOREACH_ELEMENT(i, table) {
+                r = boot_entries_find_unified_global_extras(
+                                config,
+                                where,
+                                i->directory,
+                                i->extra_type,
+                                &config->global_extras[source]);
                 if (r < 0)
                         return r;
         }
 
-        if (xbootldr_path) {
-                r = boot_entries_find_type1(config, xbootldr_path, BOOT_ENTRY_XBOOTLDR, "/loader/entries");
+        return 0;
+}
+
+int boot_config_load(
+                BootConfig *config,
+                const char *esp_path,
+                const char *xbootldr_path) {
+
+        int r;
+
+        assert(config);
+
+        if (esp_path) {
+                r = boot_loader_read_conf_path(config, esp_path, "/loader/loader.conf");
                 if (r < 0)
                         return r;
 
-                r = boot_entries_find_unified(config, xbootldr_path, BOOT_ENTRY_XBOOTLDR, "/EFI/Linux/");
+                r = boot_entries_load(config, BOOT_ENTRY_ESP, esp_path);
                 if (r < 0)
                         return r;
+        }
 
-                r = boot_entries_find_unified_global_addons(config, xbootldr_path, "/loader/addons/",
-                                                            &config->global_addons[BOOT_ENTRY_XBOOTLDR]);
+        if (xbootldr_path) {
+                r = boot_entries_load(config, BOOT_ENTRY_XBOOTLDR, xbootldr_path);
                 if (r < 0)
                         return r;
         }
@@ -1724,7 +1864,7 @@ int boot_config_augment_from_loader(
                 char **found_by_loader,
                 bool auto_only) {
 
-        static const BootEntryAddons no_addons = (BootEntryAddons) {};
+        static const BootEntryExtras no_extras = (BootEntryExtras) {};
         static const char *const title_table[] = {
                 /* Pretty names for a few well-known automatically discovered entries. */
                 "auto-osx",                      "macOS",
@@ -1783,7 +1923,7 @@ int boot_config_augment_from_loader(
                         .tries_left = UINT_MAX,
                         .tries_done = UINT_MAX,
                         .profile = UINT_MAX,
-                        .global_addons = &no_addons,
+                        .global_extras = &no_extras,
                 };
         }
 
@@ -1806,10 +1946,10 @@ static void boot_entry_file_list(
                 const char *field,
                 const char *root,
                 const char *p,
-                int *ret_status) {
+                int *pstatus) {
 
         assert(p);
-        assert(ret_status);
+        assert(pstatus);
 
         int status = chase_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL);
 
@@ -1824,16 +1964,23 @@ static void boot_entry_file_list(
         } else
                 printf("%s\n", p);
 
-        if (*ret_status == 0 && status < 0)
-                *ret_status = status;
+        if (*pstatus == 0 && status < 0)
+                *pstatus = status;
 }
 
-static void print_addon(
-                BootEntryAddon *addon,
-                const char *addon_str) {
+static void print_extra(
+                const BootEntry *e,
+                const BootEntryExtra *extra,
+                const char *field,
+                int *status) {
+
+        assert(e);
+        assert(extra);
+
+        boot_entry_file_list(field, e->root, extra->location, status);
 
-        printf("  %s: %s\n", addon_str, addon->location);
-        printf("      options: %s%s\n", glyph(GLYPH_TREE_RIGHT), addon->cmdline);
+        if (extra->cmdline)
+                printf("      options: %s%s\n", glyph(GLYPH_TREE_RIGHT), extra->cmdline);
 }
 
 static int indent_embedded_newlines(char *cmdline, char **ret_cmdline) {
@@ -1851,12 +1998,10 @@ static int indent_embedded_newlines(char *cmdline, char **ret_cmdline) {
                 return -ENOMEM;
 
         *ret_cmdline = TAKE_PTR(t);
-
         return 0;
 }
 
-static int print_cmdline(const BootEntry *e) {
-
+static int print_cmdline(const BootEntry *e, int *status) {
         _cleanup_free_ char *options = NULL, *combined_cmdline = NULL, *t2 = NULL;
 
         assert(e);
@@ -1877,17 +2022,20 @@ static int print_cmdline(const BootEntry *e) {
                         return log_oom();
         }
 
-        FOREACH_ARRAY(addon, e->global_addons->items, e->global_addons->n_items) {
-                print_addon(addon, "global-addon");
-                if (!strextend(&t2, " ", addon->cmdline))
-                        return log_oom();
+        FOREACH_ARRAY(extra, e->global_extras->items, e->global_extras->n_items) {
+                print_extra(e, extra, "extra", status);
+
+                if (extra->cmdline)
+                        if (!strextend(&t2, " ", extra->cmdline))
+                                return log_oom();
         }
 
-        FOREACH_ARRAY(addon, e->local_addons.items, e->local_addons.n_items) {
-                /* Add space at the beginning of addon_str to align it correctly */
-                print_addon(addon, " local-addon");
-                if (!strextend(&t2, " ", addon->cmdline))
-                        return log_oom();
+        FOREACH_ARRAY(extra, e->local_extras.items, e->local_extras.n_items) {
+                print_extra(e, extra, "extra", status);
+
+                if (extra->cmdline)
+                        if (!strextend(&t2, " ", extra->cmdline))
+                                return log_oom();
         }
 
         /* Don't print the combined cmdline if it's same as options. */
@@ -1904,19 +2052,19 @@ static int print_cmdline(const BootEntry *e) {
 }
 
 static int json_addon(
-                BootEntryAddon *addon,
-                const char *addon_str,
+                const BootEntryExtra *extra,
+                const char *extra_str,
                 sd_json_variant **array) {
 
         int r;
 
-        assert(addon);
-        assert(addon_str);
+        assert(extra);
+        assert(extra_str);
 
         r = sd_json_variant_append_arraybo(
                         array,
-                        SD_JSON_BUILD_PAIR_STRING(addon_str, addon->location),
-                        SD_JSON_BUILD_PAIR_STRING("options", addon->cmdline));
+                        SD_JSON_BUILD_PAIR_STRING(extra_str, extra->location),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("options", extra->cmdline));
         if (r < 0)
                 return log_oom();
 
@@ -1940,20 +2088,31 @@ static int json_cmdline(
                         return log_oom();
         }
 
-        FOREACH_ARRAY(addon, e->global_addons->items, e->global_addons->n_items) {
-                r = json_addon(addon, "globalAddon", &addons_array);
+        /* NB: these JSON fields are kinda obsolete, we want the more generic 'extra' ones to be used. */
+        FOREACH_ARRAY(extra, e->global_extras->items, e->global_extras->n_items) {
+                if (extra->type != BOOT_ENTRY_ADDON)
+                        continue;
+
+                r = json_addon(extra, "globalAddon", &addons_array);
                 if (r < 0)
                         return r;
-                if (!strextend(&combined_cmdline, " ", addon->cmdline))
-                        return log_oom();
+
+                if (extra->cmdline)
+                        if (!strextend(&combined_cmdline, " ", extra->cmdline))
+                                return log_oom();
         }
 
-        FOREACH_ARRAY(addon, e->local_addons.items, e->local_addons.n_items) {
-                r = json_addon(addon, "localAddon", &addons_array);
+        FOREACH_ARRAY(extra, e->local_extras.items, e->local_extras.n_items) {
+                if (extra->type != BOOT_ENTRY_ADDON)
+                        continue;
+
+                r = json_addon(extra, "localAddon", &addons_array);
                 if (r < 0)
                         return r;
-                if (!strextend(&combined_cmdline, " ", addon->cmdline))
-                        return log_oom();
+
+                if (extra->cmdline)
+                        if (!strextend(&combined_cmdline, " ", extra->cmdline))
+                                return log_oom();
         }
 
         r = sd_json_variant_merge_objectbo(
@@ -2064,7 +2223,7 @@ int show_boot_entry(
                                      *s,
                                      &status);
 
-        r = print_cmdline(e);
+        r = print_cmdline(e, &status);
         if (r < 0)
                 return r;
 
@@ -2144,6 +2303,24 @@ int boot_entry_to_json(const BootConfig *c, size_t i, sd_json_variant **ret) {
         if (r < 0)
                 return log_oom();
 
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *jextras = NULL;
+        FOREACH_ARRAY(extra, e->global_extras->items, e->global_extras->n_items) {
+                r = sd_json_variant_append_arrayb(&jextras, SD_JSON_BUILD_STRING(extra->location));
+                if (r < 0)
+                        return log_oom();
+        }
+        FOREACH_ARRAY(extra, e->local_extras.items, e->local_extras.n_items) {
+                r = sd_json_variant_append_arrayb(&jextras, SD_JSON_BUILD_STRING(extra->location));
+                if (r < 0)
+                        return log_oom();
+        }
+
+        r = sd_json_variant_merge_objectbo(
+                        &v,
+                        SD_JSON_BUILD_PAIR_CONDITION(!!jextras, "extras", SD_JSON_BUILD_VARIANT(jextras)));
+        if (r < 0)
+                return log_oom();
+
         *ret = TAKE_PTR(v);
         return 1;
 }
index d5f6930be99d13683ffd0794bfa751331c481194..677667383c9dee44ab9c90cc86504ea358a41b53 100644 (file)
@@ -20,15 +20,25 @@ typedef enum BootEntrySource {
         _BOOT_ENTRY_SOURCE_INVALID = -EINVAL,
 } BootEntrySource;
 
-typedef struct BootEntryAddon {
+typedef enum BootEntryExtraType {
+        BOOT_ENTRY_ADDON,
+        BOOT_ENTRY_CONFEXT,
+        BOOT_ENTRY_SYSEXT,
+        BOOT_ENTRY_CREDENTIAL,
+        _BOOT_ENTRY_EXTRA_TYPE_MAX,
+        _BOOT_ENTRY_EXTRA_TYPE_INVALID = -EINVAL,
+} BootEntryExtraType;
+
+typedef struct BootEntryExtra {
+        BootEntryExtraType type;
         char *location;
-        char *cmdline;
-} BootEntryAddon;
+        char *cmdline; /* only for BOOT_ENTRY_ADDON */
+} BootEntryExtra;
 
-typedef struct BootEntryAddons {
-        BootEntryAddon *items;
+typedef struct BootEntryExtras {
+        BootEntryExtra *items;
         size_t n_items;
-} BootEntryAddons;
+} BootEntryExtras;
 
 typedef struct BootEntry {
         BootEntryType type;
@@ -46,8 +56,8 @@ typedef struct BootEntry {
         char *machine_id;
         char *architecture;
         char **options;
-        BootEntryAddons local_addons;
-        const BootEntryAddons *global_addons; /* Backpointer into the BootConfig; we don't own this here */
+        BootEntryExtras local_extras;
+        const BootEntryExtras *global_extras; /* Backpointer into the BootConfig; we don't own this here */
         char *kernel;        /* linux is #defined to 1, yikes! */
         char *efi;
         char *uki;
@@ -84,7 +94,7 @@ typedef struct BootConfig {
         BootEntry *entries;
         size_t n_entries;
 
-        BootEntryAddons global_addons[_BOOT_ENTRY_SOURCE_MAX];
+        BootEntryExtras global_extras[_BOOT_ENTRY_SOURCE_MAX];
 
         ssize_t default_entry;
         ssize_t selected_entry;
index c16a295d8979f15506159dc6c0d35ff34d41c132..920b9479db0a4cd1c608ec987acd7fd2b7ace08f 100644 (file)
@@ -80,6 +80,8 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_DEFINE_FIELD(isSelected, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Addon images of the entry."),
                 SD_VARLINK_DEFINE_FIELD_BY_TYPE(addons, BootEntryAddon, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
+                SD_VARLINK_FIELD_COMMENT("Extra files associated with the entry."),
+                SD_VARLINK_DEFINE_FIELD(extras, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
                 SD_VARLINK_FIELD_COMMENT("Command line options of the entry."),
                 SD_VARLINK_DEFINE_FIELD(cmdline, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
 
index 440c3e5edfbcca2e7b9f0d7a0ca5101b84b5affc..668c0cfac45804b61386b57c014bd6e8be68b8a3 100755 (executable)
@@ -374,7 +374,7 @@ testcase_00_secureboot() {
     bootctl status | grep "Secure Boot: enabled" >/dev/null
 
     # Ensure the addon is fully loaded and parsed
-    bootctl status | grep "global-addon: loader/addons/test.addon.efi" >/dev/null
+    bootctl status | grep "extra: /boot//loader/addons/test.addon.efi" >/dev/null
     bootctl status | grep "cmdline" | grep addonfoobar >/dev/null
     grep -q addonfoobar /proc/cmdline
 }