D-Bus, UDisks and Glibmm bindings
Tue, 01 July 2014 :: #linux
One day I was having a problem with simple D-Bus concept. I was using Glibmm D-Bus bindings (Gio::DBus namespace) to access the UDisks interface. I wanted to read some attributes of every hard disk found in the system, so first I needed to enumerate all disks that UDisks was reporting, like this:
Glib::RefPtr<Gio::DBus::Connection> bus;
int main() {
using namespace Glib;
using namespace Gio;
Glib::init();
Gio::init();
bus = DBus::Connection::get_sync(Gio::DBus::BUS_TYPE_SYSTEM);
RefPtr<DBus::Proxy> udisks_proxy = DBus::Proxy::create_sync(bus, "org.freedesktop.UDisks", "/org/freedesktop/UDisks", "org.freedesktop.UDisks");
VariantContainerBase devices_variant = udisks_proxy->call_sync("EnumerateDevices");
VariantIter iterator(devices_variant.get_child(0));
Variant<ustring> var;
while(iterator.next_value(var)) {
ustring name = var.get();
LOG("device: '%s", name.c_str());
process_device(name);
}
return 0;
}
This seemed to work OK, because call_sync() returned a VariantContainerBase, which holds the (ao) object, which basically is: one struct of an array of object paths. From the documentation I read that the object path type is handled in the same way as a string type, that's why the untyped VariantBase that is created during get_child(0) allows itself to be casted into Variant<ustring> object. Using this parametrized Variant, it's trivial to extract the string by using var.get().
But then I was trying to read some stuff (in that particular case, the NativePath attribute) from the attributes of each drive using this method:
void process_device(const Glib::ustring& objpath) {
using namespace Glib;
using namespace Gio;
RefPtr<DBus::Proxy> attrs = DBus::Proxy::create_sync(bus, "org.freedesktop.UDisks", objpath, "org.freedesktop.DBus.Properties");
std::vector<VariantBase> args;
args.push_back(Variant<ustring>::create(objpath));
args.push_back(Variant<ustring>::create("NativePath"));
VariantContainerBase data = attrs->call_sync("Get", VariantContainerBase::create_tuple(args));
LOG("return type: %s", data.get_type_string().c_str());
}
The problem was that the VariantContainerBase object contained the (v) signature. This means that the object is variant, so I couldn't cast it to anything.
Introspection of the attributes showed that NativePath was holding a string value. So why the call_sync() method was returning an object of a variant type? How the NativePath attribute could be read, without using get_data() method and without doing memcpy of the data into manually allocated buffer?
The solution would suggest that probably it was a bug in glibmm's bindings.
Instead of calling VariantContainerBase's get_child(0), a proper way is to call a direct glib function: g_variant_get_child(), like this:
GVariant *output;
g_variant_get_child(data.gobj(), 0, "v", & output);
It will properly set the output's variant type to one that's insive v (in my case it will set the type of output to s). After this, when I got the remote data in the s type, I could cast it to Variant<ustring> type like this:
Variant<Glib::ustring> item(output);
Glib::ustring text = item.get();
since, fortunately, Variant's constructor accepts the old, glib-ish GVariant * type.
I'm still not sure whether it was a bug, or a feature.
