From: Yu Watanabe Date: Sun, 27 Apr 2025 14:06:34 +0000 (+0900) Subject: sd-bus: introduce sd_bus_message_dump_json() X-Git-Tag: v258-rc1~713^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c111392fd84c984709c9bf0ecad02bc8da1b7c5;p=thirdparty%2Fsystemd.git sd-bus: introduce sd_bus_message_dump_json() We have already expose sd_bus_message_dump(). Let's also expose how we convert dbus message into json format in busctl. --- diff --git a/man/rules/meson.build b/man/rules/meson.build index 433fffc5719..e78e6ee0c8a 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -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', diff --git a/man/sd_bus_message_dump.xml b/man/sd_bus_message_dump.xml index 1ebeece8f16..d3fd55b5707 100644 --- a/man/sd_bus_message_dump.xml +++ b/man/sd_bus_message_dump.xml @@ -18,6 +18,7 @@ sd_bus_message_dump + sd_bus_message_dump_json Produce a string representation of a message for debugging purposes @@ -32,12 +33,14 @@ FILE *f uint64_t flags - - - SD_BUS_MESSAGE_DUMP_WITH_HEADER, - SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY - + + int sd_bus_message_dump_json + sd_bus_message *m + uint64_t flags + sd_json_variant **ret + + @@ -49,16 +52,38 @@ intended to be used for debugging purposes, and the output is neither stable nor designed to be machine readable. - The flags parameter may be used to modify the output. With - SD_BUS_MESSAGE_DUMP_WITH_HEADER, a header that specifies the message type and flags - and some additional metadata is printed. When SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY is - not passed, the contents of the whole message are printed. When it is passed, - only the current container in printed. + The sd_bus_message_dump_json() function converts the DBus message + m to a JSON variant and stores it in ret. The caller should + call sd_json_variant_unref() for the acquired JSON variant after use. Unlike + sd_bus_message_dump(), the function itself does not print anything. To print the + DBus message in the JSON format, please pass the returned JSON variant to + sd_json_variant_dump(). - Note that this function moves the read pointer of the message. It may be necessary to reset the + The flags parameter may be used to modify the output, and is a combination + of zero or more of the following flags: + + + + SD_BUS_MESSAGE_DUMP_WITH_HEADER + + 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. + + + + SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY + + Only the current container will be printed or converted. When the flag is + not specified, the contents of the whole message will be printed or converted. + + + + + Note that these functions move the read pointer of the message. It may be necessary to reset the position afterwards, for example with sd_bus_message_rewind3. + @@ -102,6 +127,7 @@ systemd1 sd-bus3 + sd-json3 diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index cec300e0a0b..a4d5a60f8df 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -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; diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 4e7e1a73635..370e3b7c922 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -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; diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index 06b02a2f448..000b56880f1 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -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 index 00000000000..d753f50a0cd --- /dev/null +++ b/src/libsystemd/sd-bus/bus-dump-json.c @@ -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))); +} diff --git a/src/libsystemd/sd-bus/fuzz-bus-message.c b/src/libsystemd/sd-bus/fuzz-bus-message.c index ca7091eff27..e369f532c91 100644 --- a/src/libsystemd/sd-bus/fuzz-bus-message.c +++ b/src/libsystemd/sd-bus/fuzz-bus-message.c @@ -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); diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 78c6cc10a46..0d62ca892f4 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -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 */