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.