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