]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-bus: handle non-string keys in dictionaries in JSON dump
authorFrantisek Sumsal <frantisek@sumsal.cz>
Tue, 12 May 2026 15:09:41 +0000 (17:09 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 12 May 2026 22:50:32 +0000 (07:50 +0900)
JSON only supports string keys in objects, but D-Bus specification is a
bit more lenient and allows dict entries to have any basic type as key.
Let's stringify allowed non-string keys so that we can represent them as
JSON objects.

Relevant snippet from the D-Bus specification:

  A DICT_ENTRY works exactly like a struct, but rather than parentheses
  it uses curly braces, and it has more restrictions. The restrictions
  are: it occurs only as an array element type; it has exactly two
  single complete types inside the curly braces; the first single
  complete type (the "key") must be a basic type rather than a container
  type. Implementations must not accept dict entries outside of arrays,
  must not accept dict entries with zero, one, or more than two fields,
  and must not accept dict entries with non-basic-typed keys. A dict
  entry is always a key-value pair.

Resolves: #32904

src/libsystemd/sd-bus/bus-dump-json.c
test/units/TEST-74-AUX-UTILS.busctl.sh

index 8238ac587e90824ef9083fc0fecbd1d0bec3350d..ae3e8061946b58ffe7452d18933b6af4137cf800 100644 (file)
@@ -72,6 +72,7 @@ static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) {
         CLEANUP_ARRAY(elements, n_elements, sd_json_variant_unref_many);
 
         for (;;) {
+                _cleanup_(sd_json_variant_unrefp) sd_json_variant *key = NULL;
                 const char *contents;
                 char type;
 
@@ -94,11 +95,35 @@ static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) {
                 if (r < 0)
                         return r;
 
-                r = json_transform_one(m, elements + n_elements);
+                r = json_transform_one(m, &key);
                 if (r < 0)
                         return r;
 
-                n_elements++;
+                /* JSON only supports string keys in objects, but D-Bus specification is a bit more lenient
+                 * and allows dict entries to have any basic type as key. Let's stringify allowed non-string
+                 * keys so that we can represent them as JSON objects. */
+                if (!sd_json_variant_is_string(key)) {
+                        _cleanup_free_ char *s = NULL;
+
+                        if (!IN_SET(sd_json_variant_type(key),
+                                    SD_JSON_VARIANT_BOOLEAN,
+                                    SD_JSON_VARIANT_INTEGER,
+                                    SD_JSON_VARIANT_REAL,
+                                    SD_JSON_VARIANT_UNSIGNED))
+                                return -EINVAL;
+
+                        r = sd_json_variant_format(key, /* flags= */ 0, &s);
+                        if (r < 0)
+                                return r;
+
+                        key = sd_json_variant_unref(key);
+
+                        r = sd_json_variant_new_string(&key, s);
+                        if (r < 0)
+                                return r;
+                }
+
+                elements[n_elements++] = TAKE_PTR(key);
 
                 r = json_transform_one(m, elements + n_elements);
                 if (r < 0)
index 875f353b00ea4d47fcc5f76c219b727ecac73178..54170cf5b5507ede2f2289b54ed30aded0f0a7ae 100755 (executable)
@@ -43,6 +43,12 @@ busctl call --json=short \
 busctl call -j \
             org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Properties \
             GetAll s "org.freedesktop.systemd1.Manager" | jq -c
+# Do the same with D-Bus' org.freedesktop.DBus.Debug.Stats interface which also provides some
+# complex signatures that caused issues during the JSON conversion in the past
+# See: https://github.com/systemd/systemd/issues/32904
+busctl call --json=pretty \
+            org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.Debug.Stats \
+            GetStats | jq -c
 busctl call --verbose --timeout=60 --expect-reply=yes \
             org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
             ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"