1b2432980SVernon Mauery #ifndef DBUS_PROPERTIES_HPP 2b2432980SVernon Mauery #define DBUS_PROPERTIES_HPP 3b2432980SVernon Mauery 4377e76abSEd Tanous #include <dbus/connection.hpp> 5377e76abSEd Tanous #include <dbus/filter.hpp> 6377e76abSEd Tanous #include <dbus/match.hpp> 7377e76abSEd Tanous #include <functional> 8377e76abSEd Tanous #include <tuple> 9377e76abSEd Tanous #include <type_traits> 10377e76abSEd Tanous #include <boost/algorithm/string/predicate.hpp> 11377e76abSEd Tanous #include <boost/container/flat_map.hpp> 12377e76abSEd Tanous #include <boost/container/flat_set.hpp> 13377e76abSEd Tanous 14377e76abSEd Tanous namespace dbus { 15377e76abSEd Tanous struct DbusArgument { 16377e76abSEd Tanous DbusArgument(const std::string& direction, const std::string& name, 17377e76abSEd Tanous const std::string& type) 18377e76abSEd Tanous : direction(direction), name(name), type(type){}; 19377e76abSEd Tanous std::string direction; 20377e76abSEd Tanous std::string name; 21377e76abSEd Tanous std::string type; 22377e76abSEd Tanous }; 23377e76abSEd Tanous 24377e76abSEd Tanous class DbusMethod { 25377e76abSEd Tanous public: 26e3b0bf5bSEd Tanous DbusMethod(const std::string& name, std::shared_ptr<dbus::connection>& conn) 27377e76abSEd Tanous : name(name), conn(conn){}; 28377e76abSEd Tanous virtual void call(dbus::message& m){}; 29377e76abSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; }; 30377e76abSEd Tanous std::string name; 31377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 32377e76abSEd Tanous }; 33377e76abSEd Tanous 34377e76abSEd Tanous enum class UpdateType { VALUE_CHANGE_ONLY, FORCE }; 35377e76abSEd Tanous 36377e76abSEd Tanous // Base case for when I == the size of the tuple args. Does nothing, as we 37377e76abSEd Tanous // should be done 387a440136SEd Tanous template <std::size_t TupleIndex = 0, typename... Tp> 397a440136SEd Tanous inline typename std::enable_if<TupleIndex == sizeof...(Tp), void>::type 407a440136SEd Tanous arg_types(bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v, 41e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) {} 42377e76abSEd Tanous 43377e76abSEd Tanous // Case for when I < the size of tuple args. Unpacks the tuple type into the 44377e76abSEd Tanous // dbusargument object and names it appropriately. 457a440136SEd Tanous template <std::size_t TupleIndex = 0, typename... Tp> 46377e76abSEd Tanous inline typename std::enable_if < 477a440136SEd Tanous TupleIndex<sizeof...(Tp), void>::type arg_types( 48e3b0bf5bSEd Tanous bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v, 49e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) { 507a440136SEd Tanous typedef typename std::tuple_element<TupleIndex, std::tuple<Tp...>>::type 517a440136SEd Tanous element_type; 52377e76abSEd Tanous auto constexpr sig = element_signature<element_type>::code; 53e3b0bf5bSEd Tanous std::string name; 54e3b0bf5bSEd Tanous std::string direction; 557a440136SEd Tanous if (arg_names == nullptr || arg_names->size() <= TupleIndex) { 56377e76abSEd Tanous if (in) { 577a440136SEd Tanous name = "arg_" + std::to_string(TupleIndex); 58377e76abSEd Tanous } else { 597a440136SEd Tanous name = "out_" + std::to_string(TupleIndex); 60377e76abSEd Tanous } 61e3b0bf5bSEd Tanous } else { 627a440136SEd Tanous name = (*arg_names)[TupleIndex]; 63e3b0bf5bSEd Tanous } 64e3b0bf5bSEd Tanous v.emplace_back(in ? "in" : "out", name, &sig[0]); 65e3b0bf5bSEd Tanous 667a440136SEd Tanous arg_types<TupleIndex + 1, Tp...>(in, t, v, arg_names); 67e3b0bf5bSEd Tanous } 68e3b0bf5bSEd Tanous 69e3b0bf5bSEd Tanous // Special case for handling raw arguments returned from handlers. Because they 70e3b0bf5bSEd Tanous // don't get stored in a tuple, special handling is neccesary 71e3b0bf5bSEd Tanous template <typename Element> 72e3b0bf5bSEd Tanous void arg_types(bool in, Element& t, std::vector<DbusArgument>& v, 73e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) { 74e3b0bf5bSEd Tanous auto constexpr sig = element_signature<Element>::code; 75e3b0bf5bSEd Tanous std::string name; 76e3b0bf5bSEd Tanous if (arg_names == nullptr || arg_names->size() < 1) { 77e3b0bf5bSEd Tanous name.assign("arg_0"); 78e3b0bf5bSEd Tanous } else { 79e3b0bf5bSEd Tanous name = (*arg_names)[0]; 80e3b0bf5bSEd Tanous } 81e3b0bf5bSEd Tanous 82e3b0bf5bSEd Tanous v.emplace_back(in ? "in" : "out", name, &sig[0]); 83377e76abSEd Tanous } 84377e76abSEd Tanous 85377e76abSEd Tanous template <typename Handler> 86377e76abSEd Tanous class LambdaDbusMethod : public DbusMethod { 87377e76abSEd Tanous public: 88377e76abSEd Tanous typedef function_traits<Handler> traits; 89377e76abSEd Tanous typedef typename traits::decayed_arg_types InputTupleType; 90e3b0bf5bSEd Tanous typedef typename traits::result_type ResultType; 91e3b0bf5bSEd Tanous LambdaDbusMethod(const std::string name, 92e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn, Handler h) 93e3b0bf5bSEd Tanous : DbusMethod(name, conn), h(std::move(h)) { 94e3b0bf5bSEd Tanous InputTupleType t; 95e3b0bf5bSEd Tanous arg_types(true, t, args); 96e3b0bf5bSEd Tanous 97e3b0bf5bSEd Tanous ResultType o; 98e3b0bf5bSEd Tanous arg_types(false, o, args); 99e3b0bf5bSEd Tanous } 100e3b0bf5bSEd Tanous 101e3b0bf5bSEd Tanous LambdaDbusMethod(const std::string& name, 102e3b0bf5bSEd Tanous const std::vector<std::string>& input_arg_names, 103e3b0bf5bSEd Tanous const std::vector<std::string>& output_arg_names, 104e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn, Handler h) 105e3b0bf5bSEd Tanous : DbusMethod(name, conn), h(std::move(h)) { 106e3b0bf5bSEd Tanous InputTupleType t; 107e3b0bf5bSEd Tanous arg_types(true, t, args, &input_arg_names); 108e3b0bf5bSEd Tanous 109e3b0bf5bSEd Tanous ResultType o; 110e3b0bf5bSEd Tanous arg_types(false, o, args, &output_arg_names); 111e3b0bf5bSEd Tanous } 112377e76abSEd Tanous void call(dbus::message& m) override { 113377e76abSEd Tanous InputTupleType input_args; 114a04118e8SEd Tanous if (unpack_into_tuple(input_args, m) == false) { 115a04118e8SEd Tanous auto err = dbus::message::new_error(m, DBUS_ERROR_INVALID_ARGS, ""); 116a04118e8SEd Tanous conn->send(err, std::chrono::seconds(0)); 117a04118e8SEd Tanous return; 118a04118e8SEd Tanous } 119a04118e8SEd Tanous try { 120e62be329SEd Tanous ResultType r = apply(h, input_args); 121377e76abSEd Tanous auto ret = dbus::message::new_return(m); 122a04118e8SEd Tanous if (pack_tuple_into_msg(r, ret) == false) { 123a04118e8SEd Tanous auto err = dbus::message::new_error( 124a04118e8SEd Tanous m, DBUS_ERROR_FAILED, "Handler had issue when packing response"); 125a04118e8SEd Tanous conn->send(err, std::chrono::seconds(0)); 126a04118e8SEd Tanous return; 127a04118e8SEd Tanous } 128377e76abSEd Tanous conn->send(ret, std::chrono::seconds(0)); 129a04118e8SEd Tanous } catch (...) { 130a04118e8SEd Tanous auto err = dbus::message::new_error( 131a04118e8SEd Tanous m, DBUS_ERROR_FAILED, 132a04118e8SEd Tanous "Handler threw exception while handling request."); 133a04118e8SEd Tanous conn->send(err, std::chrono::seconds(0)); 134a04118e8SEd Tanous return; 135a04118e8SEd Tanous } 136377e76abSEd Tanous }; 137377e76abSEd Tanous 138e3b0bf5bSEd Tanous std::vector<DbusArgument> get_args() override { return args; }; 139377e76abSEd Tanous Handler h; 140e3b0bf5bSEd Tanous std::vector<DbusArgument> args; 141377e76abSEd Tanous }; 142377e76abSEd Tanous 143377e76abSEd Tanous class DbusSignal { 144377e76abSEd Tanous public: 145e3b0bf5bSEd Tanous DbusSignal(){}; 146e3b0bf5bSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; } 147e3b0bf5bSEd Tanous }; 148377e76abSEd Tanous 149e3b0bf5bSEd Tanous template <typename... Args> 150e3b0bf5bSEd Tanous class DbusTemplateSignal : public DbusSignal { 151e3b0bf5bSEd Tanous public: 152e3b0bf5bSEd Tanous DbusTemplateSignal(const std::string& name, const std::string& object_name, 153e3b0bf5bSEd Tanous const std::string& interface_name, 154e3b0bf5bSEd Tanous const std::vector<std::string>& names, 155e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn) 156e3b0bf5bSEd Tanous : DbusSignal(), 157e3b0bf5bSEd Tanous name(name), 158e3b0bf5bSEd Tanous object_name(object_name), 159e3b0bf5bSEd Tanous interface_name(interface_name), 160e3b0bf5bSEd Tanous conn(conn) { 161e3b0bf5bSEd Tanous std::tuple<Args...> tu; 162e3b0bf5bSEd Tanous arg_types(true, tu, args, &names); 163e3b0bf5bSEd Tanous }; 164e3b0bf5bSEd Tanous 165a8b4eac4SEd Tanous void send(const Args&...) { 166e3b0bf5bSEd Tanous dbus::endpoint endpoint("", object_name, interface_name); 167e3b0bf5bSEd Tanous auto m = dbus::message::new_signal(endpoint, name); 168e3b0bf5bSEd Tanous conn->send(m, std::chrono::seconds(0)); 169e3b0bf5bSEd Tanous } 170e3b0bf5bSEd Tanous 171e3b0bf5bSEd Tanous std::vector<DbusArgument> get_args() override { return args; }; 172e3b0bf5bSEd Tanous 173e3b0bf5bSEd Tanous std::vector<DbusArgument> args; 174e3b0bf5bSEd Tanous std::string name; 175e3b0bf5bSEd Tanous std::string object_name; 176e3b0bf5bSEd Tanous std::string interface_name; 177e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection> conn; 178377e76abSEd Tanous }; 179377e76abSEd Tanous 180377e76abSEd Tanous class DbusInterface { 181377e76abSEd Tanous public: 182377e76abSEd Tanous DbusInterface(std::string interface_name, 183377e76abSEd Tanous std::shared_ptr<dbus::connection>& conn) 184377e76abSEd Tanous : interface_name(std::move(interface_name)), conn(conn) {} 185377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>> 186377e76abSEd Tanous get_signals() { 187377e76abSEd Tanous return dbus_signals; 188377e76abSEd Tanous }; 189377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>> 190377e76abSEd Tanous get_methods() { 191377e76abSEd Tanous return dbus_methods; 192377e76abSEd Tanous }; 193377e76abSEd Tanous virtual std::string get_interface_name() { return interface_name; }; 194377e76abSEd Tanous virtual const boost::container::flat_map<std::string, dbus_variant> 195377e76abSEd Tanous get_properties_map() { 196377e76abSEd Tanous return properties_map; 197377e76abSEd Tanous }; 198377e76abSEd Tanous 199f95fe4ccSVernon Mauery dbus_variant get_property(const std::string& property_name) { 200f95fe4ccSVernon Mauery auto property = properties_map.find(property_name); 201f95fe4ccSVernon Mauery if (property == properties_map.end()) { 202f95fe4ccSVernon Mauery // TODO(ed) property not found error 203f95fe4ccSVernon Mauery throw std::runtime_error("property not found"); 204f95fe4ccSVernon Mauery } else { 205f95fe4ccSVernon Mauery return property->second; 206f95fe4ccSVernon Mauery } 207f95fe4ccSVernon Mauery } 208f95fe4ccSVernon Mauery 209377e76abSEd Tanous template <typename VALUE_TYPE> 210377e76abSEd Tanous void set_property(const std::string& property_name, const VALUE_TYPE value, 211377e76abSEd Tanous UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) { 212377e76abSEd Tanous // Construct a change vector of length 1. if this set_properties is ever 213377e76abSEd Tanous // templated for any type, we could probably swap with with a 214377e76abSEd Tanous // std::array<pair, 1> 215377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 216377e76abSEd Tanous v.emplace_back(property_name, value); 217377e76abSEd Tanous set_properties(v, update_mode); 218377e76abSEd Tanous } 219377e76abSEd Tanous 220377e76abSEd Tanous void set_properties( 221377e76abSEd Tanous const std::vector<std::pair<std::string, dbus_variant>>& v, 222377e76abSEd Tanous const UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) { 223377e76abSEd Tanous // TODO(ed) generalize this interface for all "map like" types, basically 224377e76abSEd Tanous // anything that will return a const iterator of std::pair<string, 225377e76abSEd Tanous // variant> 226377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> updates; 227377e76abSEd Tanous updates.reserve(v.size()); 228377e76abSEd Tanous 229377e76abSEd Tanous if (update_mode == UpdateType::FORCE) { 230377e76abSEd Tanous updates = v; 231377e76abSEd Tanous } else { 232377e76abSEd Tanous for (auto& property : v) { 233377e76abSEd Tanous auto property_map_it = properties_map.find(property.first); 234377e76abSEd Tanous if (property_map_it != properties_map.end()) { 235377e76abSEd Tanous // Property exists in map 236377e76abSEd Tanous if (property_map_it->second != property.second) { 237377e76abSEd Tanous properties_map[property.first] = property.second; 238377e76abSEd Tanous // if value has changed since last set 239377e76abSEd Tanous updates.emplace_back(*property_map_it); 240377e76abSEd Tanous } 241377e76abSEd Tanous } else { 242377e76abSEd Tanous // property doesn't exist, must be new 243377e76abSEd Tanous properties_map[property.first] = property.second; 244377e76abSEd Tanous updates.emplace_back(property.first, property.second); 245377e76abSEd Tanous } 246377e76abSEd Tanous } 247377e76abSEd Tanous } 248377e76abSEd Tanous 2492277cd81SFeist, James dbus::endpoint endpoint("org.freedesktop.DBus", object_name, 250377e76abSEd Tanous "org.freedesktop.DBus.Properties"); 251377e76abSEd Tanous 252377e76abSEd Tanous auto m = dbus::message::new_signal(endpoint, "PropertiesChanged"); 253377e76abSEd Tanous 254377e76abSEd Tanous static const std::vector<std::string> empty; 255377e76abSEd Tanous m.pack(get_interface_name(), updates, empty); 256377e76abSEd Tanous // TODO(ed) make sure this doesn't block 257377e76abSEd Tanous conn->async_send( 258377e76abSEd Tanous m, [](const boost::system::error_code ec, dbus::message r) {}); 259377e76abSEd Tanous } 260377e76abSEd Tanous 261e3b0bf5bSEd Tanous void register_method(std::shared_ptr<DbusMethod> method) { 262377e76abSEd Tanous dbus_methods.emplace(method->name, method); 263377e76abSEd Tanous } 264377e76abSEd Tanous 265377e76abSEd Tanous template <typename Handler> 266e3b0bf5bSEd Tanous void register_method(const std::string& name, Handler method) { 267377e76abSEd Tanous dbus_methods.emplace(name, 268377e76abSEd Tanous new LambdaDbusMethod<Handler>(name, conn, method)); 269377e76abSEd Tanous } 270377e76abSEd Tanous 271e3b0bf5bSEd Tanous template <typename Handler> 272e3b0bf5bSEd Tanous void register_method(const std::string& name, 273e3b0bf5bSEd Tanous const std::vector<std::string>& input_arg_names, 274e3b0bf5bSEd Tanous const std::vector<std::string>& output_arg_names, 275e3b0bf5bSEd Tanous Handler method) { 276e3b0bf5bSEd Tanous dbus_methods.emplace( 277e3b0bf5bSEd Tanous name, new LambdaDbusMethod<Handler>(name, input_arg_names, 278e3b0bf5bSEd Tanous output_arg_names, conn, method)); 279e3b0bf5bSEd Tanous } 280e3b0bf5bSEd Tanous 281e3b0bf5bSEd Tanous template <typename... Args> 282a8b4eac4SEd Tanous std::shared_ptr<DbusTemplateSignal<Args...>> register_signal( 283e3b0bf5bSEd Tanous const std::string& name, const std::vector<std::string> arg_names) { 284a8b4eac4SEd Tanous auto sig = std::make_shared<DbusTemplateSignal<Args...>>( 285a8b4eac4SEd Tanous name, object_name, interface_name, arg_names, conn); 286a8b4eac4SEd Tanous dbus_signals.emplace(name, sig); 287a8b4eac4SEd Tanous return sig; 288e3b0bf5bSEd Tanous } 289e3b0bf5bSEd Tanous 290377e76abSEd Tanous void call(dbus::message& m) { 291377e76abSEd Tanous std::string method_name = m.get_member(); 292377e76abSEd Tanous auto method = dbus_methods.find(method_name); 293377e76abSEd Tanous if (method != dbus_methods.end()) { 294377e76abSEd Tanous method->second->call(m); 295377e76abSEd Tanous } // TODO(ed) send something when method doesn't exist? 296377e76abSEd Tanous } 297377e76abSEd Tanous 298377e76abSEd Tanous std::string object_name; 299377e76abSEd Tanous std::string interface_name; 300377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>> 301377e76abSEd Tanous dbus_methods; 302377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>> 303377e76abSEd Tanous dbus_signals; 304377e76abSEd Tanous boost::container::flat_map<std::string, dbus_variant> properties_map; 305377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 306377e76abSEd Tanous }; 307377e76abSEd Tanous 308377e76abSEd Tanous class DbusObject { 309377e76abSEd Tanous public: 310377e76abSEd Tanous DbusObject(std::shared_ptr<dbus::connection> conn, std::string object_name) 311377e76abSEd Tanous : object_name(std::move(object_name)), conn(conn) { 312377e76abSEd Tanous properties_iface = add_interface("org.freedesktop.DBus.Properties"); 313377e76abSEd Tanous 314377e76abSEd Tanous properties_iface->register_method( 315e3b0bf5bSEd Tanous "Get", {"interface_name", "properties_name"}, {"value"}, 316e3b0bf5bSEd Tanous [&](const std::string& interface_name, 317377e76abSEd Tanous const std::string& property_name) { 318377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 319377e76abSEd Tanous if (interface_it == interfaces.end()) { 320377e76abSEd Tanous // Interface not found error 321377e76abSEd Tanous throw std::runtime_error("interface not found"); 322377e76abSEd Tanous } else { 323377e76abSEd Tanous auto& properties_map = interface_it->second->get_properties_map(); 324377e76abSEd Tanous auto property = properties_map.find(property_name); 325377e76abSEd Tanous if (property == properties_map.end()) { 326377e76abSEd Tanous // TODO(ed) property not found error 327377e76abSEd Tanous throw std::runtime_error("property not found"); 328377e76abSEd Tanous } else { 329377e76abSEd Tanous return std::tuple<dbus_variant>(property->second); 330377e76abSEd Tanous } 331377e76abSEd Tanous } 332377e76abSEd Tanous }); 333377e76abSEd Tanous 334e3b0bf5bSEd Tanous properties_iface->register_method( 335e3b0bf5bSEd Tanous "GetAll", {"interface_name"}, {"properties"}, 336e3b0bf5bSEd Tanous [&](const std::string& interface_name) { 337377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 338377e76abSEd Tanous if (interface_it == interfaces.end()) { 339377e76abSEd Tanous // Interface not found error 340377e76abSEd Tanous throw std::runtime_error("interface not found"); 341377e76abSEd Tanous } else { 342377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 34393f7fc83SEd Tanous for (auto& element : interface_it->second->get_properties_map()) { 344377e76abSEd Tanous v.emplace_back(element.first, element.second); 345377e76abSEd Tanous } 346e3b0bf5bSEd Tanous return std::tuple< 347e3b0bf5bSEd Tanous std::vector<std::pair<std::string, dbus_variant>>>(v); 348377e76abSEd Tanous } 349377e76abSEd Tanous }); 350377e76abSEd Tanous properties_iface->register_method( 351e3b0bf5bSEd Tanous "Set", {"interface_name", "properties_name", "value"}, {}, 352377e76abSEd Tanous [&](const std::string& interface_name, const std::string& property_name, 353377e76abSEd Tanous const dbus_variant& value) { 354377e76abSEd Tanous auto interface_it = interfaces.find(interface_name); 355377e76abSEd Tanous if (interface_it == interfaces.end()) { 356377e76abSEd Tanous // Interface not found error 357377e76abSEd Tanous throw std::runtime_error("interface not found"); 358377e76abSEd Tanous } else { 359377e76abSEd Tanous // Todo, the set propery (signular) interface should support 360a04118e8SEd Tanous // handing a variant. The below is expensive 361377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v; 362377e76abSEd Tanous v.emplace_back(property_name, value); 363377e76abSEd Tanous interface_it->second->set_properties(v); 364377e76abSEd Tanous return std::tuple<>(); 365377e76abSEd Tanous } 366377e76abSEd Tanous }); 367e3b0bf5bSEd Tanous 368e3b0bf5bSEd Tanous properties_iface->register_signal< 369e3b0bf5bSEd Tanous std::string, std::vector<std::pair<std::string, dbus_variant>>, 370e3b0bf5bSEd Tanous std::vector<std::string>>( 371e3b0bf5bSEd Tanous "PropertiesChanged", 372e3b0bf5bSEd Tanous {"interface_name", "changed_properties", "invalidated_properties"}); 373377e76abSEd Tanous } 374377e76abSEd Tanous 375377e76abSEd Tanous std::shared_ptr<DbusInterface> add_interface(const std::string& name) { 376377e76abSEd Tanous auto x = std::make_shared<DbusInterface>(name, conn); 377377e76abSEd Tanous register_interface(x); 378377e76abSEd Tanous return x; 379377e76abSEd Tanous } 380377e76abSEd Tanous 381377e76abSEd Tanous void register_interface(std::shared_ptr<DbusInterface>& interface) { 382377e76abSEd Tanous interfaces[interface->get_interface_name()] = interface; 383377e76abSEd Tanous interface->object_name = object_name; 384377e76abSEd Tanous const static dbus::endpoint endpoint("", object_name, 385377e76abSEd Tanous "org.freedesktop.DBus.ObjectManager"); 386377e76abSEd Tanous 387e3b0bf5bSEd Tanous auto m = message::new_signal(endpoint, "InterfacesAdded"); 388377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus_variant>> properties_dict; 389377e76abSEd Tanous std::vector<std::pair<std::string, properties_dict>> sig; 390377e76abSEd Tanous sig.emplace_back(interface->get_interface_name(), properties_dict()); 391377e76abSEd Tanous auto& prop_dict = sig.back().second; 392377e76abSEd Tanous for (auto& property : interface->get_properties_map()) { 393377e76abSEd Tanous prop_dict.emplace_back(property); 394377e76abSEd Tanous } 395377e76abSEd Tanous 396377e76abSEd Tanous m.pack(object_name, sig); 397f76e5e0bSFeist, James 398f76e5e0bSFeist, James conn->send(m, std::chrono::seconds(0)); 399377e76abSEd Tanous } 400377e76abSEd Tanous 401377e76abSEd Tanous auto get_interfaces() { return interfaces; } 402377e76abSEd Tanous 403377e76abSEd Tanous void call(dbus::message& m) { 404377e76abSEd Tanous auto interface = interfaces.find(m.get_interface()); 405377e76abSEd Tanous if (interface != interfaces.end()) { 406377e76abSEd Tanous interface->second->call(m); 407377e76abSEd Tanous } // TODO(ed) send something when interface doesn't exist? 408377e76abSEd Tanous } 409377e76abSEd Tanous 410377e76abSEd Tanous std::string object_name; 411377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 412377e76abSEd Tanous 413377e76abSEd Tanous // dbus::filter properties_filter; 414377e76abSEd Tanous std::shared_ptr<DbusInterface> properties_iface; 415377e76abSEd Tanous 416e3b0bf5bSEd Tanous std::shared_ptr<DbusInterface> object_manager_iface; 417e3b0bf5bSEd Tanous 418377e76abSEd Tanous std::function<void(boost::system::error_code, message)> callback; 419377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusInterface>> 420377e76abSEd Tanous interfaces; 421377e76abSEd Tanous }; 422377e76abSEd Tanous 423377e76abSEd Tanous class DbusObjectServer { 424377e76abSEd Tanous public: 425377e76abSEd Tanous DbusObjectServer(std::shared_ptr<dbus::connection>& conn) : conn(conn) { 426377e76abSEd Tanous introspect_filter = 427377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) { 428377e76abSEd Tanous if (m.get_type() != "method_call") { 429377e76abSEd Tanous return false; 430377e76abSEd Tanous } 431377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.Introspectable") { 432377e76abSEd Tanous return false; 433377e76abSEd Tanous } 434377e76abSEd Tanous if (m.get_member() != "Introspect") { 435377e76abSEd Tanous return false; 436377e76abSEd Tanous }; 437377e76abSEd Tanous return true; 438377e76abSEd Tanous }); 439377e76abSEd Tanous 440377e76abSEd Tanous introspect_filter->async_dispatch( 441377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 442377e76abSEd Tanous on_introspect(ec, m); 443377e76abSEd Tanous }); 444377e76abSEd Tanous 445377e76abSEd Tanous object_manager_filter = 446377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) { 447377e76abSEd Tanous 448377e76abSEd Tanous if (m.get_type() != "method_call") { 449377e76abSEd Tanous return false; 450377e76abSEd Tanous } 451377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.ObjectManager") { 452377e76abSEd Tanous return false; 453377e76abSEd Tanous } 454377e76abSEd Tanous if (m.get_member() != "GetManagedObjects") { 455377e76abSEd Tanous return false; 456377e76abSEd Tanous }; 457377e76abSEd Tanous return true; 458377e76abSEd Tanous }); 459377e76abSEd Tanous 460377e76abSEd Tanous object_manager_filter->async_dispatch( 461377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 462377e76abSEd Tanous on_get_managed_objects(ec, m); 463377e76abSEd Tanous }); 464377e76abSEd Tanous 465377e76abSEd Tanous method_filter = std::make_unique<dbus::filter>(conn, [](dbus::message m) { 466377e76abSEd Tanous 467377e76abSEd Tanous if (m.get_type() != "method_call") { 468377e76abSEd Tanous return false; 469377e76abSEd Tanous } 470377e76abSEd Tanous return true; 471377e76abSEd Tanous }); 472377e76abSEd Tanous 473377e76abSEd Tanous method_filter->async_dispatch( 474377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 475377e76abSEd Tanous on_method_call(ec, m); 476377e76abSEd Tanous }); 477377e76abSEd Tanous }; 478377e76abSEd Tanous 479377e76abSEd Tanous std::shared_ptr<dbus::connection>& get_connection() { return conn; } 480377e76abSEd Tanous void on_introspect(const boost::system::error_code ec, dbus::message m) { 481377e76abSEd Tanous auto xml = get_xml_for_path(m.get_path()); 482377e76abSEd Tanous auto ret = dbus::message::new_return(m); 483377e76abSEd Tanous ret.pack(xml); 484377e76abSEd Tanous conn->async_send( 485377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {}); 486377e76abSEd Tanous 487377e76abSEd Tanous introspect_filter->async_dispatch( 488377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 489377e76abSEd Tanous on_introspect(ec, m); 490377e76abSEd Tanous }); 491377e76abSEd Tanous } 492377e76abSEd Tanous 493377e76abSEd Tanous void on_method_call(const boost::system::error_code ec, dbus::message m) { 494377e76abSEd Tanous if (ec) { 495377e76abSEd Tanous std::cerr << "on_method_call error: " << ec << "\n"; 496377e76abSEd Tanous } else { 497377e76abSEd Tanous auto path = m.get_path(); 498377e76abSEd Tanous // TODO(ed) objects should be a map 499377e76abSEd Tanous for (auto& object : objects) { 500377e76abSEd Tanous if (object->object_name == path) { 501377e76abSEd Tanous object->call(m); 502377e76abSEd Tanous break; 503377e76abSEd Tanous } 504377e76abSEd Tanous } 505377e76abSEd Tanous } 506377e76abSEd Tanous method_filter->async_dispatch( 507377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 508377e76abSEd Tanous on_method_call(ec, m); 509377e76abSEd Tanous }); 510377e76abSEd Tanous } 511377e76abSEd Tanous 512377e76abSEd Tanous void on_get_managed_objects(const boost::system::error_code ec, 513377e76abSEd Tanous dbus::message m) { 514377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus::dbus_variant>> 515377e76abSEd Tanous properties_dict; 516377e76abSEd Tanous 517377e76abSEd Tanous typedef std::vector<std::pair<std::string, properties_dict>> 518377e76abSEd Tanous interfaces_dict; 519377e76abSEd Tanous 52070d534bfSFeist, James std::vector<std::pair<object_path, interfaces_dict>> dict; 521377e76abSEd Tanous 522377e76abSEd Tanous for (auto& object : objects) { 523377e76abSEd Tanous interfaces_dict i; 524377e76abSEd Tanous for (auto& interface : object->get_interfaces()) { 525377e76abSEd Tanous properties_dict p; 526377e76abSEd Tanous 527377e76abSEd Tanous for (auto& property : interface.second->get_properties_map()) { 528377e76abSEd Tanous p.push_back(property); 529377e76abSEd Tanous } 530377e76abSEd Tanous i.emplace_back(interface.second->get_interface_name(), std::move(p)); 531377e76abSEd Tanous } 53270d534bfSFeist, James dict.emplace_back(object_path{object->object_name}, std::move(i)); 533377e76abSEd Tanous } 534377e76abSEd Tanous auto ret = dbus::message::new_return(m); 535377e76abSEd Tanous ret.pack(dict); 536377e76abSEd Tanous conn->async_send( 537377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {}); 538377e76abSEd Tanous 539377e76abSEd Tanous object_manager_filter->async_dispatch( 540377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) { 541377e76abSEd Tanous on_get_managed_objects(ec, m); 542377e76abSEd Tanous }); 543377e76abSEd Tanous } 544377e76abSEd Tanous 545377e76abSEd Tanous std::shared_ptr<DbusObject> add_object(const std::string& name) { 546377e76abSEd Tanous auto x = std::make_shared<DbusObject>(conn, name); 547377e76abSEd Tanous register_object(x); 548377e76abSEd Tanous return x; 549377e76abSEd Tanous } 550377e76abSEd Tanous 551377e76abSEd Tanous void register_object(std::shared_ptr<DbusObject> object) { 552377e76abSEd Tanous objects.emplace_back(object); 553377e76abSEd Tanous } 554377e76abSEd Tanous 555*ddc0c513SJames Feist void remove_object(std::shared_ptr<DbusObject> object) { 556*ddc0c513SJames Feist std::remove(objects.begin(), objects.end(), object); 557*ddc0c513SJames Feist } 558*ddc0c513SJames Feist 559377e76abSEd Tanous std::string get_xml_for_path(const std::string& path) { 560377e76abSEd Tanous std::string newpath(path); 561377e76abSEd Tanous 562377e76abSEd Tanous if (newpath == "/") { 563377e76abSEd Tanous newpath.assign(""); 564377e76abSEd Tanous } 565377e76abSEd Tanous 566377e76abSEd Tanous boost::container::flat_set<std::string> node_names; 567377e76abSEd Tanous std::string xml( 568377e76abSEd Tanous "<!DOCTYPE node PUBLIC " 569377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " 570377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/" 571377e76abSEd Tanous "introspect.dtd\">\n<node>"); 572377e76abSEd Tanous for (auto& object : objects) { 573377e76abSEd Tanous std::string& object_name = object->object_name; 574377e76abSEd Tanous // exact match 575377e76abSEd Tanous if (object->object_name == newpath) { 576377e76abSEd Tanous xml += 577377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.Peer\">" 578377e76abSEd Tanous " <method name=\"Ping\"/>" 579377e76abSEd Tanous " <method name=\"GetMachineId\">" 580377e76abSEd Tanous " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>" 581377e76abSEd Tanous " </method>" 582377e76abSEd Tanous " </interface>"; 583377e76abSEd Tanous 584377e76abSEd Tanous xml += 585377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.ObjectManager\">" 586377e76abSEd Tanous " <method name=\"GetManagedObjects\">" 587377e76abSEd Tanous " <arg type=\"a{oa{sa{sv}}}\" " 588377e76abSEd Tanous " name=\"object_paths_interfaces_and_properties\" " 589377e76abSEd Tanous " direction=\"out\"/>" 590377e76abSEd Tanous " </method>" 591377e76abSEd Tanous " <signal name=\"InterfacesAdded\">" 592377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>" 593377e76abSEd Tanous " <arg type=\"a{sa{sv}}\" " 594377e76abSEd Tanous "name=\"interfaces_and_properties\"/>" 595377e76abSEd Tanous " </signal>" 596377e76abSEd Tanous " <signal name=\"InterfacesRemoved\">" 597377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>" 598377e76abSEd Tanous " <arg type=\"as\" name=\"interfaces\"/>" 599377e76abSEd Tanous " </signal>" 600377e76abSEd Tanous " </interface>"; 601377e76abSEd Tanous 602e3b0bf5bSEd Tanous xml += 603e3b0bf5bSEd Tanous "<interface name=\"org.freedesktop.DBus.Introspectable\">" 604e3b0bf5bSEd Tanous " <method name=\"Introspect\">" 605e3b0bf5bSEd Tanous " <arg type=\"s\" name=\"xml_data\" direction=\"out\"/>" 606e3b0bf5bSEd Tanous " </method>" 607e3b0bf5bSEd Tanous "</interface>"; 608e3b0bf5bSEd Tanous 609377e76abSEd Tanous for (auto& interface_pair : object->interfaces) { 610377e76abSEd Tanous xml += "<interface name=\""; 611377e76abSEd Tanous xml += interface_pair.first; 612377e76abSEd Tanous xml += "\">"; 613377e76abSEd Tanous for (auto& method : interface_pair.second->get_methods()) { 614377e76abSEd Tanous xml += "<method name=\""; 615377e76abSEd Tanous xml += method.first; 616377e76abSEd Tanous xml += "\">"; 617377e76abSEd Tanous for (auto& arg : method.second->get_args()) { 618377e76abSEd Tanous xml += "<arg name=\""; 619377e76abSEd Tanous xml += arg.name; 620377e76abSEd Tanous xml += "\" type=\""; 621377e76abSEd Tanous xml += arg.type; 622377e76abSEd Tanous xml += "\" direction=\""; 623377e76abSEd Tanous xml += arg.direction; 624377e76abSEd Tanous xml += "\"/>"; 625377e76abSEd Tanous } 626377e76abSEd Tanous xml += "</method>"; 627377e76abSEd Tanous } 628377e76abSEd Tanous 629377e76abSEd Tanous for (auto& signal : interface_pair.second->get_signals()) { 630377e76abSEd Tanous xml += "<signal name=\""; 631377e76abSEd Tanous xml += signal.first; 632377e76abSEd Tanous xml += "\">"; 633377e76abSEd Tanous for (auto& arg : signal.second->get_args()) { 634377e76abSEd Tanous xml += "<arg name=\""; 635377e76abSEd Tanous xml += arg.name; 636377e76abSEd Tanous xml += "\" type=\""; 637377e76abSEd Tanous xml += arg.type; 638377e76abSEd Tanous xml += "\"/>"; 639377e76abSEd Tanous } 640377e76abSEd Tanous 641377e76abSEd Tanous xml += "</signal>"; 642377e76abSEd Tanous } 643377e76abSEd Tanous 644377e76abSEd Tanous for (auto& property : interface_pair.second->get_properties_map()) { 645377e76abSEd Tanous xml += "<property name=\""; 646377e76abSEd Tanous xml += property.first; 647377e76abSEd Tanous xml += "\" type=\""; 648377e76abSEd Tanous 649377e76abSEd Tanous std::string type = std::string(boost::apply_visitor( 650377e76abSEd Tanous [&](auto val) { 651377e76abSEd Tanous static const auto constexpr sig = 652377e76abSEd Tanous element_signature<decltype(val)>::code; 653377e76abSEd Tanous return &sig[0]; 654377e76abSEd Tanous }, 655377e76abSEd Tanous property.second)); 656377e76abSEd Tanous xml += type; 657e3b0bf5bSEd Tanous xml += "\" access=\""; 658377e76abSEd Tanous // TODO direction can be readwrite, read, or write. Need to 659377e76abSEd Tanous // make this configurable 660377e76abSEd Tanous xml += "readwrite"; 661377e76abSEd Tanous xml += "\"/>"; 662377e76abSEd Tanous } 663377e76abSEd Tanous xml += "</interface>"; 664377e76abSEd Tanous } 665377e76abSEd Tanous } else if (boost::starts_with(object_name, newpath)) { 666377e76abSEd Tanous auto slash_index = object_name.find("/", newpath.size() + 1); 667377e76abSEd Tanous auto subnode = object_name.substr(newpath.size() + 1, 668377e76abSEd Tanous slash_index - newpath.size() - 1); 669377e76abSEd Tanous if (node_names.find(subnode) == node_names.end()) { 670377e76abSEd Tanous node_names.insert(subnode); 671377e76abSEd Tanous xml += "<node name=\""; 672377e76abSEd Tanous xml += subnode; 673377e76abSEd Tanous xml += "\">"; 674377e76abSEd Tanous xml += "</node>"; 675377e76abSEd Tanous } 676377e76abSEd Tanous } 677377e76abSEd Tanous } 678377e76abSEd Tanous xml += "</node>"; 679377e76abSEd Tanous return xml; 680377e76abSEd Tanous } 681377e76abSEd Tanous 682377e76abSEd Tanous private: 683377e76abSEd Tanous std::shared_ptr<dbus::connection> conn; 684377e76abSEd Tanous std::vector<std::shared_ptr<DbusObject>> objects; 685377e76abSEd Tanous std::unique_ptr<dbus::filter> introspect_filter; 686377e76abSEd Tanous std::unique_ptr<dbus::filter> object_manager_filter; 687377e76abSEd Tanous std::unique_ptr<dbus::filter> method_filter; 688377e76abSEd Tanous }; 689377e76abSEd Tanous } 690b2432980SVernon Mauery 691b2432980SVernon Mauery #endif /* DBUS_PROPERTIES_HPP */ 692