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