summaryrefslogtreecommitdiffstats
path: root/src/shared/bootspec.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:42 +0000
commit78e9bb837c258ac0ec7712b3d612cc2f407e731e (patch)
treef515d16b6efd858a9aeb5b0ef5d6f90bf288283d /src/shared/bootspec.c
parentAdding debian version 255.5-1. (diff)
downloadsystemd-78e9bb837c258ac0ec7712b3d612cc2f407e731e.tar.xz
systemd-78e9bb837c258ac0ec7712b3d612cc2f407e731e.zip
Merging upstream version 256.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/shared/bootspec.c')
-rw-r--r--src/shared/bootspec.c604
1 files changed, 485 insertions, 119 deletions
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index f4b2fdc..4bc3ae7 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -57,6 +57,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);
@@ -78,10 +79,7 @@ static int mangle_path(
assert(ret);
/* Spec leaves open if prefixed with "/" or not, let's normalize that */
- if (path_is_absolute(p))
- c = strdup(p);
- else
- c = strjoin("/", p);
+ c = path_make_absolute(p, "/");
if (!c)
return -ENOMEM;
@@ -288,7 +286,6 @@ static int boot_entry_load_type1(
BootEntry *entry) {
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_CONF);
- unsigned line = 1;
char *c;
int r;
@@ -323,18 +320,16 @@ static int boot_entry_load_type1(
if (!tmp.root)
return log_oom();
- for (;;) {
+ for (unsigned line = 1;; line++) {
_cleanup_free_ char *buf = NULL, *field = NULL;
r = read_stripped_line(f, LONG_LINE_MAX, &buf);
- if (r == 0)
- break;
if (r == -ENOBUFS)
return log_syntax(NULL, LOG_ERR, tmp.path, line, r, "Line too long.");
if (r < 0)
return log_syntax(NULL, LOG_ERR, tmp.path, line, r, "Error while reading: %m");
-
- line++;
+ if (r == 0)
+ break;
if (IN_SET(buf[0], '#', '\0'))
continue;
@@ -421,44 +416,36 @@ void boot_config_free(BootConfig *config) {
assert(config);
free(config->default_pattern);
- free(config->timeout);
- free(config->editor);
- free(config->auto_entries);
- free(config->auto_firmware);
- free(config->console_mode);
- free(config->beep);
free(config->entry_oneshot);
free(config->entry_default);
free(config->entry_selected);
- for (size_t i = 0; i < config->n_entries; i++)
- boot_entry_free(config->entries + i);
+ FOREACH_ARRAY(i, config->entries, config->n_entries)
+ boot_entry_free(i);
free(config->entries);
+ free(config->global_addons.items);
set_free(config->inodes_seen);
}
int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) {
- unsigned line = 1;
int r;
assert(config);
assert(file);
assert(path);
- for (;;) {
+ for (unsigned line = 1;; line++) {
_cleanup_free_ char *buf = NULL, *field = NULL;
r = read_stripped_line(file, LONG_LINE_MAX, &buf);
- if (r == 0)
- break;
if (r == -ENOBUFS)
return log_syntax(NULL, LOG_ERR, path, line, r, "Line too long.");
if (r < 0)
return log_syntax(NULL, LOG_ERR, path, line, r, "Error while reading: %m");
-
- line++;
+ if (r == 0)
+ break;
if (IN_SET(buf[0], '#', '\0'))
continue;
@@ -480,20 +467,10 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) {
if (streq(field, "default"))
r = free_and_strdup(&config->default_pattern, p);
- else if (streq(field, "timeout"))
- r = free_and_strdup(&config->timeout, p);
- else if (streq(field, "editor"))
- r = free_and_strdup(&config->editor, p);
- else if (streq(field, "auto-entries"))
- r = free_and_strdup(&config->auto_entries, p);
- else if (streq(field, "auto-firmware"))
- r = free_and_strdup(&config->auto_firmware, p);
- else if (streq(field, "console-mode"))
- r = free_and_strdup(&config->console_mode, p);
- else if (streq(field, "random-seed-mode"))
- log_syntax(NULL, LOG_WARNING, path, line, 0, "'random-seed-mode' has been deprecated, ignoring.");
- else if (streq(field, "beep"))
- r = free_and_strdup(&config->beep, p);
+ else if (STR_IN_SET(field, "timeout", "editor", "auto-entries", "auto-firmware",
+ "auto-poweroff", "auto-reboot", "beep", "reboot-for-bitlocker",
+ "secure-boot-enroll", "console-mode"))
+ r = 0; /* we don't parse these in userspace, but they are OK */
else {
log_syntax(NULL, LOG_WARNING, path, line, 0, "Unknown line '%s', ignoring.", field);
continue;
@@ -609,8 +586,8 @@ static int boot_entries_find_type1(
if (r < 0)
return log_error_errno(r, "Failed to read directory '%s': %m", full);
- for (size_t i = 0; i < dentries->n_entries; i++) {
- const struct dirent *de = dentries->entries[i];
+ FOREACH_ARRAY(i, dentries->entries, dentries->n_entries) {
+ const struct dirent *de = *i;
_cleanup_fclose_ FILE *f = NULL;
if (!dirent_is_file(de))
@@ -633,7 +610,7 @@ static int boot_entries_find_type1(
r = boot_config_load_type1(config, f, root, full, de->d_name);
if (r == -ENOMEM) /* ignore all other errors */
- return r;
+ return log_oom();
}
return 0;
@@ -753,13 +730,12 @@ static int boot_entry_load_unified(
static int find_sections(
int fd,
const char *path,
- char **ret_osrelease,
- char **ret_cmdline) {
+ IMAGE_SECTION_HEADER **ret_sections,
+ PeHeader **ret_pe_header) {
- _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
- _cleanup_free_ char *osrel = NULL, *cmdline = NULL;
- _cleanup_free_ PeHeader *pe_header = NULL;
+ IMAGE_SECTION_HEADER *sections;
+ PeHeader *pe_header;
int r;
assert(fd >= 0);
@@ -773,25 +749,253 @@ static int find_sections(
if (r < 0)
return log_warning_errno(r, "Failed to parse PE sections of '%s': %m", path);
- if (!pe_is_uki(pe_header, sections))
- return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Parsed PE file '%s' is not a UKI.", path);
+ if (ret_pe_header)
+ *ret_pe_header = TAKE_PTR(pe_header);
+ if (ret_sections)
+ *ret_sections = TAKE_PTR(sections);
- r = pe_read_section_data(fd, pe_header, sections, ".osrel", PE_SECTION_SIZE_MAX, (void**) &osrel, NULL);
- if (r < 0)
- return log_warning_errno(r, "Failed to read .osrel section of '%s': %m", path);
+ return 0;
+}
+
+static int find_cmdline_section(
+ int fd,
+ const char *path,
+ IMAGE_SECTION_HEADER *sections,
+ PeHeader *pe_header,
+ char **ret_cmdline) {
+
+ int r;
+ char *cmdline = NULL, *t = NULL;
+ _cleanup_free_ char *word = NULL;
+
+ assert(path);
+
+ if (!ret_cmdline)
+ return 0;
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_SIZE_MAX, (void**) &cmdline, NULL);
- if (r < 0 && r != -ENXIO) /* cmdline is optional */
+ if (r == -ENXIO) { /* cmdline is optional */
+ *ret_cmdline = NULL;
+ return 0;
+ }
+ if (r < 0)
return log_warning_errno(r, "Failed to read .cmdline section of '%s': %m", path);
- if (ret_osrelease)
- *ret_osrelease = TAKE_PTR(osrel);
- if (ret_cmdline)
+ word = strdup(cmdline);
+ if (!word)
+ return log_oom();
+
+ /* Quick test to check if there is actual content in the addon cmdline */
+ t = delete_chars(word, NULL);
+ if (isempty(t))
+ *ret_cmdline = NULL;
+ else
*ret_cmdline = TAKE_PTR(cmdline);
return 0;
}
+static int find_osrel_section(
+ int fd,
+ const char *path,
+ IMAGE_SECTION_HEADER *sections,
+ PeHeader *pe_header,
+ char **ret_osrelease) {
+
+ int r;
+
+ if (!ret_osrelease)
+ return 0;
+
+ r = pe_read_section_data(fd, pe_header, sections, ".osrel", PE_SECTION_SIZE_MAX, (void**) ret_osrelease, NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read .osrel section of '%s': %m", path);
+
+ return 0;
+}
+
+static int find_uki_sections(
+ int fd,
+ const char *path,
+ char **ret_osrelease,
+ 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;
+
+ if (!pe_is_uki(pe_header, sections))
+ return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Parsed PE file '%s' is not a UKI.", path);
+
+ r = find_osrel_section(fd, path, sections, pe_header, ret_osrelease);
+ if (r < 0)
+ return r;
+
+ r = find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
+ if (r < 0)
+ return r;
+
+ 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;
+
+ r = find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
+ /* If addon cmdline is empty or contains just separators,
+ * don't bother tracking it.
+ * Don't check r because it cannot return <0 if cmdline is empty,
+ * as cmdline is always optional. */
+ if (!ret_cmdline)
+ return log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Addon %s contains empty cmdline and will be therefore ignored.", path);
+
+ return r;
+}
+
+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 void boot_entry_addons_done(BootEntryAddons *addons) {
+ assert(addons);
+
+ FOREACH_ARRAY(addon, addons->items, addons->n_items) {
+ free(addon->cmdline);
+ free(addon->location);
+ }
+ addons->items = mfree(addons->items);
+ addons->n_items = 0;
+}
+
+static int boot_entries_find_unified_addons(
+ BootConfig *config,
+ int d_fd,
+ const char *addon_dir,
+ const char *root,
+ BootEntryAddons *ret_addons) {
+
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *full = NULL;
+ _cleanup_(boot_entry_addons_done) BootEntryAddons addons = {};
+ int r;
+
+ assert(ret_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);
+
+ 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, 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(
+ BootConfig *config,
+ const char *root,
+ const char *d_name) {
+
+ int r;
+ _cleanup_closedir_ DIR *d = NULL;
+
+ r = chase_and_opendir(root, NULL, CHASE_PROHIBIT_SYMLINKS, NULL, &d);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to open '%s/%s': %m", root, d_name);
+
+ return boot_entries_find_unified_addons(config, dirfd(d), d_name, root, &config->global_addons);
+}
+
+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,
@@ -839,13 +1043,18 @@ static int boot_entries_find_unified(
if (!j)
return log_oom();
- if (find_sections(fd, j, &osrelease, &cmdline) < 0)
+ if (find_uki_sections(fd, j, &osrelease, &cmdline) < 0)
continue;
r = boot_entry_load_unified(root, j, osrelease, cmdline, config->entries + config->n_entries);
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++;
}
@@ -1062,6 +1271,7 @@ int boot_config_load(
int r;
assert(config);
+ config->global_addons = (BootEntryAddons) {};
if (esp_path) {
r = boot_loader_read_conf_path(config, esp_path, "/loader/loader.conf");
@@ -1075,6 +1285,10 @@ int boot_config_load(
r = boot_entries_find_unified(config, esp_path, "/EFI/Linux/");
if (r < 0)
return r;
+
+ r = boot_entries_find_unified_global_addons(config, esp_path, "/loader/addons/");
+ if (r < 0)
+ return r;
}
if (xbootldr_path) {
@@ -1238,13 +1452,155 @@ 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(" options: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), addon->cmdline);
+}
+
+static int indent_embedded_newlines(char *cmdline, char **ret_cmdline) {
+ _cleanup_free_ char *t = NULL;
+ _cleanup_strv_free_ char **ts = NULL;
+
+ assert(ret_cmdline);
+
+ ts = strv_split_newlines(cmdline);
+ if (!ts)
+ return -ENOMEM;
+
+ t = strv_join(ts, "\n ");
+ if (!t)
+ return -ENOMEM;
+
+ *ret_cmdline = TAKE_PTR(t);
+
+ return 0;
+}
+
+static int print_cmdline(
+ const BootEntry *e,
+ const BootEntryAddons *global_arr) {
+
+ _cleanup_free_ char *options = NULL, *combined_cmdline = NULL, *t2 = NULL;
+
+ assert(e);
+
+ if (!strv_isempty(e->options)) {
+ _cleanup_free_ char *t = NULL;
+
+ options = strv_join(e->options, " ");
+ if (!options)
+ return log_oom();
+
+ if (indent_embedded_newlines(options, &t) < 0)
+ return log_oom();
+
+ printf(" options: %s\n", t);
+ t2 = strdup(options);
+ if (!t2)
+ return log_oom();
+ }
+
+ FOREACH_ARRAY(addon, global_arr->items, global_arr->n_items) {
+ print_addon(addon, "global-addon");
+ if (!strextend(&t2, " ", addon->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();
+ }
+
+ /* Don't print the combined cmdline if it's same as options. */
+ if (streq_ptr(t2, options))
+ return 0;
+
+ if (indent_embedded_newlines(t2, &combined_cmdline) < 0)
+ return log_oom();
+
+ if (combined_cmdline)
+ printf(" cmdline: %s\n", combined_cmdline);
+
+ return 0;
+}
+
+static int json_addon(
+ BootEntryAddon *addon,
+ const char *addon_str,
+ JsonVariant **array) {
+
+ int r;
+
+ assert(addon);
+ assert(addon_str);
+
+ r = json_variant_append_arrayb(
+ array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(addon_str, JSON_BUILD_STRING(addon->location)),
+ JSON_BUILD_PAIR("options", JSON_BUILD_STRING(addon->cmdline))));
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
+static int json_cmdline(
+ const BootEntry *e,
+ const BootEntryAddons *global_arr,
+ const char *def_cmdline,
+ JsonVariant **v) {
+
+ _cleanup_free_ char *combined_cmdline = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *addons_array = NULL;
+ int r;
+
+ assert(e);
+
+ if (def_cmdline) {
+ combined_cmdline = strdup(def_cmdline);
+ if (!combined_cmdline)
+ return log_oom();
+ }
+
+ FOREACH_ARRAY(addon, global_arr->items, global_arr->n_items) {
+ r = json_addon(addon, "globalAddon", &addons_array);
+ if (r < 0)
+ return r;
+ if (!strextend(&combined_cmdline, " ", addon->cmdline))
+ return log_oom();
+ }
+
+ FOREACH_ARRAY(addon, e->local_addons.items, e->local_addons.n_items) {
+ r = json_addon(addon, "localAddon", &addons_array);
+ if (r < 0)
+ return r;
+ if (!strextend(&combined_cmdline, " ", addon->cmdline))
+ return log_oom();
+ }
+
+ r = json_variant_merge_objectb(
+ v, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("addons", JSON_BUILD_VARIANT(addons_array)),
+ JSON_BUILD_PAIR_CONDITION(combined_cmdline, "cmdline", JSON_BUILD_STRING(combined_cmdline))));
+ if (r < 0)
+ return log_oom();
+ return 0;
+}
+
int show_boot_entry(
const BootEntry *e,
+ const BootEntryAddons *global_addons,
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. */
@@ -1323,24 +1679,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, global_addons);
+ if (r < 0)
+ return r;
if (e->device_tree)
boot_entry_file_list("devicetree", e->root, e->device_tree, &status);
@@ -1354,6 +1695,71 @@ int show_boot_entry(
return -status;
}
+int boot_entry_to_json(const BootConfig *c, size_t i, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_free_ char *opts = NULL;
+ const BootEntry *e;
+ int r;
+
+ assert(c);
+ assert(ret);
+
+ if (i >= c->n_entries) {
+ *ret = NULL;
+ return 0;
+ }
+
+ e = c->entries + i;
+
+ 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))),
+ JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)),
+ JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)),
+ JSON_BUILD_PAIR_CONDITION(e->root, "root", JSON_BUILD_STRING(e->root)),
+ JSON_BUILD_PAIR_CONDITION(e->title, "title", JSON_BUILD_STRING(e->title)),
+ JSON_BUILD_PAIR_CONDITION(boot_entry_title(e), "showTitle", JSON_BUILD_STRING(boot_entry_title(e))),
+ JSON_BUILD_PAIR_CONDITION(e->sort_key, "sortKey", JSON_BUILD_STRING(e->sort_key)),
+ 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)),
+ JSON_BUILD_PAIR_CONDITION(e->device_tree, "devicetree", JSON_BUILD_STRING(e->device_tree)),
+ JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay))));
+ 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. */
+ r = json_variant_merge_objectb(
+ &v, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("isReported", JSON_BUILD_BOOLEAN(e->reported_by_loader)),
+ JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
+ JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done)),
+ JSON_BUILD_PAIR_CONDITION(c->default_entry >= 0, "isDefault", JSON_BUILD_BOOLEAN(i == (size_t) c->default_entry)),
+ JSON_BUILD_PAIR_CONDITION(c->selected_entry >= 0, "isSelected", JSON_BUILD_BOOLEAN(i == (size_t) c->selected_entry))));
+
+ if (r < 0)
+ return log_oom();
+
+ r = json_cmdline(e, &c->global_addons, opts, &v);
+ if (r < 0)
+ return log_oom();
+
+ *ret = TAKE_PTR(v);
+ return 1;
+}
+
int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
int r;
@@ -1363,48 +1769,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))),
- JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)),
- JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)),
- JSON_BUILD_PAIR_CONDITION(e->root, "root", JSON_BUILD_STRING(e->root)),
- JSON_BUILD_PAIR_CONDITION(e->title, "title", JSON_BUILD_STRING(e->title)),
- JSON_BUILD_PAIR_CONDITION(boot_entry_title(e), "showTitle", JSON_BUILD_STRING(boot_entry_title(e))),
- JSON_BUILD_PAIR_CONDITION(e->sort_key, "sortKey", JSON_BUILD_STRING(e->sort_key)),
- 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)),
- JSON_BUILD_PAIR_CONDITION(e->device_tree, "devicetree", JSON_BUILD_STRING(e->device_tree)),
- JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay))));
- 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. */
- r = json_variant_merge_objectb(
- &v, JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR("isReported", JSON_BUILD_BOOLEAN(e->reported_by_loader)),
- JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
- JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done)),
- JSON_BUILD_PAIR_CONDITION(config->default_entry >= 0, "isDefault", JSON_BUILD_BOOLEAN(i == (size_t) config->default_entry)),
- JSON_BUILD_PAIR_CONDITION(config->selected_entry >= 0, "isSelected", JSON_BUILD_BOOLEAN(i == (size_t) config->selected_entry))));
-
+ r = boot_entry_to_json(config, i, &v);
if (r < 0)
return log_oom();
@@ -1413,12 +1780,12 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
return log_oom();
}
- json_variant_dump(array, json_format | JSON_FORMAT_EMPTY_ARRAY, NULL, NULL);
-
- } else {
+ return json_variant_dump(array, json_format | JSON_FORMAT_EMPTY_ARRAY, NULL, NULL);
+ } else
for (size_t n = 0; n < config->n_entries; n++) {
r = show_boot_entry(
config->entries + n,
+ &config->global_addons,
/* show_as_default= */ n == (size_t) config->default_entry,
/* show_as_selected= */ n == (size_t) config->selected_entry,
/* show_discovered= */ true);
@@ -1428,7 +1795,6 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
if (n+1 < config->n_entries)
putchar('\n');
}
- }
return 0;
}