]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-bus: introduce sd_bus_message_dump_json()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 27 Apr 2025 14:06:34 +0000 (23:06 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 30 Apr 2025 10:40:37 +0000 (19:40 +0900)
We have already expose sd_bus_message_dump(). Let's also expose how
we convert dbus message into json format in busctl.

man/rules/meson.build
man/sd_bus_message_dump.xml
src/busctl/busctl.c
src/libsystemd/libsystemd.sym
src/libsystemd/meson.build
src/libsystemd/sd-bus/bus-dump-json.c [new file with mode: 0644]
src/libsystemd/sd-bus/fuzz-bus-message.c
src/systemd/sd-bus.h

index 433fffc5719887c7c410939b5a19425b3a5388ae..e78e6ee0c8a106a003f069ee1c971642736a02fa 100644 (file)
@@ -326,7 +326,7 @@ manpages = [
  ['sd_bus_message_append_strv', '3', [], ''],
  ['sd_bus_message_at_end', '3', [], ''],
  ['sd_bus_message_copy', '3', [], ''],
- ['sd_bus_message_dump', '3', [], ''],
+ ['sd_bus_message_dump', '3', ['sd_bus_message_dump_json'], ''],
  ['sd_bus_message_get_cookie', '3', ['sd_bus_message_get_reply_cookie'], ''],
  ['sd_bus_message_get_monotonic_usec',
   '3',
index 1ebeece8f16577d58cda2c8ffb6da0fcd4c33277..d3fd55b57070908389bab36bd33afeb2f77e5054 100644 (file)
@@ -18,6 +18,7 @@
 
   <refnamediv>
     <refname>sd_bus_message_dump</refname>
+    <refname>sd_bus_message_dump_json</refname>
 
     <refpurpose>Produce a string representation of a message for debugging purposes</refpurpose>
   </refnamediv>
         <paramdef>FILE *<parameter>f</parameter></paramdef>
         <paramdef>uint64_t <parameter>flags</parameter></paramdef>
       </funcprototype>
-    </funcsynopsis>
 
-    <para>
-      <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>,
-      <constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant>
-    </para>
+      <funcprototype>
+        <funcdef>int sd_bus_message_dump_json</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>uint64_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     intended to be used for debugging purposes, and the output is neither stable nor designed to be machine
     readable.</para>
 
-    <para>The <parameter>flags</parameter> parameter may be used to modify the output. With
-    <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>, a header that specifies the message type and flags
-    and some additional metadata is printed. When <constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant> is
-    not passed, the contents of the whole message are printed. When it <emphasis>is</emphasis> passed,
-    only the current container in printed.</para>
+    <para>The <function>sd_bus_message_dump_json()</function> function converts the DBus message
+    <parameter>m</parameter> to a JSON variant and stores it in <parameter>ret</parameter>. The caller should
+    call <function>sd_json_variant_unref()</function> for the acquired JSON variant after use. Unlike
+    <function>sd_bus_message_dump()</function>, the function itself does not print anything. To print the
+    DBus message in the JSON format, please pass the returned JSON variant to
+    <function>sd_json_variant_dump()</function>.</para>
 
-    <para>Note that this function moves the read pointer of the message. It may be necessary to reset the
+    <para>The <parameter>flags</parameter> parameter may be used to modify the output, and is a combination
+    of zero or more of the following flags:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant></term>
+
+        <listitem><para>The header of the message that specifies the message type, flags, and several more
+        additional metadata will be printed or included in the resulting JSON object.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant></term>
+
+        <listitem><para>Only the current container will be printed or converted. When the flag is
+        <emphasis>not</emphasis> specified, the contents of the whole message will be printed or converted.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Note that these functions move the read pointer of the message. It may be necessary to reset the
     position afterwards, for example with
     <citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     </para>
+
   </refsect1>
 
   <refsect1>
     <para><simplelist type="inline">
       <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>sd-json</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
     </simplelist></para>
   </refsect1>
 
index cec300e0a0b7352aa996701ad9e16c896cf1b326..a4d5a60f8df1d617d4b289a4419d52426f5c8fb9 100644 (file)
@@ -74,8 +74,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
 #define NAME_IS_ACQUIRED INT_TO_PTR(1)
 #define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
 
-static int json_transform_message(sd_bus_message *m, sd_json_variant **ret);
-
 static int acquire_bus(bool set_monitor, sd_bus **ret) {
         _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
         _cleanup_close_ int pin_fd = -EBADF;
@@ -1201,44 +1199,19 @@ static int message_pcap(sd_bus_message *m, FILE *f) {
 }
 
 static int message_json(sd_bus_message *m, FILE *f) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
-        char e[2];
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
         int r;
-        usec_t ts;
 
-        r = json_transform_message(m, &v);
+        assert(m);
+
+        r = sd_bus_message_dump_json(m, SD_BUS_MESSAGE_DUMP_WITH_HEADER, &v);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to build JSON object from DBus message: %m");
+
+        r = sd_json_variant_dump(v, arg_json_format_flags, f, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to show JSON object: %m");
 
-        e[0] = m->header->endian;
-        e[1] = 0;
-
-        ts = m->realtime;
-        if (ts == 0)
-                ts = now(CLOCK_REALTIME);
-
-        r = sd_json_buildo(&w,
-                SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
-                SD_JSON_BUILD_PAIR("endian", SD_JSON_BUILD_STRING(e)),
-                SD_JSON_BUILD_PAIR("flags", SD_JSON_BUILD_INTEGER(m->header->flags)),
-                SD_JSON_BUILD_PAIR("version", SD_JSON_BUILD_INTEGER(m->header->version)),
-                SD_JSON_BUILD_PAIR("cookie", SD_JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
-                SD_JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", SD_JSON_BUILD_INTEGER(m->reply_cookie)),
-                SD_JSON_BUILD_PAIR("timestamp-realtime", SD_JSON_BUILD_UNSIGNED(ts)),
-                SD_JSON_BUILD_PAIR_CONDITION(!!m->sender, "sender", SD_JSON_BUILD_STRING(m->sender)),
-                SD_JSON_BUILD_PAIR_CONDITION(!!m->destination, "destination", SD_JSON_BUILD_STRING(m->destination)),
-                SD_JSON_BUILD_PAIR_CONDITION(!!m->path, "path", SD_JSON_BUILD_STRING(m->path)),
-                SD_JSON_BUILD_PAIR_CONDITION(!!m->interface, "interface", SD_JSON_BUILD_STRING(m->interface)),
-                SD_JSON_BUILD_PAIR_CONDITION(!!m->member, "member", SD_JSON_BUILD_STRING(m->member)),
-                SD_JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", SD_JSON_BUILD_INTEGER(m->monotonic)),
-                SD_JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", SD_JSON_BUILD_INTEGER(m->realtime)),
-                SD_JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", SD_JSON_BUILD_INTEGER(m->seqnum)),
-                SD_JSON_BUILD_PAIR_CONDITION(!!m->error.name, "error_name", SD_JSON_BUILD_STRING(m->error.name)),
-                SD_JSON_BUILD_PAIR("payload", SD_JSON_BUILD_VARIANT(v)));
-        if (r < 0)
-                return log_error_errno(r, "Failed to build JSON object: %m");
-
-        sd_json_variant_dump(w, arg_json_format_flags, f, NULL);
         return 0;
 }
 
@@ -1721,355 +1694,6 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, FDSe
         return 0;
 }
 
-static int json_transform_one(sd_bus_message *m, sd_json_variant **ret);
-
-static int json_transform_and_append(sd_bus_message *m, sd_json_variant **array) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *element = NULL;
-        int r;
-
-        assert(m);
-        assert(array);
-
-        r = json_transform_one(m, &element);
-        if (r < 0)
-                return r;
-
-        r = sd_json_variant_append_array(array, element);
-        if (r < 0)
-                return log_error_errno(r, "Failed to append json element to array: %m");
-
-        return 0;
-}
-
-static int json_transform_array_or_struct(sd_bus_message *m, sd_json_variant **ret) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
-        int r;
-
-        assert(m);
-        assert(ret);
-
-        r = sd_json_variant_new_array(&array, NULL, 0);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate json empty array: %m");
-
-        for (;;) {
-                r = sd_bus_message_at_end(m, false);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-                if (r > 0)
-                        break;
-
-                r = json_transform_and_append(m, &array);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = TAKE_PTR(array);
-        return 0;
-}
-
-static int json_transform_variant(sd_bus_message *m, const char *contents, sd_json_variant **ret) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *value = NULL;
-        int r;
-
-        assert(m);
-        assert(contents);
-        assert(ret);
-
-        r = json_transform_one(m, &value);
-        if (r < 0)
-                return r;
-
-        r = sd_json_buildo(ret,
-                          SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(contents)),
-                          SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_VARIANT(value)));
-        if (r < 0)
-                return log_error_errno(r, "Failed to build json object: %m");
-
-        return r;
-}
-
-static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) {
-        sd_json_variant **elements = NULL;
-        size_t n_elements = 0;
-        int r;
-
-        assert(m);
-        assert(ret);
-
-        CLEANUP_ARRAY(elements, n_elements, sd_json_variant_unref_many);
-
-        for (;;) {
-                const char *contents;
-                char type;
-
-                r = sd_bus_message_at_end(m, false);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-                if (r > 0)
-                        break;
-
-                r = sd_bus_message_peek_type(m, &type, &contents);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                assert(type == 'e');
-
-                if (!GREEDY_REALLOC(elements, n_elements + 2))
-                        return log_oom();
-
-                r = sd_bus_message_enter_container(m, type, contents);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = json_transform_one(m, elements + n_elements);
-                if (r < 0)
-                        return r;
-
-                n_elements++;
-
-                r = json_transform_one(m, elements + n_elements);
-                if (r < 0)
-                        return r;
-
-                n_elements++;
-
-                r = sd_bus_message_exit_container(m);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-        }
-
-        r = sd_json_variant_new_object(ret, elements, n_elements);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create new json object: %m");
-
-        return 0;
-}
-
-static int json_transform_one(sd_bus_message *m, sd_json_variant **ret) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
-        const char *contents;
-        char type;
-        int r;
-
-        assert(m);
-        assert(ret);
-
-        r = sd_bus_message_peek_type(m, &type, &contents);
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        switch (type) {
-
-        case SD_BUS_TYPE_BYTE: {
-                uint8_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_unsigned(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform byte: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_BOOLEAN: {
-                int b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_boolean(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform boolean: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_INT16: {
-                int16_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_integer(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform int16: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_UINT16: {
-                uint16_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_unsigned(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform uint16: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_INT32: {
-                int32_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_integer(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform int32: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_UINT32: {
-                uint32_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_unsigned(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform uint32: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_INT64: {
-                int64_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_integer(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform int64: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_UINT64: {
-                uint64_t b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_unsigned(&v, b);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform uint64: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_DOUBLE: {
-                double d;
-
-                r = sd_bus_message_read_basic(m, type, &d);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_real(&v, d);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform double: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_STRING:
-        case SD_BUS_TYPE_OBJECT_PATH:
-        case SD_BUS_TYPE_SIGNATURE: {
-                const char *s;
-
-                r = sd_bus_message_read_basic(m, type, &s);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_json_variant_new_string(&v, s);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform double: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_UNIX_FD: {
-                int fd;
-
-                r = sd_bus_message_read_basic(m, type, &fd);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = json_variant_new_fd_info(&v, fd);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to transform fd: %m");
-
-                break;
-        }
-
-        case SD_BUS_TYPE_ARRAY:
-        case SD_BUS_TYPE_VARIANT:
-        case SD_BUS_TYPE_STRUCT:
-                r = sd_bus_message_enter_container(m, type, contents);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                if (type == SD_BUS_TYPE_VARIANT)
-                        r = json_transform_variant(m, contents, &v);
-                else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
-                        r = json_transform_dict_array(m, &v);
-                else
-                        r = json_transform_array_or_struct(m, &v);
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_message_exit_container(m);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                break;
-
-        default:
-                assert_not_reached();
-        }
-
-        *ret = TAKE_PTR(v);
-        return 0;
-}
-
-static int json_transform_message(sd_bus_message *m, sd_json_variant **ret) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
-        const char *type;
-        int r;
-
-        assert(m);
-        assert(ret);
-
-        assert_se(type = sd_bus_message_get_signature(m, false));
-
-        r = json_transform_array_or_struct(m, &v);
-        if (r < 0)
-                return r;
-
-        r = sd_json_buildo(ret,
-                          SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type)),
-                          SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_VARIANT(v)));
-        if (r < 0)
-                return log_error_errno(r, "Failed to build json object: %m");
-
-        return 0;
-}
-
 static int call(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -2146,7 +1770,7 @@ static int call(int argc, char **argv, void *userdata) {
                 if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
                         pager_open(arg_pager_flags);
 
-                r = json_transform_message(reply, &v);
+                r = sd_bus_message_dump_json(reply, /* flags = */ 0, &v);
                 if (r < 0)
                         return r;
 
@@ -2256,7 +1880,7 @@ static int get_property(int argc, char **argv, void *userdata) {
                         if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
                                 pager_open(arg_pager_flags);
 
-                        r = json_transform_variant(reply, contents, &v);
+                        r = sd_bus_message_dump_json(reply, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY, &v);
                         if (r < 0)
                                 return r;
 
@@ -2304,7 +1928,7 @@ static int on_bus_signal_impl(sd_bus_message *msg) {
                 if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
                         pager_open(arg_pager_flags);
 
-                r = json_transform_message(msg, &v);
+                r = sd_bus_message_dump_json(msg, /* flags = */ 0, &v);
                 if (r < 0)
                         return r;
 
index 4e7e1a73635be0c79e0cd641fd9916531b02a840..370e3b7c922f7203e1bbf39d1be86fc51c9ea70d 100644 (file)
@@ -1062,6 +1062,7 @@ global:
 
 LIBSYSTEMD_258 {
 global:
+        sd_bus_message_dump_json;
         sd_device_enumerator_add_all_parents;
         sd_json_variant_type_from_string;
         sd_json_variant_type_to_string;
index 06b02a2f44822d773b821e00fe9a9aa6964f70df..000b56880f19d566c01b68f7ad1d08b749ccb7de 100644 (file)
@@ -65,6 +65,7 @@ sd_bus_sources = files(
         'sd-bus/bus-convenience.c',
         'sd-bus/bus-creds.c',
         'sd-bus/bus-dump.c',
+        'sd-bus/bus-dump-json.c',
         'sd-bus/bus-error.c',
         'sd-bus/bus-internal.c',
         'sd-bus/bus-introspect.c',
diff --git a/src/libsystemd/sd-bus/bus-dump-json.c b/src/libsystemd/sd-bus/bus-dump-json.c
new file mode 100644 (file)
index 0000000..d753f50
--- /dev/null
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "json-util.h"
+#include "macro.h"
+#include "memory-util.h"
+#include "time-util.h"
+
+static int json_transform_one(sd_bus_message *m, sd_json_variant **ret);
+
+static int json_transform_array_or_struct(sd_bus_message *m, sd_json_variant **ret) {
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
+        int r;
+
+        assert(m);
+        assert(ret);
+
+        r = sd_json_variant_new_array(&array, NULL, 0);
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                r = sd_bus_message_at_end(m, false);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        break;
+
+                _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+                r = json_transform_one(m, &v);
+                if (r < 0)
+                        return r;
+
+                r = sd_json_variant_append_array(&array, v);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(array);
+        return 0;
+}
+
+static int json_transform_variant(sd_bus_message *m, const char *contents, sd_json_variant **ret) {
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *value = NULL;
+        int r;
+
+        assert(m);
+        assert(contents);
+        assert(ret);
+
+        r = json_transform_one(m, &value);
+        if (r < 0)
+                return r;
+
+        return sd_json_buildo(
+                        ret,
+                        SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(contents)),
+                        SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_VARIANT(value)));
+}
+
+static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) {
+        sd_json_variant **elements = NULL;
+        size_t n_elements = 0;
+        int r;
+
+        assert(m);
+        assert(ret);
+
+        CLEANUP_ARRAY(elements, n_elements, sd_json_variant_unref_many);
+
+        for (;;) {
+                const char *contents;
+                char type;
+
+                r = sd_bus_message_at_end(m, false);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        break;
+
+                r = sd_bus_message_peek_type(m, &type, &contents);
+                if (r < 0)
+                        return r;
+
+                assert(type == 'e');
+
+                if (!GREEDY_REALLOC(elements, n_elements + 2))
+                        return -ENOMEM;
+
+                r = sd_bus_message_enter_container(m, type, contents);
+                if (r < 0)
+                        return r;
+
+                r = json_transform_one(m, elements + n_elements);
+                if (r < 0)
+                        return r;
+
+                n_elements++;
+
+                r = json_transform_one(m, elements + n_elements);
+                if (r < 0)
+                        return r;
+
+                n_elements++;
+
+                r = sd_bus_message_exit_container(m);
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_json_variant_new_object(ret, elements, n_elements);
+}
+
+static int json_transform_one(sd_bus_message *m, sd_json_variant **ret) {
+        const char *contents;
+        char type;
+        int r;
+
+        assert(m);
+        assert(ret);
+
+        r = sd_bus_message_peek_type(m, &type, &contents);
+        if (r < 0)
+                return r;
+
+        switch (type) {
+
+        case SD_BUS_TYPE_BYTE: {
+                uint8_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_unsigned(ret, b);
+        }
+
+        case SD_BUS_TYPE_BOOLEAN: {
+                int b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_boolean(ret, b);
+        }
+
+        case SD_BUS_TYPE_INT16: {
+                int16_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_integer(ret, b);
+        }
+
+        case SD_BUS_TYPE_UINT16: {
+                uint16_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_unsigned(ret, b);
+        }
+
+        case SD_BUS_TYPE_INT32: {
+                int32_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_integer(ret, b);
+        }
+
+        case SD_BUS_TYPE_UINT32: {
+                uint32_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_unsigned(ret, b);
+        }
+
+        case SD_BUS_TYPE_INT64: {
+                int64_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_integer(ret, b);
+        }
+
+        case SD_BUS_TYPE_UINT64: {
+                uint64_t b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_unsigned(ret, b);
+        }
+
+        case SD_BUS_TYPE_DOUBLE: {
+                double d;
+
+                r = sd_bus_message_read_basic(m, type, &d);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_real(ret, d);
+        }
+
+        case SD_BUS_TYPE_STRING:
+        case SD_BUS_TYPE_OBJECT_PATH:
+        case SD_BUS_TYPE_SIGNATURE: {
+                const char *s;
+
+                r = sd_bus_message_read_basic(m, type, &s);
+                if (r < 0)
+                        return r;
+
+                return sd_json_variant_new_string(ret, s);
+        }
+
+        case SD_BUS_TYPE_UNIX_FD: {
+                int fd;
+
+                r = sd_bus_message_read_basic(m, type, &fd);
+                if (r < 0)
+                        return r;
+
+                return json_variant_new_fd_info(ret, fd);
+        }
+
+        case SD_BUS_TYPE_ARRAY:
+        case SD_BUS_TYPE_VARIANT:
+        case SD_BUS_TYPE_STRUCT: {
+                _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+
+                r = sd_bus_message_enter_container(m, type, contents);
+                if (r < 0)
+                        return r;
+
+                if (type == SD_BUS_TYPE_VARIANT)
+                        r = json_transform_variant(m, contents, &v);
+                else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
+                        r = json_transform_dict_array(m, &v);
+                else
+                        r = json_transform_array_or_struct(m, &v);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_exit_container(m);
+                if (r < 0)
+                        return r;
+
+                *ret = TAKE_PTR(v);
+                return 0;
+        }
+
+        default:
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "sd-bus: an invalid message type (signature=%s, type=%c, contents=%s).",
+                                       sd_bus_message_get_signature(m, /* complete = */ false), type, strna(contents));
+        }
+}
+
+static int json_transform_message(sd_bus_message *m, const char *type, sd_json_variant **ret) {
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        int r;
+
+        assert(m);
+        assert(type);
+        assert(ret);
+
+        r = json_transform_array_or_struct(m, &v);
+        if (r < 0)
+                return r;
+
+        return sd_json_buildo(
+                        ret,
+                        SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type)),
+                        SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_VARIANT(v)));
+}
+
+_public_ int sd_bus_message_dump_json(sd_bus_message *m, uint64_t flags, sd_json_variant **ret) {
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return((flags & ~_SD_BUS_MESSAGE_DUMP_KNOWN_FLAGS) == 0, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = sd_bus_message_rewind(m, !FLAGS_SET(flags, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY));
+        if (r < 0)
+                return r;
+
+        const char *type = sd_bus_message_get_signature(m, !FLAGS_SET(flags, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY));
+        if (!type)
+                return -EINVAL;
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        if (FLAGS_SET(flags, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY))
+                r = json_transform_variant(m, type, &v);
+        else
+                r = json_transform_message(m, type, &v);
+        if (r < 0)
+                return r;
+
+        if (!FLAGS_SET(flags, SD_BUS_MESSAGE_DUMP_WITH_HEADER)) {
+                *ret = TAKE_PTR(v);
+                return 0;
+        }
+
+        usec_t ts = m->realtime;
+        if (ts == 0)
+                ts = now(CLOCK_REALTIME);
+
+        return sd_json_buildo(
+                        ret,
+                        SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
+                        SD_JSON_BUILD_PAIR("endian", SD_JSON_BUILD_STRING(CHAR_TO_STR(m->header->endian))),
+                        SD_JSON_BUILD_PAIR("flags", SD_JSON_BUILD_INTEGER(m->header->flags)),
+                        SD_JSON_BUILD_PAIR("version", SD_JSON_BUILD_INTEGER(m->header->version)),
+                        SD_JSON_BUILD_PAIR("cookie", SD_JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
+                        SD_JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", SD_JSON_BUILD_INTEGER(m->reply_cookie)),
+                        SD_JSON_BUILD_PAIR("timestamp-realtime", SD_JSON_BUILD_UNSIGNED(ts)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!m->sender, "sender", SD_JSON_BUILD_STRING(m->sender)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!m->destination, "destination", SD_JSON_BUILD_STRING(m->destination)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!m->path, "path", SD_JSON_BUILD_STRING(m->path)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!m->interface, "interface", SD_JSON_BUILD_STRING(m->interface)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!m->member, "member", SD_JSON_BUILD_STRING(m->member)),
+                        SD_JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", SD_JSON_BUILD_INTEGER(m->monotonic)),
+                        SD_JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", SD_JSON_BUILD_INTEGER(m->realtime)),
+                        SD_JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", SD_JSON_BUILD_INTEGER(m->seqnum)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!m->error.name, "error_name", SD_JSON_BUILD_STRING(m->error.name)),
+                        SD_JSON_BUILD_PAIR("payload", SD_JSON_BUILD_VARIANT(v)));
+}
index ca7091eff27a2ca2bfbd54c4afe84cc375a16e18..e369f532c91888ddf7aab0aeef1264eb177f42ac 100644 (file)
@@ -33,7 +33,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
                 assert_se(g = memstream_init(&ms));
 
-        sd_bus_message_dump(m, g ?: stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+        (void) sd_bus_message_dump(m, g, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        if (sd_bus_message_dump_json(m, SD_BUS_MESSAGE_DUMP_WITH_HEADER, &v) >= 0)
+                (void) sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY | SD_JSON_FORMAT_COLOR_AUTO, g, NULL);
 
         r = sd_bus_message_rewind(m, true);
         assert_se(r >= 0);
index 78c6cc10a463aa5b42bd8458fdf269e07cf2cd65..0d62ca892f42a95b18f268a3a0954844b8419d7e 100644 (file)
@@ -28,6 +28,7 @@
 #include "sd-bus-vtable.h"
 #include "sd-event.h"
 #include "sd-id128.h"
+#include "sd-json.h"
 
 _SD_BEGIN_DECLARATIONS;
 
@@ -257,6 +258,7 @@ int sd_bus_message_rewind(sd_bus_message *m, int complete);
 int sd_bus_message_sensitive(sd_bus_message *m);
 
 int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags);
+int sd_bus_message_dump_json(sd_bus_message *m, uint64_t flags, sd_json_variant **ret);
 
 /* Bus management */