]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
measure: add json output
authorLennart Poettering <lennart@poettering.net>
Fri, 12 Aug 2022 13:23:44 +0000 (15:23 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 19 Aug 2022 21:26:09 +0000 (23:26 +0200)
man/systemd-measure.xml
src/boot/measure.c

index e6b8d31a881e4b1d4e51a6f7b854f9b408c5737c..66d19a0072551ff62a57d16cad0b64ec0d016c0b 100644 (file)
         <literal>sha512</literal>.</para></listitem>
       </varlistentry>
 
+      <xi:include href="standard-options.xml" xpointer="json" />
+      <xi:include href="standard-options.xml" xpointer="no-pager" />
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
index 29568c5b0d129c6e8520f770de78be1a71398c90..58c027f1b1dbc53fe8b1c3723f8569752a42e622 100644 (file)
@@ -8,6 +8,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "hexdecoct.h"
+#include "json.h"
 #include "main-func.h"
 #include "openssl-util.h"
 #include "parse-argument.h"
@@ -23,6 +24,8 @@
 
 static char *arg_sections[_UNIFIED_SECTION_MAX] = {};
 static char **arg_banks = NULL;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
+static PagerFlags arg_pager_flags = 0;
 
 STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
 
@@ -47,15 +50,18 @@ static int help(int argc, char *argv[], void *userdata) {
                "  status             Show current PCR values\n"
                "  calculate          Calculate expected PCR values\n"
                "\n%3$sOptions:%4$s\n"
-               "  -h --help          Show this help\n"
-               "     --version       Print version\n"
-               "     --linux=PATH    Path Linux kernel ELF image\n"
-               "     --osrel=PATH    Path to os-release file\n"
-               "     --cmdline=PATH  Path to file with kernel command line\n"
-               "     --initrd=PATH   Path to initrd image\n"
-               "     --splash=PATH   Path to splash bitmap\n"
-               "     --dtb=PATH      Path to Devicetree file\n"
-               "     --bank=DIGEST   Select TPM bank (SHA1, SHA256)\n"
+               "  -h --help              Show this help\n"
+               "     --version           Print version\n"
+               "     --no-pager          Do not pipe output into a pager\n"
+               "     --linux=PATH        Path Linux kernel ELF image\n"
+               "     --osrel=PATH        Path to os-release file\n"
+               "     --cmdline=PATH      Path to file with kernel command line\n"
+               "     --initrd=PATH       Path to initrd image\n"
+               "     --splash=PATH       Path to splash bitmap\n"
+               "     --dtb=PATH          Path to Devicetree file\n"
+               "     --bank=DIGEST       Select TPM bank (SHA1, SHA256)\n"
+               "     --json=MODE         Output as JSON\n"
+               "  -j                     Same as --json=pretty on tty, --json=short otherwise\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -70,6 +76,7 @@ static int help(int argc, char *argv[], void *userdata) {
 static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
                 _ARG_SECTION_FIRST,
                 ARG_LINUX = _ARG_SECTION_FIRST,
                 ARG_OSREL,
@@ -79,18 +86,21 @@ static int parse_argv(int argc, char *argv[]) {
                 _ARG_SECTION_LAST,
                 ARG_DTB = _ARG_SECTION_LAST,
                 ARG_BANK,
+                ARG_JSON,
         };
 
         static const struct option options[] = {
-                { "help",    no_argument,       NULL, 'h'         },
-                { "version", no_argument,       NULL, ARG_VERSION },
-                { "linux",   required_argument, NULL, ARG_LINUX   },
-                { "osrel",   required_argument, NULL, ARG_OSREL   },
-                { "cmdline", required_argument, NULL, ARG_CMDLINE },
-                { "initrd",  required_argument, NULL, ARG_INITRD  },
-                { "splash",  required_argument, NULL, ARG_SPLASH  },
-                { "dtb",     required_argument, NULL, ARG_DTB     },
-                { "bank",    required_argument, NULL, ARG_BANK    },
+                { "help",        no_argument,       NULL, 'h'             },
+                { "no-pager",    no_argument,       NULL, ARG_NO_PAGER    },
+                { "version",     no_argument,       NULL, ARG_VERSION     },
+                { "linux",       required_argument, NULL, ARG_LINUX       },
+                { "osrel",       required_argument, NULL, ARG_OSREL       },
+                { "cmdline",     required_argument, NULL, ARG_CMDLINE     },
+                { "initrd",      required_argument, NULL, ARG_INITRD      },
+                { "splash",      required_argument, NULL, ARG_SPLASH      },
+                { "dtb",         required_argument, NULL, ARG_DTB         },
+                { "bank",        required_argument, NULL, ARG_BANK        },
+                { "json",        required_argument, NULL, ARG_JSON        },
                 {}
         };
 
@@ -102,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) {
         /* Make sure the arguments list and the section list, stays in sync */
         assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1);
 
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hj", options, NULL)) >= 0)
                 switch (c) {
 
                 case 'h':
@@ -112,6 +122,10 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_VERSION:
                         return version();
 
+                case ARG_NO_PAGER:
+                        arg_pager_flags |= PAGER_DISABLE;
+                        break;
+
                 case _ARG_SECTION_FIRST..._ARG_SECTION_LAST: {
                         UnifiedSection section = c - _ARG_SECTION_FIRST;
 
@@ -134,6 +148,17 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case 'j':
+                        arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+                        break;
+
+                case ARG_JSON:
+                        r = parse_json_argument(optarg, &arg_json_format_flags);
+                        if (r <= 0)
+                                return r;
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -313,6 +338,7 @@ static int measure_pcr(PcrState *pcr_states, size_t n) {
 
 static int verb_calculate(int argc, char *argv[], void *userdata) {
         _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         size_t n = 0;
         int r;
 
@@ -351,17 +377,49 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
                 return r;
 
         for (size_t i = 0; i < n; i++) {
-                _cleanup_free_ char *hd = NULL, *b = NULL;
-
-                hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
-                if (!hd)
-                        return log_oom();
+                _cleanup_free_ char *b = NULL;
 
                 b = strdup(EVP_MD_name(pcr_states[i].md));
                 if (!b)
                         return log_oom();
 
-                printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, ascii_strlower(b), hd);
+                ascii_strlower(b);
+
+                if (arg_json_format_flags & JSON_FORMAT_OFF) {
+                        _cleanup_free_ char *hd = NULL;
+
+                        hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
+                        if (!hd)
+                                return log_oom();
+
+                        printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, b, hd);
+                } else {
+                        _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL;
+
+                        r = json_build(&bv,
+                                       JSON_BUILD_ARRAY(
+                                                       JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
+                                                                       JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))
+                                                       )
+                                       )
+                        );
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build JSON object: %m");
+
+                        r = json_variant_set_field(&w, b, bv);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add bank info to object: %m");
+
+                }
+        }
+
+        if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+
+                if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
+                        pager_open(arg_pager_flags);
+
+                json_variant_dump(w, arg_json_format_flags, stdout, NULL);
         }
 
         return 0;
@@ -441,6 +499,7 @@ static int validate_stub(void) {
 }
 
 static int verb_status(int argc, char *argv[], void *userdata) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
         static const struct {
                 uint32_t nr;
@@ -460,7 +519,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) {
 
                 STRV_FOREACH(bank, arg_banks) {
-                        _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL, *f = NULL;
+                        _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL;
                         _cleanup_free_ void *h = NULL;
                         size_t l;
 
@@ -481,27 +540,60 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
 
-                        f = hexmem(h, l);
-                        if (!h)
-                                return log_oom();
-
-                        if (bank == arg_banks) {
-                                /* before the first line for each PCR, write a short descriptive text to
-                                 * stderr, and leave the primary content on stdout */
-                                fflush(stdout);
-                                fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
-                                        ansi_grey(),
-                                        relevant_pcrs[i].nr,
-                                        relevant_pcrs[i].description,
-                                        memeqzero(h, l) ? " (NOT SET!)" : "",
-                                        ansi_normal());
-                                fflush(stderr);
+                        if (arg_json_format_flags & JSON_FORMAT_OFF) {
+                                _cleanup_free_ char *f = NULL;
+
+                                f = hexmem(h, l);
+                                if (!h)
+                                        return log_oom();
+
+                                if (bank == arg_banks) {
+                                        /* before the first line for each PCR, write a short descriptive text to
+                                         * stderr, and leave the primary content on stdout */
+                                        fflush(stdout);
+                                        fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
+                                                ansi_grey(),
+                                                relevant_pcrs[i].nr,
+                                                relevant_pcrs[i].description,
+                                                memeqzero(h, l) ? " (NOT SET!)" : "",
+                                                ansi_normal());
+                                        fflush(stderr);
+                                }
+
+                                printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f);
+
+                        } else {
+                                _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *a = NULL;
+
+                                r = json_build(&bv,
+                                               JSON_BUILD_OBJECT(
+                                                               JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(relevant_pcrs[i].nr)),
+                                                               JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(h, l))
+                                               )
+                                );
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to build JSON object: %m");
+
+                                a = json_variant_ref(json_variant_by_key(v, b));
+
+                                r = json_variant_append_array(&a, bv);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to append PCR entry to JSON array: %m");
+
+                                r = json_variant_set_field(&v, b, a);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to add bank info to object: %m");
                         }
-
-                        printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f);
                 }
         }
 
+        if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+                if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
+                        pager_open(arg_pager_flags);
+
+                json_variant_dump(v, arg_json_format_flags, stdout, NULL);
+        }
+
         return 0;
 }