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