Search code examples
clinuxdbus

libdbus: get a list of strings from DBusMessage arguments


I've recently been learning about libdbus by writing a simple tool that sends mpris messages to active media players.

The first step of this has been fetch a list of media players via DBus:

    // send the ListNames command over the DBus
    DBusMessage* msg = dbus_message_new_method_call(
        "org.freedesktop.DBus",
        "/",
        "org.freedesktop.DBus",
        "ListNames");

    // send the message
    DBusPendingCall* call;
    dbus_bool_t success = dbus_connection_send_with_reply(con, msg, &call, 100);
    if (!success || call == NULL) return 1;

    // wait for a reply
    dbus_pending_call_block(call);

    // check reply
    DBusMessage* reply = dbus_pending_call_steal_reply(call);

I must now parse the DBus reply for a list of media player names. After reading the reference material, I believe this should be possible via dbus_message_get_args, as the documentation notes:

arrays of string, object path, and signature are supported; but these are returned as allocated memory and must be freed with dbus_free_string_array(). [...] To get a string array pass in "char ***array_location" and "int *n_elements".

This led me to write the following:

    // pull list of players from reply
    const int MAX_ARGS = 10;
    const char** args;
    dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY, &args, &MAX_ARGS);

But upon checking my DBusError, I get the following message:

DBus Error: org.freedesktop.DBus.Error.InvalidArgs Argument 0 is specified to be an array of "unknown", but is actually an array of "string"

I originally assumed I must have been passing a bad DBUS_TYPE but couldnt find one more suitable than DBUS_TYPE_ARRAY.

I'm aware that this is also possible by using a dbus_message_iter call but the documentation seems to suggest this is not necessary, and even suggests using get_message_args where possible.

So the actual question is... am I missing something about the documentation, or calling the function wrong? Or is getting a list of strings actually not possible with this method?


Solution

  • I would suggest reading the libdbus source code to be sure, but something like this is my recollection:

    dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
            DBUS_TYPE_STRING, &args, &len, DBUS_TYPE_INVALID);
    

    Note that len is not a maximum length you can specify: libdbus will write the actual length of the array there. I'm not 100% sure of the argument order here, you might have to read the source to be sure.

    If you are now thinking "but that would be a weird API with bad documentation"... you would be right: As Philip mentioned using libdbus is not a good choice to make in 2020.