1377e76abSEd Tanous #include <dbus/connection.hpp> 2377e76abSEd Tanous #include <dbus/filter.hpp> 3377e76abSEd Tanous #include <dbus/match.hpp> 4377e76abSEd Tanous #include <functional> 5377e76abSEd Tanous #include <tuple> 6377e76abSEd Tanous #include <type_traits> 7377e76abSEd Tanous #include <boost/algorithm/string/predicate.hpp> 8377e76abSEd Tanous #include <boost/container/flat_map.hpp> 9377e76abSEd Tanous #include <boost/container/flat_set.hpp> 10377e76abSEd Tanous 11377e76abSEd Tanous namespace dbus { 12377e76abSEd Tanous struct DbusArgument { 13377e76abSEd Tanous DbusArgument(const std::string& direction, const std::string& name, 14377e76abSEd Tanous const std::string& type) 15377e76abSEd Tanous : direction(direction), name(name), type(type){}; 16377e76abSEd Tanous std::string direction; 17377e76abSEd Tanous std::string name; 18377e76abSEd Tanous std::string type; 19377e76abSEd Tanous }; 20377e76abSEd Tanous 21377e76abSEd Tanous class DbusMethod { 22377e76abSEd Tanous public: 23*e3b0bf5bSEd Tanous DbusMethod(const std::string& name, std::shared_ptr<dbus::connection>& conn) 24377e76abSEd Tanous : name(name), conn(conn){}; 25377e76abSEd Tanous virtual void call(dbus::message& m){}; 26377e76abSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; }; 27377e76abSEd Tanous std::string name; 28377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 29377e76abSEd Tanous }; 30377e76abSEd Tanous 31377e76abSEd Tanous enum class UpdateType { VALUE_CHANGE_ONLY, FORCE }; 32377e76abSEd Tanous 33377e76abSEd Tanous // primary template. 34377e76abSEd Tanous template <class T> 35377e76abSEd Tanous struct function_traits : function_traits<decltype(&T::operator())> {}; 36377e76abSEd Tanous 37377e76abSEd Tanous // partial specialization for function type 38377e76abSEd Tanous template <class R, class... Args> 39377e76abSEd Tanous struct function_traits<R(Args...)> { 40377e76abSEd Tanous using result_type = R; 41377e76abSEd Tanous using argument_types = std::tuple<Args...>; 42377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 43377e76abSEd Tanous }; 44377e76abSEd Tanous 45377e76abSEd Tanous // partial specialization for function pointer 46377e76abSEd Tanous template <class R, class... Args> 47377e76abSEd Tanous struct function_traits<R (*)(Args...)> { 48377e76abSEd Tanous using result_type = R; 49377e76abSEd Tanous using argument_types = std::tuple<Args...>; 50377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 51377e76abSEd Tanous }; 52377e76abSEd Tanous 53377e76abSEd Tanous // partial specialization for std::function 54377e76abSEd Tanous template <class R, class... Args> 55377e76abSEd Tanous struct function_traits<std::function<R(Args...)>> { 56377e76abSEd Tanous using result_type = R; 57377e76abSEd Tanous using argument_types = std::tuple<Args...>; 58377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 59377e76abSEd Tanous }; 60377e76abSEd Tanous 61377e76abSEd Tanous // partial specialization for pointer-to-member-function (i.e., operator()'s) 62377e76abSEd Tanous template <class T, class R, class... Args> 63377e76abSEd Tanous struct function_traits<R (T::*)(Args...)> { 64377e76abSEd Tanous using result_type = R; 65377e76abSEd Tanous using argument_types = std::tuple<Args...>; 66377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 67377e76abSEd Tanous }; 68377e76abSEd Tanous 69377e76abSEd Tanous template <class T, class R, class... Args> 70377e76abSEd Tanous struct function_traits<R (T::*)(Args...) const> { 71377e76abSEd Tanous using result_type = R; 72377e76abSEd Tanous using argument_types = std::tuple<Args...>; 73377e76abSEd Tanous using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 74377e76abSEd Tanous }; 75377e76abSEd Tanous 76377e76abSEd Tanous template <class F, size_t... Is> 77377e76abSEd Tanous constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) { 78377e76abSEd Tanous return f(std::integral_constant<size_t, Is>{}...); 79377e76abSEd Tanous } 80377e76abSEd Tanous 81377e76abSEd Tanous template <size_t N, class F> 82377e76abSEd Tanous constexpr auto index_apply(F f) { 83377e76abSEd Tanous return index_apply_impl(f, std::make_index_sequence<N>{}); 84377e76abSEd Tanous } 85377e76abSEd Tanous 86377e76abSEd Tanous template <class Tuple, class F> 87377e76abSEd Tanous constexpr auto apply(Tuple& t, F f) { 88377e76abSEd Tanous return index_apply<std::tuple_size<Tuple>{}>( 89377e76abSEd Tanous [&](auto... Is) { return f(std::get<Is>(t)...); }); 90377e76abSEd Tanous } 91377e76abSEd Tanous 92377e76abSEd Tanous template <class Tuple> 93377e76abSEd Tanous constexpr void unpack_into_tuple(Tuple& t, dbus::message& m) { 94*e3b0bf5bSEd Tanous index_apply<std::tuple_size<Tuple>{}>( 95377e76abSEd Tanous [&](auto... Is) { m.unpack(std::get<Is>(t)...); }); 96377e76abSEd Tanous } 97377e76abSEd Tanous 98377e76abSEd Tanous // Specialization for empty tuples. No need to unpack if no arguments 99377e76abSEd Tanous constexpr void unpack_into_tuple(std::tuple<>& t, dbus::message& m) {} 100377e76abSEd Tanous 101*e3b0bf5bSEd Tanous template <typename... Args> 102*e3b0bf5bSEd Tanous constexpr void pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) { 103*e3b0bf5bSEd Tanous index_apply<std::tuple_size<std::tuple<Args...>>{}>( 104377e76abSEd Tanous [&](auto... Is) { m.pack(std::get<Is>(t)...); }); 105377e76abSEd Tanous } 106377e76abSEd Tanous 107377e76abSEd Tanous // Specialization for empty tuples. No need to pack if no arguments 108377e76abSEd Tanous constexpr void pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {} 109377e76abSEd Tanous 110*e3b0bf5bSEd Tanous template <typename Element> 111*e3b0bf5bSEd Tanous constexpr void pack_tuple_into_msg(Element& t, dbus::message& m) { 112*e3b0bf5bSEd Tanous m.pack(t); 113*e3b0bf5bSEd Tanous } 114*e3b0bf5bSEd Tanous 115377e76abSEd Tanous // Base case for when I == the size of the tuple args. Does nothing, as we 116377e76abSEd Tanous // should be done 117377e76abSEd Tanous template <std::size_t I = 0, typename... Tp> 118377e76abSEd Tanous inline typename std::enable_if<I == sizeof...(Tp), void>::type arg_types( 119*e3b0bf5bSEd Tanous bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v, 120*e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) {} 121377e76abSEd Tanous 122377e76abSEd Tanous // Case for when I < the size of tuple args. Unpacks the tuple type into the 123377e76abSEd Tanous // dbusargument object and names it appropriately. 124377e76abSEd Tanous template <std::size_t I = 0, typename... Tp> 125377e76abSEd Tanous inline typename std::enable_if < 126*e3b0bf5bSEd Tanous I<sizeof...(Tp), void>::type arg_types( 127*e3b0bf5bSEd Tanous bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v, 128*e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) { 129377e76abSEd Tanous typedef typename std::tuple_element<I, std::tuple<Tp...>>::type element_type; 130377e76abSEd Tanous auto constexpr sig = element_signature<element_type>::code; 131*e3b0bf5bSEd Tanous std::string name; 132*e3b0bf5bSEd Tanous std::string direction; 133*e3b0bf5bSEd Tanous if (arg_names == nullptr || arg_names->size() <= I) { 134377e76abSEd Tanous if (in) { 135*e3b0bf5bSEd Tanous name = "arg_" + std::to_string(I); 136377e76abSEd Tanous } else { 137*e3b0bf5bSEd Tanous name = "out_" + std::to_string(I); 138377e76abSEd Tanous } 139*e3b0bf5bSEd Tanous } else { 140*e3b0bf5bSEd Tanous name = (*arg_names)[I]; 141*e3b0bf5bSEd Tanous } 142*e3b0bf5bSEd Tanous v.emplace_back(in ? "in" : "out", name, &sig[0]); 143*e3b0bf5bSEd Tanous 144*e3b0bf5bSEd Tanous arg_types<I + 1, Tp...>(in, t, v, arg_names); 145*e3b0bf5bSEd Tanous } 146*e3b0bf5bSEd Tanous 147*e3b0bf5bSEd Tanous // Special case for handling raw arguments returned from handlers. Because they 148*e3b0bf5bSEd Tanous // don't get stored in a tuple, special handling is neccesary 149*e3b0bf5bSEd Tanous template <typename Element> 150*e3b0bf5bSEd Tanous void arg_types(bool in, Element& t, std::vector<DbusArgument>& v, 151*e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) { 152*e3b0bf5bSEd Tanous auto constexpr sig = element_signature<Element>::code; 153*e3b0bf5bSEd Tanous std::string name; 154*e3b0bf5bSEd Tanous if (arg_names == nullptr || arg_names->size() < 1) { 155*e3b0bf5bSEd Tanous name.assign("arg_0"); 156*e3b0bf5bSEd Tanous } else { 157*e3b0bf5bSEd Tanous name = (*arg_names)[0]; 158*e3b0bf5bSEd Tanous } 159*e3b0bf5bSEd Tanous 160*e3b0bf5bSEd Tanous v.emplace_back(in ? "in" : "out", name, &sig[0]); 161377e76abSEd Tanous } 162377e76abSEd Tanous 163377e76abSEd Tanous template <typename Handler> 164377e76abSEd Tanous class LambdaDbusMethod : public DbusMethod { 165377e76abSEd Tanous public: 166377e76abSEd Tanous typedef function_traits<Handler> traits; 167377e76abSEd Tanous typedef typename traits::decayed_arg_types InputTupleType; 168*e3b0bf5bSEd Tanous typedef typename traits::result_type ResultType; 169*e3b0bf5bSEd Tanous LambdaDbusMethod(const std::string name, 170*e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn, Handler h) 171*e3b0bf5bSEd Tanous : DbusMethod(name, conn), h(std::move(h)) { 172*e3b0bf5bSEd Tanous InputTupleType t; 173*e3b0bf5bSEd Tanous arg_types(true, t, args); 174*e3b0bf5bSEd Tanous 175*e3b0bf5bSEd Tanous ResultType o; 176*e3b0bf5bSEd Tanous arg_types(false, o, args); 177*e3b0bf5bSEd Tanous } 178*e3b0bf5bSEd Tanous 179*e3b0bf5bSEd Tanous LambdaDbusMethod(const std::string& name, 180*e3b0bf5bSEd Tanous const std::vector<std::string>& input_arg_names, 181*e3b0bf5bSEd Tanous const std::vector<std::string>& output_arg_names, 182*e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn, Handler h) 183*e3b0bf5bSEd Tanous : DbusMethod(name, conn), h(std::move(h)) { 184*e3b0bf5bSEd Tanous InputTupleType t; 185*e3b0bf5bSEd Tanous arg_types(true, t, args, &input_arg_names); 186*e3b0bf5bSEd Tanous 187*e3b0bf5bSEd Tanous ResultType o; 188*e3b0bf5bSEd Tanous arg_types(false, o, args, &output_arg_names); 189*e3b0bf5bSEd Tanous } 190377e76abSEd Tanous void call(dbus::message& m) override { 191377e76abSEd Tanous InputTupleType input_args; 192377e76abSEd Tanous unpack_into_tuple(input_args, m); 193*e3b0bf5bSEd Tanous ResultType r = apply(input_args, h); 194377e76abSEd Tanous auto ret = dbus::message::new_return(m); 195377e76abSEd Tanous pack_tuple_into_msg(r, ret); 196377e76abSEd Tanous conn->send(ret, std::chrono::seconds(0)); 197377e76abSEd Tanous }; 198377e76abSEd Tanous 199*e3b0bf5bSEd Tanous std::vector<DbusArgument> get_args() override { return args; }; 200377e76abSEd Tanous Handler h; 201*e3b0bf5bSEd Tanous std::vector<DbusArgument> args; 202377e76abSEd Tanous }; 203377e76abSEd Tanous 204377e76abSEd Tanous class DbusSignal { 205377e76abSEd Tanous public: 206*e3b0bf5bSEd Tanous DbusSignal(){}; 207*e3b0bf5bSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; } 208*e3b0bf5bSEd Tanous }; 209377e76abSEd Tanous 210*e3b0bf5bSEd Tanous template <typename... Args> 211*e3b0bf5bSEd Tanous class DbusTemplateSignal : public DbusSignal { 212*e3b0bf5bSEd Tanous public: 213*e3b0bf5bSEd Tanous DbusTemplateSignal(const std::string& name, const std::string& object_name, 214*e3b0bf5bSEd Tanous const std::string& interface_name, 215*e3b0bf5bSEd Tanous const std::vector<std::string>& names, 216*e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn) 217*e3b0bf5bSEd Tanous : DbusSignal(), 218*e3b0bf5bSEd Tanous name(name), 219*e3b0bf5bSEd Tanous object_name(object_name), 220*e3b0bf5bSEd Tanous interface_name(interface_name), 221*e3b0bf5bSEd Tanous conn(conn) { 222*e3b0bf5bSEd Tanous std::tuple<Args...> tu; 223*e3b0bf5bSEd Tanous arg_types(true, tu, args, &names); 224*e3b0bf5bSEd Tanous }; 225*e3b0bf5bSEd Tanous 226*e3b0bf5bSEd Tanous void send(Args&...) { 227*e3b0bf5bSEd Tanous dbus::endpoint endpoint("", object_name, interface_name); 228*e3b0bf5bSEd Tanous auto m = dbus::message::new_signal(endpoint, name); 229*e3b0bf5bSEd Tanous conn->send(m, std::chrono::seconds(0)); 230*e3b0bf5bSEd Tanous } 231*e3b0bf5bSEd Tanous 232*e3b0bf5bSEd Tanous std::vector<DbusArgument> get_args() override { return args; }; 233*e3b0bf5bSEd Tanous 234*e3b0bf5bSEd Tanous std::vector<DbusArgument> args; 235*e3b0bf5bSEd Tanous std::string name; 236*e3b0bf5bSEd Tanous std::string object_name; 237*e3b0bf5bSEd Tanous std::string interface_name; 238*e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection> conn; 239377e76abSEd Tanous }; 240377e76abSEd Tanous 241377e76abSEd Tanous class DbusInterface { 242377e76abSEd Tanous public: 243377e76abSEd Tanous DbusInterface(std::string interface_name, 244377e76abSEd Tanous std::shared_ptr<dbus::connection>& conn) 245377e76abSEd Tanous : interface_name(std::move(interface_name)), conn(conn) {} 246377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>> 247377e76abSEd Tanous get_signals() { 248377e76abSEd Tanous return dbus_signals; 249377e76abSEd Tanous }; 250377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>> 251377e76abSEd Tanous get_methods() { 252377e76abSEd Tanous return dbus_methods; 253377e76abSEd Tanous }; 254377e76abSEd Tanous virtual std::string get_interface_name() { return interface_name; }; 255377e76abSEd Tanous virtual const boost::container::flat_map<std::string, dbus_variant> 256377e76abSEd Tanous get_properties_map() { 257377e76abSEd Tanous return properties_map; 258377e76abSEd Tanous }; 259377e76abSEd Tanous 260377e76abSEd Tanous template <typename VALUE_TYPE> 261377e76abSEd Tanous void set_property(const std::string& property_name, const VALUE_TYPE value, 262377e76abSEd Tanous UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) { 263377e76abSEd Tanous // Construct a change vector of length 1. if this set_properties is ever 264377e76abSEd Tanous // templated for any type, we could probably swap with with a 265377e76abSEd Tanous // std::array<pair, 1> 266377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 267377e76abSEd Tanous v.emplace_back(property_name, value); 268377e76abSEd Tanous set_properties(v, update_mode); 269377e76abSEd Tanous } 270377e76abSEd Tanous 271377e76abSEd Tanous void set_properties( 272377e76abSEd Tanous const std::vector<std::pair<std::string, dbus_variant>>& v, 273377e76abSEd Tanous const UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) { 274377e76abSEd Tanous // TODO(ed) generalize this interface for all "map like" types, basically 275377e76abSEd Tanous // anything that will return a const iterator of std::pair<string, 276377e76abSEd Tanous // variant> 277377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> updates; 278377e76abSEd Tanous updates.reserve(v.size()); 279377e76abSEd Tanous 280377e76abSEd Tanous if (update_mode == UpdateType::FORCE) { 281377e76abSEd Tanous updates = v; 282377e76abSEd Tanous } else { 283377e76abSEd Tanous for (auto& property : v) { 284377e76abSEd Tanous auto property_map_it = properties_map.find(property.first); 285377e76abSEd Tanous if (property_map_it != properties_map.end()) { 286377e76abSEd Tanous // Property exists in map 287377e76abSEd Tanous if (property_map_it->second != property.second) { 288377e76abSEd Tanous properties_map[property.first] = property.second; 289377e76abSEd Tanous // if value has changed since last set 290377e76abSEd Tanous updates.emplace_back(*property_map_it); 291377e76abSEd Tanous } 292377e76abSEd Tanous } else { 293377e76abSEd Tanous // property doesn't exist, must be new 294377e76abSEd Tanous properties_map[property.first] = property.second; 295377e76abSEd Tanous updates.emplace_back(property.first, property.second); 296377e76abSEd Tanous } 297377e76abSEd Tanous } 298377e76abSEd Tanous } 299377e76abSEd Tanous 300377e76abSEd Tanous const static dbus::endpoint endpoint("org.freedesktop.DBus", object_name, 301377e76abSEd Tanous "org.freedesktop.DBus.Properties"); 302377e76abSEd Tanous 303377e76abSEd Tanous auto m = dbus::message::new_signal(endpoint, "PropertiesChanged"); 304377e76abSEd Tanous 305377e76abSEd Tanous static const std::vector<std::string> empty; 306377e76abSEd Tanous m.pack(get_interface_name(), updates, empty); 307377e76abSEd Tanous // TODO(ed) make sure this doesn't block 308377e76abSEd Tanous conn->async_send( 309377e76abSEd Tanous m, [](const boost::system::error_code ec, dbus::message r) {}); 310377e76abSEd Tanous } 311377e76abSEd Tanous 312*e3b0bf5bSEd Tanous void register_method(std::shared_ptr<DbusMethod> method) { 313377e76abSEd Tanous dbus_methods.emplace(method->name, method); 314377e76abSEd Tanous } 315377e76abSEd Tanous 316377e76abSEd Tanous template <typename Handler> 317*e3b0bf5bSEd Tanous void register_method(const std::string& name, Handler method) { 318377e76abSEd Tanous dbus_methods.emplace(name, 319377e76abSEd Tanous new LambdaDbusMethod<Handler>(name, conn, method)); 320377e76abSEd Tanous } 321377e76abSEd Tanous 322*e3b0bf5bSEd Tanous template <typename Handler> 323*e3b0bf5bSEd Tanous void register_method(const std::string& name, 324*e3b0bf5bSEd Tanous const std::vector<std::string>& input_arg_names, 325*e3b0bf5bSEd Tanous const std::vector<std::string>& output_arg_names, 326*e3b0bf5bSEd Tanous Handler method) { 327*e3b0bf5bSEd Tanous dbus_methods.emplace( 328*e3b0bf5bSEd Tanous name, new LambdaDbusMethod<Handler>(name, input_arg_names, 329*e3b0bf5bSEd Tanous output_arg_names, conn, method)); 330*e3b0bf5bSEd Tanous } 331*e3b0bf5bSEd Tanous 332*e3b0bf5bSEd Tanous template <typename... Args> 333*e3b0bf5bSEd Tanous std::shared_ptr<DbusSignal> register_signal( 334*e3b0bf5bSEd Tanous const std::string& name, const std::vector<std::string> arg_names) { 335*e3b0bf5bSEd Tanous auto it = dbus_signals.emplace( 336*e3b0bf5bSEd Tanous name, new DbusTemplateSignal<Args...>(name, object_name, interface_name, 337*e3b0bf5bSEd Tanous arg_names, conn)); 338*e3b0bf5bSEd Tanous return it.first->second; 339*e3b0bf5bSEd Tanous } 340*e3b0bf5bSEd Tanous 341377e76abSEd Tanous void call(dbus::message& m) { 342377e76abSEd Tanous std::string method_name = m.get_member(); 343377e76abSEd Tanous auto method = dbus_methods.find(method_name); 344377e76abSEd Tanous if (method != dbus_methods.end()) { 345377e76abSEd Tanous method->second->call(m); 346377e76abSEd Tanous } // TODO(ed) send something when method doesn't exist? 347377e76abSEd Tanous } 348377e76abSEd Tanous 349377e76abSEd Tanous std::string object_name; 350377e76abSEd Tanous std::string interface_name; 351377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>> 352377e76abSEd Tanous dbus_methods; 353377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>> 354377e76abSEd Tanous dbus_signals; 355377e76abSEd Tanous boost::container::flat_map<std::string, dbus_variant> properties_map; 356377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 357377e76abSEd Tanous }; 358377e76abSEd Tanous 359377e76abSEd Tanous class DbusObject { 360377e76abSEd Tanous public: 361377e76abSEd Tanous DbusObject(std::shared_ptr<dbus::connection> conn, std::string object_name) 362377e76abSEd Tanous : object_name(std::move(object_name)), conn(conn) { 363377e76abSEd Tanous properties_iface = add_interface("org.freedesktop.DBus.Properties"); 364377e76abSEd Tanous 365377e76abSEd Tanous properties_iface->register_method( 366*e3b0bf5bSEd Tanous "Get", {"interface_name", "properties_name"}, {"value"}, 367*e3b0bf5bSEd Tanous [&](const std::string& interface_name, 368377e76abSEd Tanous const std::string& property_name) { 369377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 370377e76abSEd Tanous if (interface_it == interfaces.end()) { 371377e76abSEd Tanous // Interface not found error 372377e76abSEd Tanous throw std::runtime_error("interface not found"); 373377e76abSEd Tanous } else { 374377e76abSEd Tanous auto& properties_map = interface_it->second->get_properties_map(); 375377e76abSEd Tanous auto property = properties_map.find(property_name); 376377e76abSEd Tanous if (property == properties_map.end()) { 377377e76abSEd Tanous // TODO(ed) property not found error 378377e76abSEd Tanous throw std::runtime_error("property not found"); 379377e76abSEd Tanous } else { 380377e76abSEd Tanous return std::tuple<dbus_variant>(property->second); 381377e76abSEd Tanous } 382377e76abSEd Tanous } 383377e76abSEd Tanous }); 384377e76abSEd Tanous 385*e3b0bf5bSEd Tanous properties_iface->register_method( 386*e3b0bf5bSEd Tanous "GetAll", {"interface_name"}, {"properties"}, 387*e3b0bf5bSEd Tanous [&](const std::string& interface_name) { 388377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 389377e76abSEd Tanous if (interface_it == interfaces.end()) { 390377e76abSEd Tanous // Interface not found error 391377e76abSEd Tanous throw std::runtime_error("interface not found"); 392377e76abSEd Tanous } else { 393377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 394377e76abSEd Tanous for (auto& element : properties_iface->get_properties_map()) { 395377e76abSEd Tanous v.emplace_back(element.first, element.second); 396377e76abSEd Tanous } 397*e3b0bf5bSEd Tanous return std::tuple< 398*e3b0bf5bSEd Tanous std::vector<std::pair<std::string, dbus_variant>>>(v); 399377e76abSEd Tanous } 400377e76abSEd Tanous }); 401377e76abSEd Tanous properties_iface->register_method( 402*e3b0bf5bSEd Tanous "Set", {"interface_name", "properties_name", "value"}, {}, 403377e76abSEd Tanous [&](const std::string& interface_name, const std::string& property_name, 404377e76abSEd Tanous const dbus_variant& value) { 405377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 406377e76abSEd Tanous if (interface_it == interfaces.end()) { 407377e76abSEd Tanous // Interface not found error 408377e76abSEd Tanous throw std::runtime_error("interface not found"); 409377e76abSEd Tanous } else { 410377e76abSEd Tanous // Todo, the set propery (signular) interface should support 411377e76abSEd Tanous // handing 412377e76abSEd Tanous // a variant. THe below is expensive 413377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 414377e76abSEd Tanous v.emplace_back(property_name, value); 415377e76abSEd Tanous interface_it->second->set_properties(v); 416377e76abSEd Tanous return std::tuple<>(); 417377e76abSEd Tanous } 418377e76abSEd Tanous }); 419*e3b0bf5bSEd Tanous 420*e3b0bf5bSEd Tanous properties_iface->register_signal< 421*e3b0bf5bSEd Tanous std::string, std::vector<std::pair<std::string, dbus_variant>>, 422*e3b0bf5bSEd Tanous std::vector<std::string>>( 423*e3b0bf5bSEd Tanous "PropertiesChanged", 424*e3b0bf5bSEd Tanous {"interface_name", "changed_properties", "invalidated_properties"}); 425377e76abSEd Tanous } 426377e76abSEd Tanous 427377e76abSEd Tanous std::shared_ptr<DbusInterface> add_interface(const std::string& name) { 428377e76abSEd Tanous auto x = std::make_shared<DbusInterface>(name, conn); 429377e76abSEd Tanous register_interface(x); 430377e76abSEd Tanous return x; 431377e76abSEd Tanous } 432377e76abSEd Tanous 433377e76abSEd Tanous void register_interface(std::shared_ptr<DbusInterface>& interface) { 434377e76abSEd Tanous interfaces[interface->get_interface_name()] = interface; 435377e76abSEd Tanous interface->object_name = object_name; 436377e76abSEd Tanous const static dbus::endpoint endpoint("", object_name, 437377e76abSEd Tanous "org.freedesktop.DBus.ObjectManager"); 438377e76abSEd Tanous 439*e3b0bf5bSEd Tanous auto m = message::new_signal(endpoint, "InterfacesAdded"); 440377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus_variant>> properties_dict; 441377e76abSEd Tanous std::vector<std::pair<std::string, properties_dict>> sig; 442377e76abSEd Tanous sig.emplace_back(interface->get_interface_name(), properties_dict()); 443377e76abSEd Tanous auto& prop_dict = sig.back().second; 444377e76abSEd Tanous for (auto& property : interface->get_properties_map()) { 445377e76abSEd Tanous prop_dict.emplace_back(property); 446377e76abSEd Tanous } 447377e76abSEd Tanous 448377e76abSEd Tanous m.pack(object_name, sig); 449*e3b0bf5bSEd Tanous // TODO(ed) 450*e3b0bf5bSEd Tanous // conn->send(m, std::chrono::seconds(0)); 451377e76abSEd Tanous } 452377e76abSEd Tanous 453377e76abSEd Tanous auto get_interfaces() { return interfaces; } 454377e76abSEd Tanous 455377e76abSEd Tanous void call(dbus::message& m) { 456377e76abSEd Tanous auto interface = interfaces.find(m.get_interface()); 457377e76abSEd Tanous if (interface != interfaces.end()) { 458377e76abSEd Tanous interface->second->call(m); 459377e76abSEd Tanous } // TODO(ed) send something when interface doesn't exist? 460377e76abSEd Tanous } 461377e76abSEd Tanous 462377e76abSEd Tanous std::string object_name; 463377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 464377e76abSEd Tanous 465377e76abSEd Tanous // dbus::filter properties_filter; 466377e76abSEd Tanous std::shared_ptr<DbusInterface> properties_iface; 467377e76abSEd Tanous 468*e3b0bf5bSEd Tanous std::shared_ptr<DbusInterface> object_manager_iface; 469*e3b0bf5bSEd Tanous 470377e76abSEd Tanous std::function<void(boost::system::error_code, message)> callback; 471377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusInterface>> 472377e76abSEd Tanous interfaces; 473377e76abSEd Tanous }; 474377e76abSEd Tanous 475377e76abSEd Tanous class DbusObjectServer { 476377e76abSEd Tanous public: 477377e76abSEd Tanous DbusObjectServer(std::shared_ptr<dbus::connection>& conn) : conn(conn) { 478377e76abSEd Tanous introspect_filter = 479377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) { 480377e76abSEd Tanous if (m.get_type() != "method_call") { 481377e76abSEd Tanous return false; 482377e76abSEd Tanous } 483377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.Introspectable") { 484377e76abSEd Tanous return false; 485377e76abSEd Tanous } 486377e76abSEd Tanous if (m.get_member() != "Introspect") { 487377e76abSEd Tanous return false; 488377e76abSEd Tanous }; 489377e76abSEd Tanous return true; 490377e76abSEd Tanous }); 491377e76abSEd Tanous 492377e76abSEd Tanous introspect_filter->async_dispatch( 493377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 494377e76abSEd Tanous on_introspect(ec, m); 495377e76abSEd Tanous }); 496377e76abSEd Tanous 497377e76abSEd Tanous object_manager_filter = 498377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) { 499377e76abSEd Tanous 500377e76abSEd Tanous if (m.get_type() != "method_call") { 501377e76abSEd Tanous return false; 502377e76abSEd Tanous } 503377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.ObjectManager") { 504377e76abSEd Tanous return false; 505377e76abSEd Tanous } 506377e76abSEd Tanous if (m.get_member() != "GetManagedObjects") { 507377e76abSEd Tanous return false; 508377e76abSEd Tanous }; 509377e76abSEd Tanous return true; 510377e76abSEd Tanous }); 511377e76abSEd Tanous 512377e76abSEd Tanous object_manager_filter->async_dispatch( 513377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 514377e76abSEd Tanous on_get_managed_objects(ec, m); 515377e76abSEd Tanous }); 516377e76abSEd Tanous 517377e76abSEd Tanous method_filter = std::make_unique<dbus::filter>(conn, [](dbus::message m) { 518377e76abSEd Tanous 519377e76abSEd Tanous if (m.get_type() != "method_call") { 520377e76abSEd Tanous return false; 521377e76abSEd Tanous } 522377e76abSEd Tanous return true; 523377e76abSEd Tanous }); 524377e76abSEd Tanous 525377e76abSEd Tanous method_filter->async_dispatch( 526377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 527377e76abSEd Tanous on_method_call(ec, m); 528377e76abSEd Tanous }); 529377e76abSEd Tanous }; 530377e76abSEd Tanous 531377e76abSEd Tanous std::shared_ptr<dbus::connection>& get_connection() { return conn; } 532377e76abSEd Tanous void on_introspect(const boost::system::error_code ec, dbus::message m) { 533377e76abSEd Tanous auto xml = get_xml_for_path(m.get_path()); 534*e3b0bf5bSEd Tanous std::cout << "path: " << m.get_path() << "\n" << xml << "\n"; 535377e76abSEd Tanous auto ret = dbus::message::new_return(m); 536377e76abSEd Tanous ret.pack(xml); 537377e76abSEd Tanous conn->async_send( 538377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {}); 539377e76abSEd Tanous 540377e76abSEd Tanous introspect_filter->async_dispatch( 541377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 542377e76abSEd Tanous on_introspect(ec, m); 543377e76abSEd Tanous }); 544377e76abSEd Tanous } 545377e76abSEd Tanous 546377e76abSEd Tanous void on_method_call(const boost::system::error_code ec, dbus::message m) { 547377e76abSEd Tanous std::cout << "on method call\n"; 548377e76abSEd Tanous if (ec) { 549377e76abSEd Tanous std::cerr << "on_method_call error: " << ec << "\n"; 550377e76abSEd Tanous } else { 551377e76abSEd Tanous auto path = m.get_path(); 552377e76abSEd Tanous // TODO(ed) objects should be a map 553377e76abSEd Tanous for (auto& object : objects) { 554377e76abSEd Tanous if (object->object_name == path) { 555377e76abSEd Tanous object->call(m); 556377e76abSEd Tanous break; 557377e76abSEd Tanous } 558377e76abSEd Tanous } 559377e76abSEd Tanous } 560377e76abSEd Tanous method_filter->async_dispatch( 561377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 562377e76abSEd Tanous on_method_call(ec, m); 563377e76abSEd Tanous }); 564377e76abSEd Tanous } 565377e76abSEd Tanous 566377e76abSEd Tanous void on_get_managed_objects(const boost::system::error_code ec, 567377e76abSEd Tanous dbus::message m) { 568377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus::dbus_variant>> 569377e76abSEd Tanous properties_dict; 570377e76abSEd Tanous 571377e76abSEd Tanous typedef std::vector<std::pair<std::string, properties_dict>> 572377e76abSEd Tanous interfaces_dict; 573377e76abSEd Tanous 574377e76abSEd Tanous std::vector<std::pair<std::string, interfaces_dict>> dict; 575377e76abSEd Tanous 576377e76abSEd Tanous for (auto& object : objects) { 577377e76abSEd Tanous interfaces_dict i; 578377e76abSEd Tanous for (auto& interface : object->get_interfaces()) { 579377e76abSEd Tanous properties_dict p; 580377e76abSEd Tanous 581377e76abSEd Tanous for (auto& property : interface.second->get_properties_map()) { 582377e76abSEd Tanous p.push_back(property); 583377e76abSEd Tanous } 584377e76abSEd Tanous 585377e76abSEd Tanous i.emplace_back(interface.second->get_interface_name(), std::move(p)); 586377e76abSEd Tanous } 587377e76abSEd Tanous dict.emplace_back(object->object_name, std::move(i)); 588377e76abSEd Tanous } 589377e76abSEd Tanous auto ret = dbus::message::new_return(m); 590377e76abSEd Tanous ret.pack(dict); 591377e76abSEd Tanous conn->async_send( 592377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {}); 593377e76abSEd Tanous 594377e76abSEd Tanous object_manager_filter->async_dispatch( 595377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 596377e76abSEd Tanous on_get_managed_objects(ec, m); 597377e76abSEd Tanous }); 598377e76abSEd Tanous } 599377e76abSEd Tanous 600377e76abSEd Tanous std::shared_ptr<DbusObject> add_object(const std::string& name) { 601377e76abSEd Tanous auto x = std::make_shared<DbusObject>(conn, name); 602377e76abSEd Tanous register_object(x); 603377e76abSEd Tanous return x; 604377e76abSEd Tanous } 605377e76abSEd Tanous 606377e76abSEd Tanous void register_object(std::shared_ptr<DbusObject> object) { 607377e76abSEd Tanous objects.emplace_back(object); 608377e76abSEd Tanous } 609377e76abSEd Tanous 610377e76abSEd Tanous std::string get_xml_for_path(const std::string& path) { 611377e76abSEd Tanous std::string newpath(path); 612377e76abSEd Tanous 613377e76abSEd Tanous if (newpath == "/") { 614377e76abSEd Tanous newpath.assign(""); 615377e76abSEd Tanous } 616377e76abSEd Tanous 617377e76abSEd Tanous boost::container::flat_set<std::string> node_names; 618377e76abSEd Tanous std::string xml( 619377e76abSEd Tanous "<!DOCTYPE node PUBLIC " 620377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " 621377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/" 622377e76abSEd Tanous "introspect.dtd\">\n<node>"); 623377e76abSEd Tanous for (auto& object : objects) { 624377e76abSEd Tanous std::string& object_name = object->object_name; 625377e76abSEd Tanous // exact match 626377e76abSEd Tanous if (object->object_name == newpath) { 627377e76abSEd Tanous xml += 628377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.Peer\">" 629377e76abSEd Tanous " <method name=\"Ping\"/>" 630377e76abSEd Tanous " <method name=\"GetMachineId\">" 631377e76abSEd Tanous " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>" 632377e76abSEd Tanous " </method>" 633377e76abSEd Tanous " </interface>"; 634377e76abSEd Tanous 635377e76abSEd Tanous xml += 636377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.ObjectManager\">" 637377e76abSEd Tanous " <method name=\"GetManagedObjects\">" 638377e76abSEd Tanous " <arg type=\"a{oa{sa{sv}}}\" " 639377e76abSEd Tanous " name=\"object_paths_interfaces_and_properties\" " 640377e76abSEd Tanous " direction=\"out\"/>" 641377e76abSEd Tanous " </method>" 642377e76abSEd Tanous " <signal name=\"InterfacesAdded\">" 643377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>" 644377e76abSEd Tanous " <arg type=\"a{sa{sv}}\" " 645377e76abSEd Tanous "name=\"interfaces_and_properties\"/>" 646377e76abSEd Tanous " </signal>" 647377e76abSEd Tanous " <signal name=\"InterfacesRemoved\">" 648377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>" 649377e76abSEd Tanous " <arg type=\"as\" name=\"interfaces\"/>" 650377e76abSEd Tanous " </signal>" 651377e76abSEd Tanous " </interface>"; 652377e76abSEd Tanous 653*e3b0bf5bSEd Tanous xml += 654*e3b0bf5bSEd Tanous "<interface name=\"org.freedesktop.DBus.Introspectable\">" 655*e3b0bf5bSEd Tanous " <method name=\"Introspect\">" 656*e3b0bf5bSEd Tanous " <arg type=\"s\" name=\"xml_data\" direction=\"out\"/>" 657*e3b0bf5bSEd Tanous " </method>" 658*e3b0bf5bSEd Tanous "</interface>"; 659*e3b0bf5bSEd Tanous 660377e76abSEd Tanous for (auto& interface_pair : object->interfaces) { 661377e76abSEd Tanous xml += "<interface name=\""; 662377e76abSEd Tanous xml += interface_pair.first; 663377e76abSEd Tanous xml += "\">"; 664377e76abSEd Tanous for (auto& method : interface_pair.second->get_methods()) { 665377e76abSEd Tanous xml += "<method name=\""; 666377e76abSEd Tanous xml += method.first; 667377e76abSEd Tanous xml += "\">"; 668377e76abSEd Tanous for (auto& arg : method.second->get_args()) { 669377e76abSEd Tanous xml += "<arg name=\""; 670377e76abSEd Tanous xml += arg.name; 671377e76abSEd Tanous xml += "\" type=\""; 672377e76abSEd Tanous xml += arg.type; 673377e76abSEd Tanous xml += "\" direction=\""; 674377e76abSEd Tanous xml += arg.direction; 675377e76abSEd Tanous xml += "\"/>"; 676377e76abSEd Tanous } 677377e76abSEd Tanous xml += "</method>"; 678377e76abSEd Tanous } 679377e76abSEd Tanous 680377e76abSEd Tanous for (auto& signal : interface_pair.second->get_signals()) { 681377e76abSEd Tanous xml += "<signal name=\""; 682377e76abSEd Tanous xml += signal.first; 683377e76abSEd Tanous xml += "\">"; 684377e76abSEd Tanous for (auto& arg : signal.second->get_args()) { 685377e76abSEd Tanous xml += "<arg name=\""; 686377e76abSEd Tanous xml += arg.name; 687377e76abSEd Tanous xml += "\" type=\""; 688377e76abSEd Tanous xml += arg.type; 689377e76abSEd Tanous xml += "\"/>"; 690377e76abSEd Tanous } 691377e76abSEd Tanous 692377e76abSEd Tanous xml += "</signal>"; 693377e76abSEd Tanous } 694377e76abSEd Tanous 695377e76abSEd Tanous for (auto& property : interface_pair.second->get_properties_map()) { 696377e76abSEd Tanous xml += "<property name=\""; 697377e76abSEd Tanous xml += property.first; 698377e76abSEd Tanous xml += "\" type=\""; 699377e76abSEd Tanous 700377e76abSEd Tanous std::string type = std::string(boost::apply_visitor( 701377e76abSEd Tanous [&](auto val) { 702377e76abSEd Tanous static const auto constexpr sig = 703377e76abSEd Tanous element_signature<decltype(val)>::code; 704377e76abSEd Tanous return &sig[0]; 705377e76abSEd Tanous }, 706377e76abSEd Tanous property.second)); 707377e76abSEd Tanous xml += type; 708*e3b0bf5bSEd Tanous xml += "\" access=\""; 709377e76abSEd Tanous // TODO direction can be readwrite, read, or write. Need to 710377e76abSEd Tanous // make this configurable 711377e76abSEd Tanous xml += "readwrite"; 712377e76abSEd Tanous xml += "\"/>"; 713377e76abSEd Tanous } 714377e76abSEd Tanous xml += "</interface>"; 715377e76abSEd Tanous } 716377e76abSEd Tanous } else if (boost::starts_with(object_name, newpath)) { 717377e76abSEd Tanous auto slash_index = object_name.find("/", newpath.size() + 1); 718377e76abSEd Tanous auto subnode = object_name.substr(newpath.size() + 1, 719377e76abSEd Tanous slash_index - newpath.size() - 1); 720377e76abSEd Tanous if (node_names.find(subnode) == node_names.end()) { 721377e76abSEd Tanous node_names.insert(subnode); 722377e76abSEd Tanous xml += "<node name=\""; 723377e76abSEd Tanous xml += subnode; 724377e76abSEd Tanous xml += "\">"; 725377e76abSEd Tanous xml += "</node>"; 726377e76abSEd Tanous } 727377e76abSEd Tanous } 728377e76abSEd Tanous } 729377e76abSEd Tanous xml += "</node>"; 730377e76abSEd Tanous return xml; 731377e76abSEd Tanous } 732377e76abSEd Tanous 733377e76abSEd Tanous private: 734377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 735377e76abSEd Tanous std::vector<std::shared_ptr<DbusObject>> objects; 736377e76abSEd Tanous std::unique_ptr<dbus::filter> introspect_filter; 737377e76abSEd Tanous std::unique_ptr<dbus::filter> object_manager_filter; 738377e76abSEd Tanous std::unique_ptr<dbus::filter> method_filter; 739377e76abSEd Tanous }; 740377e76abSEd Tanous } 741