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

Sergey 'Jin' Bostandzhyan jin at mediatomb.cc
Tue Sep 8 18:51:59 UTC 2020


Hi Lennart,

On Tue, Sep 08, 2020 at 03:58:34PM +0200, Lennart Poettering wrote:
> > 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?
> 
> Hmm, consider running this with the env var SYSTEMD_LOG_LEVEL=debug
> set, maybe that explains where the error is generated.

interestingly enough the same error does not happen today, it just returns
the tree, thanks for the log level hint though - that is indeed
very helpful!
 
> > 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
> 
> GetManagedObjects() is only available in some services, it's an
> optional interface. "busctl" uses the XML introspection logic to
> enumerate objects, i.e. this:
>
> https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format

Oh, so you mean that when I use "busctl call" it would detect and fake a 
GetManagedObjects() method invocation and internally use XML introspection?

That's good to know since I was using busctl to debug/test and I was assuming 
that it does the calls as instructed.

> Unlike GetManagedObjects() the XML introspection stuff is implemented
> by most services.

Understood.

Is it somehow possible to figure this out via command line tools if the
problem is in the missing GetManagedObjects() implementation or not?

> > while (sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{oa{sa{sv}}}") > 0)
> > {
> 
> Hmm, this is what you'd write when you have an array of arrays...

Uhm, how do I correctly "enter" a container like this one: "a{oa{sa{sv}}}" ?

I guess this is correct, what GetManagedObjects() returns is an array of
dictionaries "a{oa{sa{sv}}}" or am I misinterpreting it?
 
> sd_bus_message_enter_container() returns < 0 on error, == 0 if we
> reached the end of the surrounding container, and > 0 if all was
> good. I'd recommend checking for error cases which you ignore
> here. The errors tell you where things go wrong.

I already had error checks everywhere except loops, so I added that now.

sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "oa{sa{sv}}")
returns 1 on the first call, thats where I get the first path that
equals to "/org/freedesktop/ModemManager1/Modem/0" and it returns 0 on the
next invocation.

This leads me to the next question below...

> Normally, you#d enter the array once and then iterate through the
> dict entries contained therein. i.e. unless there's an "aa" object
> somewhere (i.e. "array of array") you'd do the while loop around the
> dict entries, not the array object.

Could you please clarify how iteration works in sd_bus sense? Is my 
understanding correct that I need to use sd_bus_message_skip() before
exiting the container in order to iterate to the next item, if I am not
interested in parsing all of the nested items? Or can I just "exit" at any
time and expect the next "enter" invocation to fast forward to the
next entry?

Based on what you are saying the inner while loop that I had right after
entering the array should have returned all items. So I start by entering
the array:

// starting with a{oa{sa{sv}}}
sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{oa{sa{sv}}}")

And loop over dictionary key-value pairs:

// brings me down into oa{sa{sv}}}, I am only interested in the object paths:
while (sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "oa{sa{sv}}") > 0) {
    sd_bus_message_read_basic(m, SD_BUS_TYPE_OBJECT_PATH, &path);
    sd_bus_message_skip(m, "a{sa{sv}}");
    sd_bus_message_exit_container(m);
}

Does the above look correct? As mentioned, I am not getting errors, return
codes are fine, so it all looks normal, but I am only reading out
the first path, on the second iteration
sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "oa{sa{sv}}") returns 0 which aborts the loop.

At this point I am not sure if sd_bus actually behaves correctly and
GetManagedObjects() returns only one path or if I messed up the parsing of
the reply somehow? And I am not sure how to check that...

I would like to avoid parsing XML if I can, but of course that option is
still there; nevertheless, I would first like to understand what is going on 
with the GetManagedObjects() call and if the problem is really there or 
somewhere else.

Kind regars,
Jin



More information about the systemd-devel mailing list