Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D-Bus how to create and send a Dict?

Tags:

c

ipc

dbus

I have a process which exposes a method to DBus with one of the arguments taking the following type signature a{sv}:

Dict of {String, Variant}

The libDBus documentation for dbus_message_append_args fails to provide adequate reference for this. Some information appears in the specification under container-types, specifically:

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.


On attempting to append a dict I receive the following error message:

type dict_entry isn't supported yet in dbus_message_append_args_valist

Although I'm actually using dbus_message_append_args(I guess the error message is somewhat off).

There are two other alternatives to dbus_message_append_args() using either:

dbus_message_iter_append_basic() and dbus_message_iter_append_fixed_array()

While I can create an empty Dict container with the following:

  const char * container_d_sig = "{sv}";
  DBusMessageIter iter, sub;
  dbus_message_iter_init_append(msg, &iter);
  dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, container_d_sig, &sub);
  dbus_message_iter_close_container(&iter, &sub);

Neither of the append methods appear to support adding a struct. Not sure what to try here...

like image 656
user3467349 Avatar asked Apr 30 '15 16:04

user3467349


2 Answers

First, about D-Bus libraries: you talk about dbus-glib in several places but the functions you refer to are not part of dbus-glib but libdbus. If you are still trying to find the best way to use D-Bus, I suggest you forget both of these: libdbus is very low-level (it's documentation even starts with "If you use this low-level API directly, you're signing up for some pain") and dbus-glib is deprecated. The best D-Bus API currently is GDBus which is part of GLib GIO: it's a far better designed API than either of the other two, well tested and supported.

Now, as for the actual question, documentation for dbus_message_append_args() does say it quite clearly:

To append variable-length basic types, or any more complex value, you have to use an iterator rather than this function.

In other words you should use dbus_message_iter_open_container() to prepare the iterator until it is pointing to somewhere where you can use dbus_message_iter_append_basic(). Note that in your example the dictionary is a container, the dictionary entry is a container and the variant is a container... In other words it gets pretty complex quite fast. If you really want to do it, look at e.g. Connman code for examples.

As I mentioned, the sane route is GDBus. There creating even much more complex signatures is pretty easy as you can use the GVariantBuilder API:

GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add (&builder, "{sv}", "name1", my_variant);

/* Now use the builder results with g_dbus_connection_call()
   or g_dbus_proxy_call() */
like image 90
Jussi Kukkonen Avatar answered Oct 12 '22 18:10

Jussi Kukkonen


I know this question was asked awhile ago, but I had a very similar question recently, and after several hours of trial and error this is some code I came up with that works for me. Hopefully it helps someone else...

DBusMessage* testMessage()
{
    DBusMessage* mssg = dbus_message_new_signal("/fi/w1/wpa_supplicant1/Interfaces/0", "fi.w1.wpa_supplicant1.Interface", "PropertiesChanged");

    DBusMessageIter iter, aIter;
    dbus_message_iter_init_append(mssg, &iter);
    if (!dbus_message_iter_open_container(&iter, 'a', "{sv}", &aIter))
        return nullptr;

    DBusMessageIter eIter;
    if (!dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) {
        dbus_message_iter_abandon_container_if_open(&iter, &aIter);
        return nullptr;
    }

    const char* key = "test key";
    dbus_message_iter_append_basic(&eIter, 's', static_cast<void*>(&key));

    DBusMessageIter vIter;
    if (!dbus_message_iter_open_container(&eIter, 'v', "i", &vIter)) {
        dbus_message_iter_abandon_container_if_open(&aIter, &eIter);
        dbus_message_iter_abandon_container_if_open(&iter, &aIter);
        return nullptr;
    }

    dbus_int32_t val = 42;
    dbus_message_iter_append_basic(&vIter, 'i', static_cast<void*>(&val));

    dbus_message_iter_close_container(&eIter, &vIter);
    dbus_message_iter_close_container(&aIter, &eIter);
    dbus_message_iter_close_container(&iter, &aIter);

    return mssg;
}

This is C++ but should be pretty easy to adapt for C. The returned message has a signature of a{sv}. The dbus docs are helpful-ish.

like image 32
Dovahkiin Avatar answered Oct 12 '22 19:10

Dovahkiin