]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
kernel-install: Add --json option for inspect verb 29675/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 23 Oct 2023 09:18:35 +0000 (11:18 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 6 Nov 2023 13:43:09 +0000 (14:43 +0100)
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.

man/kernel-install.xml
src/kernel-install/kernel-install.c
src/kernel-install/test-kernel-install.sh

index 64137ce61c22d7159a3b09820a432e9ac55f72a2..822c2892598cf902bad2dd3515c7ccd397603340 100644 (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>
 
         </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>
 
       <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>
 
index b72e2ac79a04e9e55bc69361e6acb32f3fb3cd6e..1c5621e6c86fa78648b4ce0adf50b8430d182a1f 100644 (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();
+
+        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);
+
+        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("%sUsing plugins:%s\n", ansi_underline(), ansi_normal());
-        strv_print_full(c->plugins, "    ");
-        puts("");
+        table_set_ersatz_string(t, TABLE_ERSATZ_UNSET);
 
-        printf("%sPlugin environment:%s\n", ansi_underline(), ansi_normal());
-        strv_print_full(c->envp, "    ");
-        puts("");
+        for (size_t row = 1; row < table_get_rows(t); row++) {
+                _cleanup_free_ char *name = NULL;
 
-        printf("%sPlugin arguments:%s\n", ansi_underline(), ansi_normal());
-        joined = strv_join(strv_skip(c->argv, 1), " ");
-        printf("    %s\n", strna(joined));
+                name = strdup(table_get_at(t, row, 0));
+                if (!name)
+                        return log_oom();
 
-        return 0;
+                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 = {
index 30bee06b533ffb191b054d72e863a97c84e43342..931fae5372fd0e407dc4926134ef8e4eb5248555 100755 (executable)
@@ -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