From 0d1506d4a8e100c7ba311d77840b11afc4b76dd6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Mar 2022 16:08:36 +0100 Subject: [PATCH] bootctl: optionally, output entries in JSON format Replaces: #18387 Fixes: #18094 --- man/bootctl.xml | 6 +++++- src/boot/bootctl.c | 51 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/man/bootctl.xml b/man/bootctl.xml index ff49a8043c8..8d5b1d3d2c3 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -92,7 +92,10 @@ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification, as well as any other entries discovered or automatically generated by a boot loader implementing the Boot Loader - Interface. + Interface. + + JSON output may be requested with . + @@ -315,6 +318,7 @@ + diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index cc79f51655f..8596939ff35 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -70,6 +70,7 @@ static enum { ARG_ENTRY_TOKEN_AUTO, } arg_entry_token_type = ARG_ENTRY_TOKEN_AUTO; static char *arg_entry_token = NULL; +static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); @@ -1420,6 +1421,8 @@ static int help(int argc, char *argv[], void *userdata) { " Create $BOOT/ENTRY-TOKEN/ directory\n" " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n" " Entry token to use for this installation\n" + " --json=pretty|short|off\n" + " Generate JSON output\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -1441,6 +1444,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_GRACEFUL, ARG_MAKE_ENTRY_DIRECTORY, ARG_ENTRY_TOKEN, + ARG_JSON, }; static const struct option options[] = { @@ -1458,6 +1462,7 @@ static int parse_argv(int argc, char *argv[]) { { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, + { "json", required_argument, NULL, ARG_JSON }, {} }; @@ -1552,6 +1557,13 @@ static int parse_argv(int argc, char *argv[]) { } break; + case ARG_JSON: + r = parse_json_argument(optarg, &arg_json_format_flags); + if (r <= 0) + return r; + + break; + case '?': return -EINVAL; @@ -1812,7 +1824,44 @@ static int verb_list(int argc, char *argv[], void *userdata) { else (void) boot_entries_augment_from_loader(&config, efi_entries, /* only_auto= */ false); - if (config.n_entries == 0) + if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) { + + pager_open(arg_pager_flags); + + for (size_t i = 0; i < config.n_entries; i++) { + _cleanup_free_ char *opts = NULL; + 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_build(&v, JSON_BUILD_OBJECT( + 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(); + + json_variant_dump(v, arg_json_format_flags, stdout, NULL); + } + + } else if (config.n_entries == 0) log_info("No boot loader entries found."); else { pager_open(arg_pager_flags);