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