1*377e76abSEd Tanous #include <dbus/connection.hpp> 2*377e76abSEd Tanous #include <dbus/filter.hpp> 3*377e76abSEd Tanous #include <dbus/match.hpp> 4*377e76abSEd Tanous #include <functional> 5*377e76abSEd Tanous #include <tuple> 6*377e76abSEd Tanous #include <type_traits> 7*377e76abSEd Tanous #include <boost/algorithm/string/predicate.hpp> 8*377e76abSEd Tanous #include <boost/container/flat_map.hpp> 9*377e76abSEd Tanous #include <boost/container/flat_set.hpp> 10*377e76abSEd Tanous 11*377e76abSEd Tanous namespace dbus { 12*377e76abSEd Tanous struct DbusArgument { 13*377e76abSEd Tanous DbusArgument(const std::string& direction, const std::string& name, 14*377e76abSEd Tanous const std::string& type) 15*377e76abSEd Tanous : direction(direction), name(name), type(type){}; 16*377e76abSEd Tanous std::string direction; 17*377e76abSEd Tanous std::string name; 18*377e76abSEd Tanous std::string type; 19*377e76abSEd Tanous }; 20*377e76abSEd Tanous 21*377e76abSEd Tanous class DbusMethod { 22*377e76abSEd Tanous public: 23*377e76abSEd Tanous DbusMethod(std::string name, std::shared_ptr<dbus::connection>& conn) 24*377e76abSEd Tanous : name(name), conn(conn){}; 25*377e76abSEd Tanous virtual void call(dbus::message& m){}; 26*377e76abSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; }; 27*377e76abSEd Tanous std::string name; 28*377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 29*377e76abSEd Tanous }; 30*377e76abSEd Tanous 31*377e76abSEd Tanous enum class UpdateType { VALUE_CHANGE_ONLY, FORCE }; 32*377e76abSEd Tanous 33*377e76abSEd Tanous // primary template. 34*377e76abSEd Tanous template <class T> 35*377e76abSEd Tanous struct function_traits : function_traits<decltype(&T::operator())> {}; 36*377e76abSEd Tanous 37*377e76abSEd Tanous // partial specialization for function type 38*377e76abSEd Tanous template <class R, class... Args> 39*377e76abSEd Tanous struct function_traits<R(Args...)> { 40*377e76abSEd Tanous using result_type = R; 41*377e76abSEd Tanous using argument_types = std::tuple<Args...>; 42*377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 43*377e76abSEd Tanous }; 44*377e76abSEd Tanous 45*377e76abSEd Tanous // partial specialization for function pointer 46*377e76abSEd Tanous template <class R, class... Args> 47*377e76abSEd Tanous struct function_traits<R (*)(Args...)> { 48*377e76abSEd Tanous using result_type = R; 49*377e76abSEd Tanous using argument_types = std::tuple<Args...>; 50*377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 51*377e76abSEd Tanous }; 52*377e76abSEd Tanous 53*377e76abSEd Tanous // partial specialization for std::function 54*377e76abSEd Tanous template <class R, class... Args> 55*377e76abSEd Tanous struct function_traits<std::function<R(Args...)>> { 56*377e76abSEd Tanous using result_type = R; 57*377e76abSEd Tanous using argument_types = std::tuple<Args...>; 58*377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 59*377e76abSEd Tanous }; 60*377e76abSEd Tanous 61*377e76abSEd Tanous // partial specialization for pointer-to-member-function (i.e., operator()'s) 62*377e76abSEd Tanous template <class T, class R, class... Args> 63*377e76abSEd Tanous struct function_traits<R (T::*)(Args...)> { 64*377e76abSEd Tanous using result_type = R; 65*377e76abSEd Tanous using argument_types = std::tuple<Args...>; 66*377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 67*377e76abSEd Tanous }; 68*377e76abSEd Tanous 69*377e76abSEd Tanous template <class T, class R, class... Args> 70*377e76abSEd Tanous struct function_traits<R (T::*)(Args...) const> { 71*377e76abSEd Tanous using result_type = R; 72*377e76abSEd Tanous using argument_types = std::tuple<Args...>; 73*377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 74*377e76abSEd Tanous }; 75*377e76abSEd Tanous 76*377e76abSEd Tanous template <class F, size_t... Is> 77*377e76abSEd Tanous constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) { 78*377e76abSEd Tanous return f(std::integral_constant<size_t, Is>{}...); 79*377e76abSEd Tanous } 80*377e76abSEd Tanous 81*377e76abSEd Tanous template <size_t N, class F> 82*377e76abSEd Tanous constexpr auto index_apply(F f) { 83*377e76abSEd Tanous return index_apply_impl(f, std::make_index_sequence<N>{}); 84*377e76abSEd Tanous } 85*377e76abSEd Tanous 86*377e76abSEd Tanous template <class Tuple, class F> 87*377e76abSEd Tanous constexpr auto apply(Tuple& t, F f) { 88*377e76abSEd Tanous return index_apply<std::tuple_size<Tuple>{}>( 89*377e76abSEd Tanous [&](auto... Is) { return f(std::get<Is>(t)...); }); 90*377e76abSEd Tanous } 91*377e76abSEd Tanous 92*377e76abSEd Tanous template <class Tuple> 93*377e76abSEd Tanous constexpr void unpack_into_tuple(Tuple& t, dbus::message& m) { 94*377e76abSEd Tanous return index_apply<std::tuple_size<Tuple>{}>( 95*377e76abSEd Tanous [&](auto... Is) { m.unpack(std::get<Is>(t)...); }); 96*377e76abSEd Tanous } 97*377e76abSEd Tanous 98*377e76abSEd Tanous // Specialization for empty tuples. No need to unpack if no arguments 99*377e76abSEd Tanous constexpr void unpack_into_tuple(std::tuple<>& t, dbus::message& m) {} 100*377e76abSEd Tanous 101*377e76abSEd Tanous template <class Tuple> 102*377e76abSEd Tanous constexpr void pack_tuple_into_msg(Tuple& t, dbus::message& m) { 103*377e76abSEd Tanous return index_apply<std::tuple_size<Tuple>{}>( 104*377e76abSEd Tanous [&](auto... Is) { m.pack(std::get<Is>(t)...); }); 105*377e76abSEd Tanous } 106*377e76abSEd Tanous 107*377e76abSEd Tanous // Specialization for empty tuples. No need to pack if no arguments 108*377e76abSEd Tanous constexpr void pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {} 109*377e76abSEd Tanous 110*377e76abSEd Tanous // Base case for when I == the size of the tuple args. Does nothing, as we 111*377e76abSEd Tanous // should be done 112*377e76abSEd Tanous template <std::size_t I = 0, typename... Tp> 113*377e76abSEd Tanous inline typename std::enable_if<I == sizeof...(Tp), void>::type arg_types( 114*377e76abSEd Tanous bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v) {} 115*377e76abSEd Tanous 116*377e76abSEd Tanous // Case for when I < the size of tuple args. Unpacks the tuple type into the 117*377e76abSEd Tanous // dbusargument object and names it appropriately. 118*377e76abSEd Tanous template <std::size_t I = 0, typename... Tp> 119*377e76abSEd Tanous inline typename std::enable_if < 120*377e76abSEd Tanous I<sizeof...(Tp), void>::type arg_types(bool in, std::tuple<Tp...>& t, 121*377e76abSEd Tanous std::vector<DbusArgument>& v) { 122*377e76abSEd Tanous typedef typename std::tuple_element<I, std::tuple<Tp...>>::type element_type; 123*377e76abSEd Tanous auto constexpr sig = element_signature<element_type>::code; 124*377e76abSEd Tanous 125*377e76abSEd Tanous if (in) { 126*377e76abSEd Tanous auto name = "arg_" + std::to_string(I); 127*377e76abSEd Tanous v.emplace_back("in", name, &sig[0]); 128*377e76abSEd Tanous } else { 129*377e76abSEd Tanous auto name = "out_" + std::to_string(I); 130*377e76abSEd Tanous v.emplace_back("out", name, &sig[0]); 131*377e76abSEd Tanous } 132*377e76abSEd Tanous arg_types<I + 1, Tp...>(in, t, v); 133*377e76abSEd Tanous } 134*377e76abSEd Tanous 135*377e76abSEd Tanous template <typename Handler> 136*377e76abSEd Tanous class LambdaDbusMethod : public DbusMethod { 137*377e76abSEd Tanous public: 138*377e76abSEd Tanous typedef function_traits<Handler> traits; 139*377e76abSEd Tanous typedef typename traits::decayed_arg_types InputTupleType; 140*377e76abSEd Tanous typedef typename traits::result_type ResultTupleType; 141*377e76abSEd Tanous LambdaDbusMethod(std::string name, std::shared_ptr<dbus::connection>& conn, 142*377e76abSEd Tanous Handler h) 143*377e76abSEd Tanous : DbusMethod(name, conn), h(std::move(h)) {} 144*377e76abSEd Tanous void call(dbus::message& m) override { 145*377e76abSEd Tanous InputTupleType input_args; 146*377e76abSEd Tanous unpack_into_tuple(input_args, m); 147*377e76abSEd Tanous ResultTupleType r = apply(input_args, h); 148*377e76abSEd Tanous auto ret = dbus::message::new_return(m); 149*377e76abSEd Tanous pack_tuple_into_msg(r, ret); 150*377e76abSEd Tanous conn->send(ret, std::chrono::seconds(0)); 151*377e76abSEd Tanous }; 152*377e76abSEd Tanous 153*377e76abSEd Tanous std::vector<DbusArgument> get_args() override { 154*377e76abSEd Tanous std::vector<DbusArgument> ret; 155*377e76abSEd Tanous std::string s; 156*377e76abSEd Tanous InputTupleType t; 157*377e76abSEd Tanous arg_types(true, t, ret); 158*377e76abSEd Tanous 159*377e76abSEd Tanous ResultTupleType o; 160*377e76abSEd Tanous arg_types(false, o, ret); 161*377e76abSEd Tanous return ret; 162*377e76abSEd Tanous }; 163*377e76abSEd Tanous Handler h; 164*377e76abSEd Tanous }; 165*377e76abSEd Tanous 166*377e76abSEd Tanous class DbusSignal { 167*377e76abSEd Tanous public: 168*377e76abSEd Tanous DbusSignal(std::string name) : name(name){}; 169*377e76abSEd Tanous std::string name; 170*377e76abSEd Tanous 171*377e76abSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; }; 172*377e76abSEd Tanous }; 173*377e76abSEd Tanous 174*377e76abSEd Tanous class DbusInterface { 175*377e76abSEd Tanous public: 176*377e76abSEd Tanous DbusInterface(std::string interface_name, 177*377e76abSEd Tanous std::shared_ptr<dbus::connection>& conn) 178*377e76abSEd Tanous : interface_name(std::move(interface_name)), conn(conn) {} 179*377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>> 180*377e76abSEd Tanous get_signals() { 181*377e76abSEd Tanous return dbus_signals; 182*377e76abSEd Tanous }; 183*377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>> 184*377e76abSEd Tanous get_methods() { 185*377e76abSEd Tanous return dbus_methods; 186*377e76abSEd Tanous }; 187*377e76abSEd Tanous virtual std::string get_interface_name() { return interface_name; }; 188*377e76abSEd Tanous virtual const boost::container::flat_map<std::string, dbus_variant> 189*377e76abSEd Tanous get_properties_map() { 190*377e76abSEd Tanous return properties_map; 191*377e76abSEd Tanous }; 192*377e76abSEd Tanous 193*377e76abSEd Tanous template <typename VALUE_TYPE> 194*377e76abSEd Tanous void set_property(const std::string& property_name, const VALUE_TYPE value, 195*377e76abSEd Tanous UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) { 196*377e76abSEd Tanous // Construct a change vector of length 1. if this set_properties is ever 197*377e76abSEd Tanous // templated for any type, we could probably swap with with a 198*377e76abSEd Tanous // std::array<pair, 1> 199*377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 200*377e76abSEd Tanous v.emplace_back(property_name, value); 201*377e76abSEd Tanous set_properties(v, update_mode); 202*377e76abSEd Tanous } 203*377e76abSEd Tanous 204*377e76abSEd Tanous void set_properties( 205*377e76abSEd Tanous const std::vector<std::pair<std::string, dbus_variant>>& v, 206*377e76abSEd Tanous const UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) { 207*377e76abSEd Tanous // TODO(ed) generalize this interface for all "map like" types, basically 208*377e76abSEd Tanous // anything that will return a const iterator of std::pair<string, 209*377e76abSEd Tanous // variant> 210*377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> updates; 211*377e76abSEd Tanous updates.reserve(v.size()); 212*377e76abSEd Tanous 213*377e76abSEd Tanous if (update_mode == UpdateType::FORCE) { 214*377e76abSEd Tanous updates = v; 215*377e76abSEd Tanous } else { 216*377e76abSEd Tanous for (auto& property : v) { 217*377e76abSEd Tanous auto property_map_it = properties_map.find(property.first); 218*377e76abSEd Tanous if (property_map_it != properties_map.end()) { 219*377e76abSEd Tanous // Property exists in map 220*377e76abSEd Tanous if (property_map_it->second != property.second) { 221*377e76abSEd Tanous properties_map[property.first] = property.second; 222*377e76abSEd Tanous // if value has changed since last set 223*377e76abSEd Tanous updates.emplace_back(*property_map_it); 224*377e76abSEd Tanous } 225*377e76abSEd Tanous } else { 226*377e76abSEd Tanous // property doesn't exist, must be new 227*377e76abSEd Tanous properties_map[property.first] = property.second; 228*377e76abSEd Tanous updates.emplace_back(property.first, property.second); 229*377e76abSEd Tanous } 230*377e76abSEd Tanous } 231*377e76abSEd Tanous } 232*377e76abSEd Tanous 233*377e76abSEd Tanous const static dbus::endpoint endpoint("org.freedesktop.DBus", object_name, 234*377e76abSEd Tanous "org.freedesktop.DBus.Properties"); 235*377e76abSEd Tanous 236*377e76abSEd Tanous auto m = dbus::message::new_signal(endpoint, "PropertiesChanged"); 237*377e76abSEd Tanous 238*377e76abSEd Tanous static const std::vector<std::string> empty; 239*377e76abSEd Tanous m.pack(get_interface_name(), updates, empty); 240*377e76abSEd Tanous // TODO(ed) make sure this doesn't block 241*377e76abSEd Tanous conn->async_send( 242*377e76abSEd Tanous m, [](const boost::system::error_code ec, dbus::message r) {}); 243*377e76abSEd Tanous } 244*377e76abSEd Tanous 245*377e76abSEd Tanous void register_method(std::shared_ptr<DbusMethod>&& method) { 246*377e76abSEd Tanous dbus_methods.emplace(method->name, method); 247*377e76abSEd Tanous } 248*377e76abSEd Tanous 249*377e76abSEd Tanous template <typename Handler> 250*377e76abSEd Tanous void register_method(const std::string& name, Handler&& method) { 251*377e76abSEd Tanous dbus_methods.emplace(name, 252*377e76abSEd Tanous new LambdaDbusMethod<Handler>(name, conn, method)); 253*377e76abSEd Tanous } 254*377e76abSEd Tanous 255*377e76abSEd Tanous void call(dbus::message& m) { 256*377e76abSEd Tanous std::string method_name = m.get_member(); 257*377e76abSEd Tanous auto method = dbus_methods.find(method_name); 258*377e76abSEd Tanous if (method != dbus_methods.end()) { 259*377e76abSEd Tanous method->second->call(m); 260*377e76abSEd Tanous } // TODO(ed) send something when method doesn't exist? 261*377e76abSEd Tanous } 262*377e76abSEd Tanous 263*377e76abSEd Tanous std::string object_name; 264*377e76abSEd Tanous std::string interface_name; 265*377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>> 266*377e76abSEd Tanous dbus_methods; 267*377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>> 268*377e76abSEd Tanous dbus_signals; 269*377e76abSEd Tanous boost::container::flat_map<std::string, dbus_variant> properties_map; 270*377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 271*377e76abSEd Tanous }; 272*377e76abSEd Tanous 273*377e76abSEd Tanous class DbusObject { 274*377e76abSEd Tanous public: 275*377e76abSEd Tanous DbusObject(std::shared_ptr<dbus::connection> conn, std::string object_name) 276*377e76abSEd Tanous : object_name(std::move(object_name)), conn(conn) { 277*377e76abSEd Tanous properties_iface = add_interface("org.freedesktop.DBus.Properties"); 278*377e76abSEd Tanous 279*377e76abSEd Tanous properties_iface->register_method( 280*377e76abSEd Tanous "Get", [&](const std::string& interface_name, 281*377e76abSEd Tanous const std::string& property_name) { 282*377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 283*377e76abSEd Tanous if (interface_it == interfaces.end()) { 284*377e76abSEd Tanous // Interface not found error 285*377e76abSEd Tanous throw std::runtime_error("interface not found"); 286*377e76abSEd Tanous } else { 287*377e76abSEd Tanous auto& properties_map = interface_it->second->get_properties_map(); 288*377e76abSEd Tanous auto property = properties_map.find(property_name); 289*377e76abSEd Tanous if (property == properties_map.end()) { 290*377e76abSEd Tanous // TODO(ed) property not found error 291*377e76abSEd Tanous throw std::runtime_error("property not found"); 292*377e76abSEd Tanous } else { 293*377e76abSEd Tanous return std::tuple<dbus_variant>(property->second); 294*377e76abSEd Tanous } 295*377e76abSEd Tanous } 296*377e76abSEd Tanous }); 297*377e76abSEd Tanous 298*377e76abSEd Tanous properties_iface->register_method("GetAll", [&](const std::string& 299*377e76abSEd Tanous interface_name) { 300*377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 301*377e76abSEd Tanous if (interface_it == interfaces.end()) { 302*377e76abSEd Tanous // Interface not found error 303*377e76abSEd Tanous throw std::runtime_error("interface not found"); 304*377e76abSEd Tanous } else { 305*377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 306*377e76abSEd Tanous for (auto& element : properties_iface->get_properties_map()) { 307*377e76abSEd Tanous v.emplace_back(element.first, element.second); 308*377e76abSEd Tanous } 309*377e76abSEd Tanous return std::tuple<std::vector<std::pair<std::string, dbus_variant>>>(v); 310*377e76abSEd Tanous } 311*377e76abSEd Tanous }); 312*377e76abSEd Tanous // TODO(Ed) support returning nothing 313*377e76abSEd Tanous properties_iface->register_method( 314*377e76abSEd Tanous "Set", 315*377e76abSEd Tanous [&](const std::string& interface_name, const std::string& property_name, 316*377e76abSEd Tanous const dbus_variant& value) { 317*377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 318*377e76abSEd Tanous if (interface_it == interfaces.end()) { 319*377e76abSEd Tanous // Interface not found error 320*377e76abSEd Tanous throw std::runtime_error("interface not found"); 321*377e76abSEd Tanous } else { 322*377e76abSEd Tanous // Todo, the set propery (signular) interface should support 323*377e76abSEd Tanous // handing 324*377e76abSEd Tanous // a variant. THe below is expensive 325*377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 326*377e76abSEd Tanous v.emplace_back(property_name, value); 327*377e76abSEd Tanous interface_it->second->set_properties(v); 328*377e76abSEd Tanous return std::tuple<>(); 329*377e76abSEd Tanous } 330*377e76abSEd Tanous }); 331*377e76abSEd Tanous } 332*377e76abSEd Tanous 333*377e76abSEd Tanous std::shared_ptr<DbusInterface> add_interface(const std::string& name) { 334*377e76abSEd Tanous auto x = std::make_shared<DbusInterface>(name, conn); 335*377e76abSEd Tanous register_interface(x); 336*377e76abSEd Tanous return x; 337*377e76abSEd Tanous } 338*377e76abSEd Tanous 339*377e76abSEd Tanous void register_interface(std::shared_ptr<DbusInterface>& interface) { 340*377e76abSEd Tanous interfaces[interface->get_interface_name()] = interface; 341*377e76abSEd Tanous interface->object_name = object_name; 342*377e76abSEd Tanous const static dbus::endpoint endpoint("", object_name, 343*377e76abSEd Tanous "org.freedesktop.DBus.ObjectManager"); 344*377e76abSEd Tanous 345*377e76abSEd Tanous const static std::string signal_name("InterfacesAdded"); 346*377e76abSEd Tanous auto m = message::new_signal(endpoint, signal_name); 347*377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus_variant>> properties_dict; 348*377e76abSEd Tanous std::vector<std::pair<std::string, properties_dict>> sig; 349*377e76abSEd Tanous sig.emplace_back(interface->get_interface_name(), properties_dict()); 350*377e76abSEd Tanous auto& prop_dict = sig.back().second; 351*377e76abSEd Tanous for (auto& property : interface->get_properties_map()) { 352*377e76abSEd Tanous prop_dict.emplace_back(property); 353*377e76abSEd Tanous } 354*377e76abSEd Tanous 355*377e76abSEd Tanous m.pack(object_name, sig); 356*377e76abSEd Tanous conn->send(m); 357*377e76abSEd Tanous } 358*377e76abSEd Tanous 359*377e76abSEd Tanous auto get_interfaces() { return interfaces; } 360*377e76abSEd Tanous 361*377e76abSEd Tanous void call(dbus::message& m) { 362*377e76abSEd Tanous auto interface = interfaces.find(m.get_interface()); 363*377e76abSEd Tanous if (interface != interfaces.end()) { 364*377e76abSEd Tanous interface->second->call(m); 365*377e76abSEd Tanous } // TODO(ed) send something when interface doesn't exist? 366*377e76abSEd Tanous } 367*377e76abSEd Tanous 368*377e76abSEd Tanous std::string object_name; 369*377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 370*377e76abSEd Tanous 371*377e76abSEd Tanous // dbus::filter properties_filter; 372*377e76abSEd Tanous std::shared_ptr<DbusInterface> properties_iface; 373*377e76abSEd Tanous 374*377e76abSEd Tanous std::function<void(boost::system::error_code, message)> callback; 375*377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusInterface>> 376*377e76abSEd Tanous interfaces; 377*377e76abSEd Tanous }; 378*377e76abSEd Tanous 379*377e76abSEd Tanous class DbusObjectServer { 380*377e76abSEd Tanous public: 381*377e76abSEd Tanous DbusObjectServer(std::shared_ptr<dbus::connection>& conn) : conn(conn) { 382*377e76abSEd Tanous introspect_filter = 383*377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) { 384*377e76abSEd Tanous if (m.get_type() != "method_call") { 385*377e76abSEd Tanous return false; 386*377e76abSEd Tanous } 387*377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.Introspectable") { 388*377e76abSEd Tanous return false; 389*377e76abSEd Tanous } 390*377e76abSEd Tanous if (m.get_member() != "Introspect") { 391*377e76abSEd Tanous return false; 392*377e76abSEd Tanous }; 393*377e76abSEd Tanous return true; 394*377e76abSEd Tanous }); 395*377e76abSEd Tanous 396*377e76abSEd Tanous introspect_filter->async_dispatch( 397*377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 398*377e76abSEd Tanous on_introspect(ec, m); 399*377e76abSEd Tanous }); 400*377e76abSEd Tanous 401*377e76abSEd Tanous object_manager_filter = 402*377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) { 403*377e76abSEd Tanous 404*377e76abSEd Tanous if (m.get_type() != "method_call") { 405*377e76abSEd Tanous return false; 406*377e76abSEd Tanous } 407*377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.ObjectManager") { 408*377e76abSEd Tanous return false; 409*377e76abSEd Tanous } 410*377e76abSEd Tanous if (m.get_member() != "GetManagedObjects") { 411*377e76abSEd Tanous return false; 412*377e76abSEd Tanous }; 413*377e76abSEd Tanous return true; 414*377e76abSEd Tanous }); 415*377e76abSEd Tanous 416*377e76abSEd Tanous object_manager_filter->async_dispatch( 417*377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 418*377e76abSEd Tanous on_get_managed_objects(ec, m); 419*377e76abSEd Tanous }); 420*377e76abSEd Tanous 421*377e76abSEd Tanous method_filter = std::make_unique<dbus::filter>(conn, [](dbus::message m) { 422*377e76abSEd Tanous 423*377e76abSEd Tanous if (m.get_type() != "method_call") { 424*377e76abSEd Tanous return false; 425*377e76abSEd Tanous } 426*377e76abSEd Tanous return true; 427*377e76abSEd Tanous }); 428*377e76abSEd Tanous 429*377e76abSEd Tanous method_filter->async_dispatch( 430*377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 431*377e76abSEd Tanous on_method_call(ec, m); 432*377e76abSEd Tanous }); 433*377e76abSEd Tanous }; 434*377e76abSEd Tanous 435*377e76abSEd Tanous std::shared_ptr<dbus::connection>& get_connection() { return conn; } 436*377e76abSEd Tanous void on_introspect(const boost::system::error_code ec, dbus::message m) { 437*377e76abSEd Tanous auto xml = get_xml_for_path(m.get_path()); 438*377e76abSEd Tanous auto ret = dbus::message::new_return(m); 439*377e76abSEd Tanous ret.pack(xml); 440*377e76abSEd Tanous conn->async_send( 441*377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {}); 442*377e76abSEd Tanous 443*377e76abSEd Tanous introspect_filter->async_dispatch( 444*377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 445*377e76abSEd Tanous on_introspect(ec, m); 446*377e76abSEd Tanous }); 447*377e76abSEd Tanous } 448*377e76abSEd Tanous 449*377e76abSEd Tanous void on_method_call(const boost::system::error_code ec, dbus::message m) { 450*377e76abSEd Tanous std::cout << "on method call\n"; 451*377e76abSEd Tanous if (ec) { 452*377e76abSEd Tanous std::cerr << "on_method_call error: " << ec << "\n"; 453*377e76abSEd Tanous } else { 454*377e76abSEd Tanous auto path = m.get_path(); 455*377e76abSEd Tanous // TODO(ed) objects should be a map 456*377e76abSEd Tanous for (auto& object : objects) { 457*377e76abSEd Tanous if (object->object_name == path) { 458*377e76abSEd Tanous object->call(m); 459*377e76abSEd Tanous break; 460*377e76abSEd Tanous } 461*377e76abSEd Tanous } 462*377e76abSEd Tanous } 463*377e76abSEd Tanous method_filter->async_dispatch( 464*377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 465*377e76abSEd Tanous on_method_call(ec, m); 466*377e76abSEd Tanous }); 467*377e76abSEd Tanous } 468*377e76abSEd Tanous 469*377e76abSEd Tanous void on_get_managed_objects(const boost::system::error_code ec, 470*377e76abSEd Tanous dbus::message m) { 471*377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus::dbus_variant>> 472*377e76abSEd Tanous properties_dict; 473*377e76abSEd Tanous 474*377e76abSEd Tanous typedef std::vector<std::pair<std::string, properties_dict>> 475*377e76abSEd Tanous interfaces_dict; 476*377e76abSEd Tanous 477*377e76abSEd Tanous std::vector<std::pair<std::string, interfaces_dict>> dict; 478*377e76abSEd Tanous 479*377e76abSEd Tanous for (auto& object : objects) { 480*377e76abSEd Tanous interfaces_dict i; 481*377e76abSEd Tanous for (auto& interface : object->get_interfaces()) { 482*377e76abSEd Tanous properties_dict p; 483*377e76abSEd Tanous 484*377e76abSEd Tanous for (auto& property : interface.second->get_properties_map()) { 485*377e76abSEd Tanous p.push_back(property); 486*377e76abSEd Tanous } 487*377e76abSEd Tanous 488*377e76abSEd Tanous i.emplace_back(interface.second->get_interface_name(), std::move(p)); 489*377e76abSEd Tanous } 490*377e76abSEd Tanous dict.emplace_back(object->object_name, std::move(i)); 491*377e76abSEd Tanous } 492*377e76abSEd Tanous auto ret = dbus::message::new_return(m); 493*377e76abSEd Tanous ret.pack(dict); 494*377e76abSEd Tanous conn->async_send( 495*377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {}); 496*377e76abSEd Tanous 497*377e76abSEd Tanous object_manager_filter->async_dispatch( 498*377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 499*377e76abSEd Tanous on_get_managed_objects(ec, m); 500*377e76abSEd Tanous }); 501*377e76abSEd Tanous } 502*377e76abSEd Tanous 503*377e76abSEd Tanous std::shared_ptr<DbusObject> add_object(const std::string& name) { 504*377e76abSEd Tanous auto x = std::make_shared<DbusObject>(conn, name); 505*377e76abSEd Tanous register_object(x); 506*377e76abSEd Tanous return x; 507*377e76abSEd Tanous } 508*377e76abSEd Tanous 509*377e76abSEd Tanous void register_object(std::shared_ptr<DbusObject> object) { 510*377e76abSEd Tanous objects.emplace_back(object); 511*377e76abSEd Tanous } 512*377e76abSEd Tanous 513*377e76abSEd Tanous std::string get_xml_for_path(const std::string& path) { 514*377e76abSEd Tanous std::string newpath(path); 515*377e76abSEd Tanous 516*377e76abSEd Tanous if (newpath == "/") { 517*377e76abSEd Tanous newpath.assign(""); 518*377e76abSEd Tanous } 519*377e76abSEd Tanous 520*377e76abSEd Tanous boost::container::flat_set<std::string> node_names; 521*377e76abSEd Tanous std::string xml( 522*377e76abSEd Tanous "<!DOCTYPE node PUBLIC " 523*377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " 524*377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/" 525*377e76abSEd Tanous "introspect.dtd\">\n<node>"); 526*377e76abSEd Tanous for (auto& object : objects) { 527*377e76abSEd Tanous std::string& object_name = object->object_name; 528*377e76abSEd Tanous // exact match 529*377e76abSEd Tanous if (object->object_name == newpath) { 530*377e76abSEd Tanous xml += 531*377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.Peer\">" 532*377e76abSEd Tanous " <method name=\"Ping\"/>" 533*377e76abSEd Tanous " <method name=\"GetMachineId\">" 534*377e76abSEd Tanous " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>" 535*377e76abSEd Tanous " </method>" 536*377e76abSEd Tanous " </interface>"; 537*377e76abSEd Tanous 538*377e76abSEd Tanous xml += 539*377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.ObjectManager\">" 540*377e76abSEd Tanous " <method name=\"GetManagedObjects\">" 541*377e76abSEd Tanous " <arg type=\"a{oa{sa{sv}}}\" " 542*377e76abSEd Tanous " name=\"object_paths_interfaces_and_properties\" " 543*377e76abSEd Tanous " direction=\"out\"/>" 544*377e76abSEd Tanous " </method>" 545*377e76abSEd Tanous " <signal name=\"InterfacesAdded\">" 546*377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>" 547*377e76abSEd Tanous " <arg type=\"a{sa{sv}}\" " 548*377e76abSEd Tanous "name=\"interfaces_and_properties\"/>" 549*377e76abSEd Tanous " </signal>" 550*377e76abSEd Tanous " <signal name=\"InterfacesRemoved\">" 551*377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>" 552*377e76abSEd Tanous " <arg type=\"as\" name=\"interfaces\"/>" 553*377e76abSEd Tanous " </signal>" 554*377e76abSEd Tanous " </interface>"; 555*377e76abSEd Tanous 556*377e76abSEd Tanous for (auto& interface_pair : object->interfaces) { 557*377e76abSEd Tanous xml += "<interface name=\""; 558*377e76abSEd Tanous xml += interface_pair.first; 559*377e76abSEd Tanous xml += "\">"; 560*377e76abSEd Tanous for (auto& method : interface_pair.second->get_methods()) { 561*377e76abSEd Tanous xml += "<method name=\""; 562*377e76abSEd Tanous xml += method.first; 563*377e76abSEd Tanous xml += "\">"; 564*377e76abSEd Tanous for (auto& arg : method.second->get_args()) { 565*377e76abSEd Tanous xml += "<arg name=\""; 566*377e76abSEd Tanous xml += arg.name; 567*377e76abSEd Tanous xml += "\" type=\""; 568*377e76abSEd Tanous xml += arg.type; 569*377e76abSEd Tanous xml += "\" direction=\""; 570*377e76abSEd Tanous xml += arg.direction; 571*377e76abSEd Tanous xml += "\"/>"; 572*377e76abSEd Tanous } 573*377e76abSEd Tanous xml += "</method>"; 574*377e76abSEd Tanous } 575*377e76abSEd Tanous 576*377e76abSEd Tanous for (auto& signal : interface_pair.second->get_signals()) { 577*377e76abSEd Tanous xml += "<signal name=\""; 578*377e76abSEd Tanous xml += signal.first; 579*377e76abSEd Tanous xml += "\">"; 580*377e76abSEd Tanous for (auto& arg : signal.second->get_args()) { 581*377e76abSEd Tanous xml += "<arg name=\""; 582*377e76abSEd Tanous xml += arg.name; 583*377e76abSEd Tanous xml += "\" type=\""; 584*377e76abSEd Tanous xml += arg.type; 585*377e76abSEd Tanous xml += "\"/>"; 586*377e76abSEd Tanous } 587*377e76abSEd Tanous 588*377e76abSEd Tanous xml += "</signal>"; 589*377e76abSEd Tanous } 590*377e76abSEd Tanous 591*377e76abSEd Tanous for (auto& property : interface_pair.second->get_properties_map()) { 592*377e76abSEd Tanous xml += "<property name=\""; 593*377e76abSEd Tanous xml += property.first; 594*377e76abSEd Tanous xml += "\" type =\""; 595*377e76abSEd Tanous 596*377e76abSEd Tanous std::string type = std::string(boost::apply_visitor( 597*377e76abSEd Tanous [&](auto val) { 598*377e76abSEd Tanous static const auto constexpr sig = 599*377e76abSEd Tanous element_signature<decltype(val)>::code; 600*377e76abSEd Tanous return &sig[0]; 601*377e76abSEd Tanous }, 602*377e76abSEd Tanous property.second)); 603*377e76abSEd Tanous xml += type; 604*377e76abSEd Tanous xml += "\" direction =\""; 605*377e76abSEd Tanous // TODO direction can be readwrite, read, or write. Need to 606*377e76abSEd Tanous // make this configurable 607*377e76abSEd Tanous xml += "readwrite"; 608*377e76abSEd Tanous xml += "\"/>"; 609*377e76abSEd Tanous } 610*377e76abSEd Tanous xml += "</interface>"; 611*377e76abSEd Tanous } 612*377e76abSEd Tanous } else if (boost::starts_with(object_name, newpath)) { 613*377e76abSEd Tanous auto slash_index = object_name.find("/", newpath.size() + 1); 614*377e76abSEd Tanous auto subnode = object_name.substr(newpath.size() + 1, 615*377e76abSEd Tanous slash_index - newpath.size() - 1); 616*377e76abSEd Tanous if (node_names.find(subnode) == node_names.end()) { 617*377e76abSEd Tanous node_names.insert(subnode); 618*377e76abSEd Tanous xml += "<node name=\""; 619*377e76abSEd Tanous xml += subnode; 620*377e76abSEd Tanous xml += "\">"; 621*377e76abSEd Tanous xml += "</node>"; 622*377e76abSEd Tanous } 623*377e76abSEd Tanous } 624*377e76abSEd Tanous } 625*377e76abSEd Tanous xml += "</node>"; 626*377e76abSEd Tanous return xml; 627*377e76abSEd Tanous } 628*377e76abSEd Tanous 629*377e76abSEd Tanous private: 630*377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 631*377e76abSEd Tanous std::vector<std::shared_ptr<DbusObject>> objects; 632*377e76abSEd Tanous std::unique_ptr<dbus::filter> introspect_filter; 633*377e76abSEd Tanous std::unique_ptr<dbus::filter> object_manager_filter; 634*377e76abSEd Tanous std::unique_ptr<dbus::filter> method_filter; 635*377e76abSEd Tanous }; 636*377e76abSEd Tanous } 637