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

2020-09-28 Thread Sergey Jin' Bostandzhyan
Hi Lennart,

sorry for the late reaction, thanks to your help (especially the busctl
monitor hint) I was able to figure out what was going on.

On Wed, Sep 09, 2020 at 06:31:26PM +0200, Lennart Poettering wrote:
[...] 
> > 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.
> 
> Consider using "busctl monitor" to see what the message your client
> receives actually contains.

The issue was that "busctl tree" returned a list of paths which I assumed would
be also present in the response of GetManagedObjects(), however - this was
not the case. Instead of being an object path on the same level as shown in 
busctl tree, the SIM path which I saw in the GetManagedObjects() reply was 
returned in a property. I simply missed that when looking at the busctl  
response in the console.

This explains why I was not getting the second path in the array when parsing
with sd_bus - it was really not there, I just thought it would be, because of
what I have seen in the busctl tree output.

So once again, thank you for the hints, it's solved now.

Kind regars,
Jin
 
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2020-09-09 Thread Lennart Poettering
On Di, 08.09.20 20:51, Sergey 'Jin' Bostandzhyan (j...@mediatomb.cc) wrote:

> >
> > 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?

No. I meant that "busctl introspect" and "busctl tree" use the XML
data, and not GetManagedObjects(). If you invoke "busctl call" then
you get what you ask for, we do no magic redirect.

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

Well, the introspection data should advertise GetManagedObjects()
method if it's supported.

>
> > > 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}}}" ?

Using sd_bus_message_enter_container() as you do, but you#d not call
it in a loop like you do, as that only makes sense if the actual type
string is "aa{oa{sa{sv}}}", i.e. starts with an array of arrays.

> I guess this is correct, what GetManagedObjects() returns is an array of
> dictionaries "a{oa{sa{sv}}}" or am I misinterpreting it?

Well, the way dbus encodes dictionaries is as "array of dictionary
entries. That's what the "a{…}" means.

> 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.

It returns 0 if you reached the end of the surrounding container, i.e. the
end of the structure or array.

> > 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?

You are supposed to be at the end of the structure/array when calling
sd_bus_message_exit_container(). Otherwise you'll get EBUSY. We should
probably document that in the man page.

> 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, );
> 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.

This does look correct to me.

> 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.

Consider using "busctl monitor" to see what the message your client
receives actually contains.

Lennart

--
Lennart Poettering, Berlin
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2020-09-08 Thread Sergey 'Jin' Bostandzhyan
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, );
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

___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2020-09-08 Thread Lennart Poettering
On Mo, 07.09.20 19:40, Sergey 'Jin' Bostandzhyan (j...@mediatomb.cc) wrote:

> 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?

Hmm, consider running this with the env var SYSTEMD_LOG_LEVEL=debug
set, maybe that explains where the error is generated.

> 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

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

> 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...

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.

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.

Lennart

--
Lennart Poettering, Berlin
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2020-09-07 Thread Sergey 'Jin' Bostandzhyan
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, );
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

/* 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 
#include 
#include 
#include 
#include 

int main()
{
sd_bus *bus = NULL;
if (sd_bus_open_system() < 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", , , 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, ) <
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;
}