kernel-install: Add --json option for inspect verb

In mkosi, we can't use kernel-install directly but we do want to
mimick its behavior as much as possible. Let's make that easier by
making it easy to fetch data from kernel-install as JSON.

To get all the necessary data, we extend the inspect verb to also
allow passing in a kernel version and initrds, to mimick the "add"
verb. The kernel version is used to determine the "Entry Directory",
and in absence of auto-detection of kernel version in kernel-install
we have to allow users to pass it.

We also add --no-pager while we're at it.
This commit is contained in:
Daan De Meyer 2023-10-23 11:18:35 +02:00
parent ad6216cbc7
commit bdd36c003d
3 changed files with 138 additions and 23 deletions

View file

@ -40,7 +40,9 @@
<command>kernel-install</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">inspect</arg>
<arg choice="opt"><replaceable>KERNEL-VERSION</replaceable></arg>
<arg choice="opt"><replaceable>KERNEL-IMAGE</replaceable></arg>
<arg choice="opt" rep="repeat"><replaceable>INITRD-FILE</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
@ -176,11 +178,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><command>inspect [<replaceable>KERNEL-IMAGE</replaceable>]</command></term>
<term>
<command>inspect [[<replaceable>KERNEL-VERSION</replaceable>] <replaceable>KERNEL-IMAGE</replaceable>] [<replaceable>INITRD-FILE</replaceable> ...]</command>
</term>
<listitem>
<para>Shows the various paths and parameters configured or auto-detected. In particular shows the
values of the various <varname>$KERNEL_INSTALL_*</varname> environment variables listed
below.</para>
below. The <option>--json</option> option can be used to get the output of this verb as a JSON
object.</para>
<xi:include href="version-info.xml" xpointer="v251"/>
</listitem>
@ -298,6 +303,8 @@
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager"/>
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View file

@ -13,6 +13,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "find-esp.h"
#include "format-table.h"
#include "id128-util.h"
#include "kernel-image.h"
#include "main-func.h"
@ -32,6 +33,8 @@ static bool arg_verbose = false;
static char *arg_esp_path = NULL;
static char *arg_xbootldr_path = NULL;
static int arg_make_entry_directory = -1; /* tristate */
static PagerFlags arg_pager_flags = 0;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
@ -860,8 +863,6 @@ static int context_build_arguments(Context *c) {
break;
case ACTION_INSPECT:
assert(!c->version);
assert(!c->initrds);
verb = "add|remove";
break;
@ -1081,39 +1082,92 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
static int verb_inspect(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
_cleanup_free_ char *joined = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
int r;
c->action = ACTION_INSPECT;
if (argc >= 2) {
if (argc == 2) {
r = context_set_kernel(c, argv[1]);
if (r < 0)
return r;
} else if (argc >= 3) {
r = context_set_version(c, argv[1]);
if (r < 0)
return r;
r = context_set_kernel(c, argv[2]);
if (r < 0)
return r;
r = context_set_initrds(c, strv_skip(argv, 3));
if (r < 0)
return r;
}
r = context_prepare_execution(c);
if (r < 0)
return r;
printf("%sBoot Loader Entries:%s\n", ansi_underline(), ansi_normal());
printf(" $BOOT: %s\n", c->boot_root);
printf(" Token: %s\n", c->entry_token);
puts("");
t = table_new_vertical();
if (!t)
return log_oom();
printf("%sUsing plugins:%s\n", ansi_underline(), ansi_normal());
strv_print_full(c->plugins, " ");
puts("");
r = table_add_many(t,
TABLE_FIELD, "Machine ID",
TABLE_ID128, c->machine_id,
TABLE_FIELD, "Kernel Image Type",
TABLE_STRING, kernel_image_type_to_string(c->kernel_image_type),
TABLE_FIELD, "Layout",
TABLE_STRING, context_get_layout(c),
TABLE_FIELD, "Boot Root",
TABLE_STRING, c->boot_root,
TABLE_FIELD, "Entry Token Type",
TABLE_STRING, boot_entry_token_type_to_string(c->entry_token_type),
TABLE_FIELD, "Entry Token",
TABLE_STRING, c->entry_token,
TABLE_FIELD, "Entry Directory",
TABLE_STRING, c->entry_dir,
TABLE_FIELD, "Kernel Version",
TABLE_STRING, c->version,
TABLE_FIELD, "Kernel",
TABLE_STRING, c->kernel,
TABLE_FIELD, "Initrds",
TABLE_STRV, c->initrds,
TABLE_FIELD, "Initrd Generator",
TABLE_STRING, c->initrd_generator,
TABLE_FIELD, "UKI Generator",
TABLE_STRING, c->uki_generator,
TABLE_FIELD, "Plugins",
TABLE_STRV, c->plugins,
TABLE_FIELD, "Plugin Environment",
TABLE_STRV, c->envp);
if (r < 0)
return table_log_add_error(r);
printf("%sPlugin environment:%s\n", ansi_underline(), ansi_normal());
strv_print_full(c->envp, " ");
puts("");
if (arg_json_format_flags & JSON_FORMAT_OFF) {
r = table_add_many(t,
TABLE_FIELD, "Plugin Arguments",
TABLE_STRV, strv_skip(c->argv, 1));
if (r < 0)
return table_log_add_error(r);
}
printf("%sPlugin arguments:%s\n", ansi_underline(), ansi_normal());
joined = strv_join(strv_skip(c->argv, 1), " ");
printf(" %s\n", strna(joined));
table_set_ersatz_string(t, TABLE_ERSATZ_UNSET);
return 0;
for (size_t row = 1; row < table_get_rows(t); row++) {
_cleanup_free_ char *name = NULL;
name = strdup(table_get_at(t, row, 0));
if (!name)
return log_oom();
r = table_set_json_field_name(t, row - 1, delete_chars(name, " "));
if (r < 0)
return log_error_errno(r, "Failed to set JSON field name: %m");
}
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, /* show_header= */ false);
}
static int help(void) {
@ -1127,9 +1181,9 @@ static int help(void) {
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
"%2$sAdd and remove kernel and initrd images to and from /boot%3$s\n"
"\nUsage:\n"
" kernel-install [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE...]\n"
" kernel-install [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD ...]\n"
" kernel-install [OPTIONS...] remove KERNEL-VERSION\n"
" kernel-install [OPTIONS...] inspect [KERNEL-IMAGE]\n"
" kernel-install [OPTIONS...] inspect [KERNEL-VERSION] KERNEL-IMAGE [INITRD ...]\n"
"\n"
"Options:\n"
" -h --help Show this help\n"
@ -1141,6 +1195,9 @@ static int help(void) {
" 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"
" --no-pager Do not pipe inspect output into a pager\n"
" --json=pretty|short|off\n"
" Generate JSON output\n"
"\n"
"This program may also be invoked as 'installkernel':\n"
" installkernel [OPTIONS...] VERSION VMLINUZ [MAP] [INSTALLATION-DIR]\n"
@ -1162,6 +1219,8 @@ static int parse_argv(int argc, char *argv[], Context *c) {
ARG_BOOT_PATH,
ARG_MAKE_ENTRY_DIRECTORY,
ARG_ENTRY_TOKEN,
ARG_NO_PAGER,
ARG_JSON,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
@ -1171,6 +1230,8 @@ static int parse_argv(int argc, char *argv[], Context *c) {
{ "boot-path", required_argument, NULL, ARG_BOOT_PATH },
{ "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY },
{ "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "json", required_argument, NULL, ARG_JSON },
{}
};
int t, r;
@ -1222,6 +1283,16 @@ static int parse_argv(int argc, char *argv[], Context *c) {
return r;
break;
case ARG_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -1236,7 +1307,7 @@ static int run(int argc, char* argv[]) {
static const Verb verbs[] = {
{ "add", 3, VERB_ANY, 0, verb_add },
{ "remove", 2, VERB_ANY, 0, verb_remove },
{ "inspect", 1, 2, VERB_DEFAULT, verb_inspect },
{ "inspect", 1, VERB_ANY, VERB_DEFAULT, verb_inspect },
{}
};
_cleanup_(context_done) Context c = {

View file

@ -269,3 +269,40 @@ test -d "$BOOT_ROOT/hoge/1.1.1"
test ! -e "$BOOT_ROOT/hoge/1.1.1"
test -d "$BOOT_ROOT/hoge"
rmdir "$BOOT_ROOT/hoge"
###########################################
# tests for --json=
###########################################
output="$("$kernel_install" -v --json=pretty inspect 1.1.1 "$D/sources/linux")"
diff -u <(echo "$output") - <<EOF
{
"MachineID" : "3e0484f3634a418b8e6a39e8828b03e3",
"KernelImageType" : "unknown",
"Layout" : "other",
"BootRoot" : "$BOOT_ROOT",
"EntryTokenType" : "literal",
"EntryToken" : "the-token",
"EntryDirectory" : "$BOOT_ROOT/the-token/1.1.1",
"KernelVersion" : "1.1.1",
"Kernel" : "$D/sources/linux",
"Initrds" : null,
"InitrdGenerator" : "none",
"UKIGenerator" : null,
"Plugins" : [
"$D/00-skip.install"
],
"PluginEnvironment" : [
"LC_COLLATE=C.UTF-8",
"KERNEL_INSTALL_VERBOSE=1",
"KERNEL_INSTALL_IMAGE_TYPE=unknown",
"KERNEL_INSTALL_MACHINE_ID=3e0484f3634a418b8e6a39e8828b03e3",
"KERNEL_INSTALL_ENTRY_TOKEN=the-token",
"KERNEL_INSTALL_BOOT_ROOT=$BOOT_ROOT",
"KERNEL_INSTALL_LAYOUT=other",
"KERNEL_INSTALL_INITRD_GENERATOR=none",
"KERNEL_INSTALL_UKI_GENERATOR=",
"KERNEL_INSTALL_STAGING_AREA=/tmp/kernel-install.staging.XXXXXX"
]
}
EOF