mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
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.
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
@@ -32,12 +33,14 @@
|
||||
<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>
|
||||
@@ -49,16 +52,38 @@
|
||||
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>
|
||||
@@ -102,6 +127,7 @@
|
||||
<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>
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
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)));
|
||||
r = sd_json_variant_dump(v, arg_json_format_flags, f, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object: %m");
|
||||
return log_error_errno(r, "Failed to show 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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
346
src/libsystemd/sd-bus/bus-dump-json.c
Normal file
346
src/libsystemd/sd-bus/bus-dump-json.c
Normal file
@@ -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)));
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user