[systemd-devel] libsystemd/sd_bus: trouble understanding how to parse complex responses

Sergey 'Jin' Bostandzhyan jin at mediatomb.cc
Mon Sep 7 17:40:51 UTC 2020


Hi,

I have spent quite some time on this, but I don't seem to be getting
anywhere, so I hope someone could point me in the right direction.

I am learning sd_bus, my goal is to get a list of available modem services 
from ModemManager. To make sure that what I am looking for is actually there,
I first checked with busctl:

Service org.freedesktop.ModemManager1:
└─/org
  └─/org/freedesktop
    └─/org/freedesktop/ModemManager1
      ├─/org/freedesktop/ModemManager1/Modem
      │ └─/org/freedesktop/ModemManager1/Modem/0
      └─/org/freedesktop/ModemManager1/SIM
        └─/org/freedesktop/ModemManager1/SIM/0

Service /org/freedesktop/ModemManager1:
Failed to introspect object / of service /org/freedesktop/ModemManager1: Invali argument
No objects discovered.

The tree looks fine, the error message is a bit weird?

The only things I am interested in are the paths:
/org/freedesktop/ModemManager1/Modem/0
/org/freedesktop/ModemManager1/SIM/0

The above paths may change when the modem is replugged, so I'd like to query
them dynamically.

I learned that the call to list what I need is "GetManagedObjects":
busctl call org.freedesktop.ModemManager1 /org/freedesktop/ModemManager1 org.freedesktop.DBus.ObjectManager  GetManagedObjects

It returns a data structure of the type a{oa{sa{sv}}} and the
object paths "/org/freedesktop/ModemManager1/Modem/0" and 
"/org/freedesktop/ModemManager1/SIM/0" are indeed present in its
output.

However, when trying to parse the response in the code I only get as far as
reading the first /org/freedesktop/ModemManager1/Modem/0 but I never get
to the second /org/freedesktop/ModemManager1/SIM/0 entry.

What I am doing is (full and compilable source is attached, here I am leaving 
out the error checks etc): 

while (sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{oa{sa{sv}}}") > 0)
{
    while (sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY,
                                          "oa{sa{sv}}") > 0)
    {
        const char *path;

        sd_bus_message_read_basic(m, SD_BUS_TYPE_OBJECT_PATH, &path);
        fprintf(stderr, "Object path: %s\n", path);

        sd_bus_message_skip(m, "a{sa{sv}}");

        /* exit dictionary entry */
        sd_bus_message_exit_container(m);
    } // while SD_BUS_TYPE_DICT_ENTRY

    /* exit array */
    sd_bus_message_exit_container(m);
} // while SD_BUS_TYPE_ARRAY

I do not get any errors in the "real" version (attached), but it never
gets further to the SIM entry. What am I missing?

Kind regards,
Jin

-------------- next part --------------
/* compile: gcc `pkg-config --cflags --libs libsystemd` listpaths.c -o listpaths
 
 Looking for:
 /org/freedesktop/ModemManager1/Modem/0
 /org/freedesktop/ModemManager1/SIM/0

 $ busctl tree org.freedesktop.ModemManager1 /org/freedesktop/ModemManager1
 Service org.freedesktop.ModemManager1:
 └─/org
   └─/org/freedesktop
     └─/org/freedesktop/ModemManager1
       ├─/org/freedesktop/ModemManager1/Modem
       │ └─/org/freedesktop/ModemManager1/Modem/0
       └─/org/freedesktop/ModemManager1/SIM
         └─/org/freedesktop/ModemManager1/SIM/0
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <systemd/sd-bus.h>

int main()
{
    sd_bus *bus = NULL;
    if (sd_bus_open_system(&bus) < 0)
    {
        return 1;
    }

    while (sd_bus_is_ready(bus) <= 0)
    {
        sd_bus_wait(bus, 1000);
        sd_bus_process(bus, NULL);
    }

    printf("Bus ready.\n");

    sd_bus_message *m = NULL;
    sd_bus_error error = SD_BUS_ERROR_NULL;
    int ret = sd_bus_call_method(bus, "org.freedesktop.ModemManager1",
                                 "/org/freedesktop/ModemManager1",
                                 "org.freedesktop.DBus.ObjectManager",
                                 "GetManagedObjects", &error, &m, NULL);
    if (ret < 0)
    {
        printf("Could not get object list\n");
        return ret;
    }

    while (sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY,
                                          "{oa{sa{sv}}}") > 0)
    {
        while (sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY,
                                              "oa{sa{sv}}") > 0)
        {
            const char *path;

            if (sd_bus_message_read_basic(m, SD_BUS_TYPE_OBJECT_PATH, &path) <
                0)
            {
                printf("Could not read object path\n");
                goto finish;
            }

            fprintf(stderr, "Object path: %s\n", path);

            if (sd_bus_message_skip(m, "a{sa{sv}}") < 0)
            {
                printf("Could not skip message\n");
                goto finish;
            }

            /* exit dictionary entry */
            if (sd_bus_message_exit_container(m) < 0)
            {
                printf("Could not exit container (inner loop)\n");
                goto finish;
            }
        } // while SD_BUS_TYPE_DICT_ENTRY

        /* exit array */
        if (sd_bus_message_exit_container(m) < 0)
        {
            printf("Could not exit container (outer loop)\n");
            goto finish;
        }
    } // while SD_BUS_TYPE_ARRAY

finish:
    sd_bus_error_free(&error);
    sd_bus_message_unref(m);
    sd_bus_unref(bus);
    return 0;
}


More information about the systemd-devel mailing list