2

When trying to list network properties - https://w1.fi/wpa_supplicant/devel/dbus.html#dbus_network using dbus-cpp I get a number of errors about missing operator== for core::dbus::types::Variant

/usr/include/core/dbus/impl/object.h:185:17:   required from ‘std::shared_ptr<core::dbus::Property<PropertyDescription> > core::dbus::Object::get_property() [with PropertyDescription = fi::w1::wpa_supplicant1::Network::Properties::Propertiez]’ /home/martin/ClionProjects/ang-wifi-controller/src/wpasupplicantclient.cpp:149:118:   required from here /usr/include/c++/6/bits/stl_pair.h:364:51: error: no match for ‘operator==’ (operand types are ‘const core::dbus::types::Variant’ and ‘const core::dbus::types::Variant’)
 { return __x.first == __y.first && __x.second == __y.second; }

My code is based on dbus-cpp examples and http://quaintous.com/2015/08/30/cpp11-dbus/, but they only offer limited assistance. The code representing Properties property is as follows:

namespace fi {
namespace w1 {
struct wpa_supplicant1 {
struct Network {
    struct Properties {
        struct Propertiez {
            inline static std::string name() { return "Properties"; };
            typedef Network Interface;
            typedef std::map<std::string, core::dbus::types::Variant> ValueType;
            static const bool readable = true;
            static const bool writable = true;
        };
    };
};

And the offending line in .cpp is networkProxy->get_property<fi::w1::wpa_supplicant1::Network::Properties::Propertiez>();

I found that this question has already been asked at https://answers.launchpad.net/ubuntu/+source/dbus-cpp/+question/593271, but nobody offered any advice. Going through the code of packages listed by apt-cache rdepends libdbus-cpp5 also yielded no results. I tried messing with the ValueType but it all resulted in runtime errors as the expected result probably truly is the map. It honestly seems like a bug in the library to me, but since this must be an obvious use case I am trying to find the mistake in my usage of the library. So what am I doing wrong?

Edit: Since I did not get any response, I am including minimal sample.

#include <core/dbus/bus.h>
#include <core/dbus/object.h>
#include <core/dbus/property.h>
#include <core/dbus/service.h>
#include <core/dbus/result.h>
#include <core/dbus/asio/executor.h>
#include <core/dbus/interfaces/properties.h>
#include <core/dbus/types/stl/string.h>
#include <core/dbus/types/stl/tuple.h>
#include <core/dbus/types/stl/vector.h>
#include <core/dbus/types/struct.h>
#include <core/dbus/types/variant.h>

using namespace std::chrono_literals;
using DBusDict = std::map<std::string, core::dbus::types::Variant>;

namespace fi {
namespace w1 {
struct wpa_supplicant1
{
    struct GetInterface {
        typedef wpa_supplicant1 Interface;
        static const std::string &name()
        {
            static const std::string s("GetInterface");
            return s;
        }
        inline static const std::chrono::milliseconds default_timeout() { return 1s; }
    };
    struct Iface
    {
        struct AddNetwork {
            typedef Iface Interface;
            static const std::string &name()
            {
                static const std::string s("AddNetwork");
                return s;
            }
            inline static const std::chrono::milliseconds default_timeout() { return 1s; }
        };
        struct Properties
        {
            struct Networks
            {
                inline static std::string name()
                { return "Networks"; };
                typedef Iface Interface;
                typedef std::vector<core::dbus::types::ObjectPath> ValueType;
                static const bool readable = true;
                static const bool writable = false;
            };
        };
    };
    struct Network
    {
        struct Properties
        {
            struct Propertiez
            {
                inline static std::string name()
                { return "Properties"; };
                typedef Network Interface;
                typedef DBusDict ValueType;
                static const bool readable = true;
                static const bool writable = true;
            };
        };
    };
};
};
};

namespace core {
namespace dbus {
namespace traits {
template <> struct Service<fi::w1::wpa_supplicant1> {
    inline static const std::string &interface_name()
    {
        static const std::string s("fi.w1.wpa_supplicant1");
        return s;
    }
};

template <> struct Service<fi::w1::wpa_supplicant1::Iface> {
    inline static const std::string &interface_name()
    {
        static const std::string s("fi.w1.wpa_supplicant1.Interface");
        return s;
    }
};
template <> struct Service<fi::w1::wpa_supplicant1::Network> {
    inline static const std::string &interface_name()
    {
        static const std::string s("fi.w1.wpa_supplicant1.Network");
        return s;
    }
};
}
}
}

int main()
{
    //bus
    auto systemBus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::system);
    systemBus->install_executor(core::dbus::asio::make_executor(systemBus));
    auto busThread = std::thread(std::bind(&core::dbus::Bus::run, systemBus));

    //service
    auto wpaService = core::dbus::Service::use_service<fi::w1::wpa_supplicant1>(systemBus);
    auto wpaObjectPath = core::dbus::types::ObjectPath("/fi/w1/wpa_supplicant1");
    auto wpaRootProxy = wpaService->object_for_path(wpaObjectPath);

    //iface
    auto ifacePath = wpaRootProxy->transact_method<fi::w1::wpa_supplicant1::GetInterface,
                                             core::dbus::types::ObjectPath,
                                             std::string>("wlan0"); //change this to your own wireless interface
    auto wpaIfaceProxy = wpaService->object_for_path(ifacePath.value());
    auto networkPaths = wpaIfaceProxy->get_property<fi::w1::wpa_supplicant1::Iface::Properties::Networks>();

    //network
    std::string ssid("network");
    std::string password("password");
    DBusDict args = {
        {"ssid", core::dbus::types::Variant::encode(ssid)},
        {"psk", core::dbus::types::Variant::encode(password)},
    };

    auto networkPath = wpaIfaceProxy->transact_method<fi::w1::wpa_supplicant1::Iface::AddNetwork,
                                                        core::dbus::types::ObjectPath,
                                                        DBusDict>(args);
    auto networkProxy = wpaService->object_for_path(networkPath.value());

    //get properties - uncomment line below to get compiler errors
    //auto netProps = networkProxy->get_property<fi::w1::wpa_supplicant1::Network::Properties::Propertiez>();

    while (true) {
        continue;
    }
}

Compile using: g++ $(pkg-config --cflags dbus-1 dbus-cpp) ./main.cpp $(pkg-config --libs dbus-1 dbus-cpp) -lpthread

mmatous
  • 482
  • 6
  • 16
  • +1 for the tip to use apt-cache rdepends libdbus-cpp5. I've been thinking of using this d-bus binding, but it seems to have very little support. I wanted to use the Introspectable interface, but that guy doesn't even compile out-of-the-box. – Rich von Lehe Apr 11 '18 at 21:25
  • FWIW, I'm on Ubuntu 14.04 at work and tried: apt-cache rdepends libdbus-cpp2 and it came up with several services. I'm looking into some of that source now (e.g. libubuntu-location-service-dev). – Rich von Lehe Apr 11 '18 at 21:47

2 Answers2

2

Update:

dbus-cpp has an implementation for org.freedesktop.DBus.Properties.Get method.

Get:

auto resultVariant = dbus_object->invoke_method_synchronously
/*Method*/          <dbus::interfaces::Properties::Get, 
/*Output Type*/      dbus::types::Variant, 
/*Input Types*/      std::string, std::string>
                    ("fi.w1.wpa_supplicant1.Network","Properties").value();
auto props = resultVariant.as<std::map<std::string, dbus::types::Variant>>();

Sadly, the Set method, while also implemented, does NOT seem to work with any ArgumentTypes with nested Variants within them. So:

  • a{sv} : std::map<std::string, core::dbus::types::Variant>
  • a{v} : std::vector<core::dbus::types::Variant>

in a Set method call will actually cause the program to crash. (Didn't test more)



Old post:

I ran into the same bug today and found a workaround for at least getting the properties value.

Instead of using

auto prop = dbus_object->get_property<fi::w1::wpa_supplicant1::Network::Properties::Propertiez>();

try

//returns std::map<std::string, core::dbus::types::Variant>>
auto props = dbus_object->get_all_properties<fi::w1::wpa_supplicant1::Network>();

auto prop = props["Properties"];
auto prop_value = prop.as<std::map<std::string, core::dbus::types::Variant>>();

As far as I understand the bug, dbus-cpp makes use of the org.freedesktop.DBus.Properties interface to read out Properties.

So dbus_object->get_property() tries to invoke a org.freedesktop.DBus.Properties.Get and fails to compile because of the missing ==operator implementation. (Something it needs for casting the specific ValueType, I guess)

dbus_object->get_all_properties() invokes org.freedesktop.DBus.Properties.GetAll which does not need a specific ValueType, so it works.


Of course this is just a workaround to getting properties, since setting a property value is bound to the same shared_pointer as getting it.

Fleet Gone
  • 36
  • 5
0

As the documentation for fi.w1.wpa_supplicant1.Network.Properties.Properties says:

[...] All values are string type, e.g., frequency is "2437", not 2437.

So try to define DBusDict as follow:

using DBusDict = std::map<std::string, std::string>;
Sanyi
  • 1
  • This is obviously wrong. The documentation clearly states that the property signature is "Properties - a{sv} - (read/write)". The inside of the variant is irrelevant. I mean sure, it compiles, but then it throws `org.freedesktop.DBus.Error.UnknownMethod: Method "Get" with signature "ss" on interface "org.freedesktop.DBus.Properties" doesn't exist` at runtime. – mmatous Jul 17 '17 at 19:08