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