1b2432980SVernon Mauery #ifndef DBUS_PROPERTIES_HPP
2b2432980SVernon Mauery #define DBUS_PROPERTIES_HPP
3b2432980SVernon Mauery
4377e76abSEd Tanous #include <dbus/connection.hpp>
5377e76abSEd Tanous #include <dbus/filter.hpp>
6377e76abSEd Tanous #include <dbus/match.hpp>
7377e76abSEd Tanous #include <functional>
8377e76abSEd Tanous #include <tuple>
9377e76abSEd Tanous #include <type_traits>
10377e76abSEd Tanous #include <boost/algorithm/string/predicate.hpp>
11377e76abSEd Tanous #include <boost/container/flat_map.hpp>
12377e76abSEd Tanous #include <boost/container/flat_set.hpp>
13377e76abSEd Tanous
14377e76abSEd Tanous namespace dbus {
15377e76abSEd Tanous struct DbusArgument {
DbusArgumentdbus::DbusArgument16377e76abSEd Tanous DbusArgument(const std::string& direction, const std::string& name,
17377e76abSEd Tanous const std::string& type)
18377e76abSEd Tanous : direction(direction), name(name), type(type){};
19377e76abSEd Tanous std::string direction;
20377e76abSEd Tanous std::string name;
21377e76abSEd Tanous std::string type;
22377e76abSEd Tanous };
23377e76abSEd Tanous
24377e76abSEd Tanous class DbusMethod {
25377e76abSEd Tanous public:
DbusMethod(const std::string & name,std::shared_ptr<dbus::connection> & conn)26e3b0bf5bSEd Tanous DbusMethod(const std::string& name, std::shared_ptr<dbus::connection>& conn)
27377e76abSEd Tanous : name(name), conn(conn){};
call(dbus::message & m)28377e76abSEd Tanous virtual void call(dbus::message& m){};
get_args()29377e76abSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; };
30377e76abSEd Tanous std::string name;
31377e76abSEd Tanous std::shared_ptr<dbus::connection> conn;
32377e76abSEd Tanous };
33377e76abSEd Tanous
34377e76abSEd Tanous enum class UpdateType { VALUE_CHANGE_ONLY, FORCE };
35377e76abSEd Tanous
36377e76abSEd Tanous // Base case for when I == the size of the tuple args. Does nothing, as we
37377e76abSEd Tanous // should be done
387a440136SEd Tanous template <std::size_t TupleIndex = 0, typename... Tp>
397a440136SEd Tanous 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)407a440136SEd Tanous arg_types(bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v,
41e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) {}
42377e76abSEd Tanous
43377e76abSEd Tanous // Case for when I < the size of tuple args. Unpacks the tuple type into the
44377e76abSEd Tanous // dbusargument object and names it appropriately.
457a440136SEd Tanous template <std::size_t TupleIndex = 0, typename... Tp>
46377e76abSEd Tanous inline typename std::enable_if <
477a440136SEd Tanous TupleIndex<sizeof...(Tp), void>::type arg_types(
48e3b0bf5bSEd Tanous bool in, std::tuple<Tp...>& t, std::vector<DbusArgument>& v,
49e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) {
507a440136SEd Tanous typedef typename std::tuple_element<TupleIndex, std::tuple<Tp...>>::type
517a440136SEd Tanous element_type;
52377e76abSEd Tanous auto constexpr sig = element_signature<element_type>::code;
53e3b0bf5bSEd Tanous std::string name;
54e3b0bf5bSEd Tanous std::string direction;
557a440136SEd Tanous if (arg_names == nullptr || arg_names->size() <= TupleIndex) {
56377e76abSEd Tanous if (in) {
577a440136SEd Tanous name = "arg_" + std::to_string(TupleIndex);
58377e76abSEd Tanous } else {
597a440136SEd Tanous name = "out_" + std::to_string(TupleIndex);
60377e76abSEd Tanous }
61e3b0bf5bSEd Tanous } else {
627a440136SEd Tanous name = (*arg_names)[TupleIndex];
63e3b0bf5bSEd Tanous }
64e3b0bf5bSEd Tanous v.emplace_back(in ? "in" : "out", name, &sig[0]);
65e3b0bf5bSEd Tanous
667a440136SEd Tanous arg_types<TupleIndex + 1, Tp...>(in, t, v, arg_names);
67e3b0bf5bSEd Tanous }
68e3b0bf5bSEd Tanous
69e3b0bf5bSEd Tanous // Special case for handling raw arguments returned from handlers. Because they
70e3b0bf5bSEd Tanous // don't get stored in a tuple, special handling is neccesary
71e3b0bf5bSEd Tanous template <typename Element>
arg_types(bool in,Element & t,std::vector<DbusArgument> & v,const std::vector<std::string> * arg_names=nullptr)72e3b0bf5bSEd Tanous void arg_types(bool in, Element& t, std::vector<DbusArgument>& v,
73e3b0bf5bSEd Tanous const std::vector<std::string>* arg_names = nullptr) {
74e3b0bf5bSEd Tanous auto constexpr sig = element_signature<Element>::code;
75e3b0bf5bSEd Tanous std::string name;
76e3b0bf5bSEd Tanous if (arg_names == nullptr || arg_names->size() < 1) {
77e3b0bf5bSEd Tanous name.assign("arg_0");
78e3b0bf5bSEd Tanous } else {
79e3b0bf5bSEd Tanous name = (*arg_names)[0];
80e3b0bf5bSEd Tanous }
81e3b0bf5bSEd Tanous
82e3b0bf5bSEd Tanous v.emplace_back(in ? "in" : "out", name, &sig[0]);
83377e76abSEd Tanous }
84377e76abSEd Tanous
85377e76abSEd Tanous template <typename Handler>
86377e76abSEd Tanous class LambdaDbusMethod : public DbusMethod {
87377e76abSEd Tanous public:
88377e76abSEd Tanous typedef function_traits<Handler> traits;
89377e76abSEd Tanous typedef typename traits::decayed_arg_types InputTupleType;
90e3b0bf5bSEd Tanous typedef typename traits::result_type ResultType;
LambdaDbusMethod(const std::string name,std::shared_ptr<dbus::connection> & conn,Handler h)91e3b0bf5bSEd Tanous LambdaDbusMethod(const std::string name,
92e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn, Handler h)
93e3b0bf5bSEd Tanous : DbusMethod(name, conn), h(std::move(h)) {
94e3b0bf5bSEd Tanous InputTupleType t;
95e3b0bf5bSEd Tanous arg_types(true, t, args);
96e3b0bf5bSEd Tanous
97e3b0bf5bSEd Tanous ResultType o;
98e3b0bf5bSEd Tanous arg_types(false, o, args);
99e3b0bf5bSEd Tanous }
100e3b0bf5bSEd Tanous
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)101e3b0bf5bSEd Tanous LambdaDbusMethod(const std::string& name,
102e3b0bf5bSEd Tanous const std::vector<std::string>& input_arg_names,
103e3b0bf5bSEd Tanous const std::vector<std::string>& output_arg_names,
104e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn, Handler h)
105e3b0bf5bSEd Tanous : DbusMethod(name, conn), h(std::move(h)) {
106e3b0bf5bSEd Tanous InputTupleType t;
107e3b0bf5bSEd Tanous arg_types(true, t, args, &input_arg_names);
108e3b0bf5bSEd Tanous
109e3b0bf5bSEd Tanous ResultType o;
110e3b0bf5bSEd Tanous arg_types(false, o, args, &output_arg_names);
111e3b0bf5bSEd Tanous }
call(dbus::message & m)112377e76abSEd Tanous void call(dbus::message& m) override {
113377e76abSEd Tanous InputTupleType input_args;
114a04118e8SEd Tanous if (unpack_into_tuple(input_args, m) == false) {
115a04118e8SEd Tanous auto err = dbus::message::new_error(m, DBUS_ERROR_INVALID_ARGS, "");
116a04118e8SEd Tanous conn->send(err, std::chrono::seconds(0));
117a04118e8SEd Tanous return;
118a04118e8SEd Tanous }
119a04118e8SEd Tanous try {
120e62be329SEd Tanous ResultType r = apply(h, input_args);
121377e76abSEd Tanous auto ret = dbus::message::new_return(m);
122a04118e8SEd Tanous if (pack_tuple_into_msg(r, ret) == false) {
123a04118e8SEd Tanous auto err = dbus::message::new_error(
124a04118e8SEd Tanous m, DBUS_ERROR_FAILED, "Handler had issue when packing response");
125a04118e8SEd Tanous conn->send(err, std::chrono::seconds(0));
126a04118e8SEd Tanous return;
127a04118e8SEd Tanous }
128377e76abSEd Tanous conn->send(ret, std::chrono::seconds(0));
129a04118e8SEd Tanous } catch (...) {
130a04118e8SEd Tanous auto err = dbus::message::new_error(
131a04118e8SEd Tanous m, DBUS_ERROR_FAILED,
132a04118e8SEd Tanous "Handler threw exception while handling request.");
133a04118e8SEd Tanous conn->send(err, std::chrono::seconds(0));
134a04118e8SEd Tanous return;
135a04118e8SEd Tanous }
136377e76abSEd Tanous };
137377e76abSEd Tanous
get_args()138e3b0bf5bSEd Tanous std::vector<DbusArgument> get_args() override { return args; };
139377e76abSEd Tanous Handler h;
140e3b0bf5bSEd Tanous std::vector<DbusArgument> args;
141377e76abSEd Tanous };
142377e76abSEd Tanous
143377e76abSEd Tanous class DbusSignal {
144377e76abSEd Tanous public:
DbusSignal()145e3b0bf5bSEd Tanous DbusSignal(){};
get_args()146e3b0bf5bSEd Tanous virtual std::vector<DbusArgument> get_args() { return {}; }
147e3b0bf5bSEd Tanous };
148377e76abSEd Tanous
149e3b0bf5bSEd Tanous template <typename... Args>
150e3b0bf5bSEd Tanous class DbusTemplateSignal : public DbusSignal {
151e3b0bf5bSEd Tanous 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)152e3b0bf5bSEd Tanous DbusTemplateSignal(const std::string& name, const std::string& object_name,
153e3b0bf5bSEd Tanous const std::string& interface_name,
154e3b0bf5bSEd Tanous const std::vector<std::string>& names,
155e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection>& conn)
156e3b0bf5bSEd Tanous : DbusSignal(),
157e3b0bf5bSEd Tanous name(name),
158e3b0bf5bSEd Tanous object_name(object_name),
159e3b0bf5bSEd Tanous interface_name(interface_name),
160e3b0bf5bSEd Tanous conn(conn) {
161e3b0bf5bSEd Tanous std::tuple<Args...> tu;
162e3b0bf5bSEd Tanous arg_types(true, tu, args, &names);
163e3b0bf5bSEd Tanous };
164e3b0bf5bSEd Tanous
send(const Args &...)165a8b4eac4SEd Tanous void send(const Args&...) {
166e3b0bf5bSEd Tanous dbus::endpoint endpoint("", object_name, interface_name);
167e3b0bf5bSEd Tanous auto m = dbus::message::new_signal(endpoint, name);
168e3b0bf5bSEd Tanous conn->send(m, std::chrono::seconds(0));
169e3b0bf5bSEd Tanous }
170e3b0bf5bSEd Tanous
get_args()171e3b0bf5bSEd Tanous std::vector<DbusArgument> get_args() override { return args; };
172e3b0bf5bSEd Tanous
173e3b0bf5bSEd Tanous std::vector<DbusArgument> args;
174e3b0bf5bSEd Tanous std::string name;
175e3b0bf5bSEd Tanous std::string object_name;
176e3b0bf5bSEd Tanous std::string interface_name;
177e3b0bf5bSEd Tanous std::shared_ptr<dbus::connection> conn;
178377e76abSEd Tanous };
179377e76abSEd Tanous
180377e76abSEd Tanous class DbusInterface {
181377e76abSEd Tanous public:
DbusInterface(std::string interface_name,std::shared_ptr<dbus::connection> & conn)182377e76abSEd Tanous DbusInterface(std::string interface_name,
183377e76abSEd Tanous std::shared_ptr<dbus::connection>& conn)
184377e76abSEd Tanous : interface_name(std::move(interface_name)), conn(conn) {}
185377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>>
get_signals()186377e76abSEd Tanous get_signals() {
187377e76abSEd Tanous return dbus_signals;
188377e76abSEd Tanous };
189377e76abSEd Tanous virtual boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>>
get_methods()190377e76abSEd Tanous get_methods() {
191377e76abSEd Tanous return dbus_methods;
192377e76abSEd Tanous };
get_interface_name()193377e76abSEd Tanous virtual std::string get_interface_name() { return interface_name; };
194377e76abSEd Tanous virtual const boost::container::flat_map<std::string, dbus_variant>
get_properties_map()195377e76abSEd Tanous get_properties_map() {
196377e76abSEd Tanous return properties_map;
197377e76abSEd Tanous };
198377e76abSEd Tanous
get_property(const std::string & property_name)199f95fe4ccSVernon Mauery dbus_variant get_property(const std::string& property_name) {
200f95fe4ccSVernon Mauery auto property = properties_map.find(property_name);
201f95fe4ccSVernon Mauery if (property == properties_map.end()) {
202f95fe4ccSVernon Mauery // TODO(ed) property not found error
203f95fe4ccSVernon Mauery throw std::runtime_error("property not found");
204f95fe4ccSVernon Mauery } else {
205f95fe4ccSVernon Mauery return property->second;
206f95fe4ccSVernon Mauery }
207f95fe4ccSVernon Mauery }
208f95fe4ccSVernon Mauery
209377e76abSEd Tanous template <typename VALUE_TYPE>
set_property(const std::string & property_name,const VALUE_TYPE value,UpdateType update_mode=UpdateType::VALUE_CHANGE_ONLY)210377e76abSEd Tanous void set_property(const std::string& property_name, const VALUE_TYPE value,
211377e76abSEd Tanous UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) {
212377e76abSEd Tanous // Construct a change vector of length 1. if this set_properties is ever
213377e76abSEd Tanous // templated for any type, we could probably swap with with a
214377e76abSEd Tanous // std::array<pair, 1>
215377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v;
216377e76abSEd Tanous v.emplace_back(property_name, value);
217377e76abSEd Tanous set_properties(v, update_mode);
218377e76abSEd Tanous }
219377e76abSEd Tanous
set_properties(const std::vector<std::pair<std::string,dbus_variant>> & v,const UpdateType update_mode=UpdateType::VALUE_CHANGE_ONLY)220377e76abSEd Tanous void set_properties(
221377e76abSEd Tanous const std::vector<std::pair<std::string, dbus_variant>>& v,
222377e76abSEd Tanous const UpdateType update_mode = UpdateType::VALUE_CHANGE_ONLY) {
223377e76abSEd Tanous // TODO(ed) generalize this interface for all "map like" types, basically
224377e76abSEd Tanous // anything that will return a const iterator of std::pair<string,
225377e76abSEd Tanous // variant>
226377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> updates;
227377e76abSEd Tanous updates.reserve(v.size());
228377e76abSEd Tanous
229377e76abSEd Tanous if (update_mode == UpdateType::FORCE) {
230377e76abSEd Tanous updates = v;
231377e76abSEd Tanous } else {
232377e76abSEd Tanous for (auto& property : v) {
233377e76abSEd Tanous auto property_map_it = properties_map.find(property.first);
234377e76abSEd Tanous if (property_map_it != properties_map.end()) {
235377e76abSEd Tanous // Property exists in map
236377e76abSEd Tanous if (property_map_it->second != property.second) {
237377e76abSEd Tanous properties_map[property.first] = property.second;
238377e76abSEd Tanous // if value has changed since last set
239377e76abSEd Tanous updates.emplace_back(*property_map_it);
240377e76abSEd Tanous }
241377e76abSEd Tanous } else {
242377e76abSEd Tanous // property doesn't exist, must be new
243377e76abSEd Tanous properties_map[property.first] = property.second;
244377e76abSEd Tanous updates.emplace_back(property.first, property.second);
245377e76abSEd Tanous }
246377e76abSEd Tanous }
247377e76abSEd Tanous }
248377e76abSEd Tanous
2492277cd81SFeist, James dbus::endpoint endpoint("org.freedesktop.DBus", object_name,
250377e76abSEd Tanous "org.freedesktop.DBus.Properties");
251377e76abSEd Tanous
252377e76abSEd Tanous auto m = dbus::message::new_signal(endpoint, "PropertiesChanged");
253377e76abSEd Tanous
254377e76abSEd Tanous static const std::vector<std::string> empty;
255377e76abSEd Tanous m.pack(get_interface_name(), updates, empty);
256377e76abSEd Tanous // TODO(ed) make sure this doesn't block
257377e76abSEd Tanous conn->async_send(
258377e76abSEd Tanous m, [](const boost::system::error_code ec, dbus::message r) {});
259377e76abSEd Tanous }
260377e76abSEd Tanous
register_method(std::shared_ptr<DbusMethod> method)261e3b0bf5bSEd Tanous void register_method(std::shared_ptr<DbusMethod> method) {
262377e76abSEd Tanous dbus_methods.emplace(method->name, method);
263377e76abSEd Tanous }
264377e76abSEd Tanous
265377e76abSEd Tanous template <typename Handler>
register_method(const std::string & name,Handler method)266e3b0bf5bSEd Tanous void register_method(const std::string& name, Handler method) {
267377e76abSEd Tanous dbus_methods.emplace(name,
268377e76abSEd Tanous new LambdaDbusMethod<Handler>(name, conn, method));
269377e76abSEd Tanous }
270377e76abSEd Tanous
271e3b0bf5bSEd Tanous 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)272e3b0bf5bSEd Tanous void register_method(const std::string& name,
273e3b0bf5bSEd Tanous const std::vector<std::string>& input_arg_names,
274e3b0bf5bSEd Tanous const std::vector<std::string>& output_arg_names,
275e3b0bf5bSEd Tanous Handler method) {
276e3b0bf5bSEd Tanous dbus_methods.emplace(
277e3b0bf5bSEd Tanous name, new LambdaDbusMethod<Handler>(name, input_arg_names,
278e3b0bf5bSEd Tanous output_arg_names, conn, method));
279e3b0bf5bSEd Tanous }
280e3b0bf5bSEd Tanous
281e3b0bf5bSEd Tanous template <typename... Args>
register_signal(const std::string & name,const std::vector<std::string> arg_names)282a8b4eac4SEd Tanous std::shared_ptr<DbusTemplateSignal<Args...>> register_signal(
283e3b0bf5bSEd Tanous const std::string& name, const std::vector<std::string> arg_names) {
284a8b4eac4SEd Tanous auto sig = std::make_shared<DbusTemplateSignal<Args...>>(
285a8b4eac4SEd Tanous name, object_name, interface_name, arg_names, conn);
286a8b4eac4SEd Tanous dbus_signals.emplace(name, sig);
287a8b4eac4SEd Tanous return sig;
288e3b0bf5bSEd Tanous }
289e3b0bf5bSEd Tanous
call(dbus::message & m)290377e76abSEd Tanous void call(dbus::message& m) {
291377e76abSEd Tanous std::string method_name = m.get_member();
292377e76abSEd Tanous auto method = dbus_methods.find(method_name);
293377e76abSEd Tanous if (method != dbus_methods.end()) {
294377e76abSEd Tanous method->second->call(m);
295377e76abSEd Tanous } // TODO(ed) send something when method doesn't exist?
296377e76abSEd Tanous }
297377e76abSEd Tanous
298377e76abSEd Tanous std::string object_name;
299377e76abSEd Tanous std::string interface_name;
300377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusMethod>>
301377e76abSEd Tanous dbus_methods;
302377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusSignal>>
303377e76abSEd Tanous dbus_signals;
304377e76abSEd Tanous boost::container::flat_map<std::string, dbus_variant> properties_map;
305377e76abSEd Tanous std::shared_ptr<dbus::connection> conn;
306377e76abSEd Tanous };
307377e76abSEd Tanous
308377e76abSEd Tanous class DbusObject {
309377e76abSEd Tanous public:
DbusObject(std::shared_ptr<dbus::connection> conn,std::string object_name)310377e76abSEd Tanous DbusObject(std::shared_ptr<dbus::connection> conn, std::string object_name)
311377e76abSEd Tanous : object_name(std::move(object_name)), conn(conn) {
312377e76abSEd Tanous properties_iface = add_interface("org.freedesktop.DBus.Properties");
313377e76abSEd Tanous
314377e76abSEd Tanous properties_iface->register_method(
315e3b0bf5bSEd Tanous "Get", {"interface_name", "properties_name"}, {"value"},
316e3b0bf5bSEd Tanous [&](const std::string& interface_name,
317377e76abSEd Tanous const std::string& property_name) {
318377e76abSEd Tanous auto interface_it = interfaces.find(interface_name);
319377e76abSEd Tanous if (interface_it == interfaces.end()) {
320377e76abSEd Tanous // Interface not found error
321377e76abSEd Tanous throw std::runtime_error("interface not found");
322377e76abSEd Tanous } else {
323377e76abSEd Tanous auto& properties_map = interface_it->second->get_properties_map();
324377e76abSEd Tanous auto property = properties_map.find(property_name);
325377e76abSEd Tanous if (property == properties_map.end()) {
326377e76abSEd Tanous // TODO(ed) property not found error
327377e76abSEd Tanous throw std::runtime_error("property not found");
328377e76abSEd Tanous } else {
329377e76abSEd Tanous return std::tuple<dbus_variant>(property->second);
330377e76abSEd Tanous }
331377e76abSEd Tanous }
332377e76abSEd Tanous });
333377e76abSEd Tanous
334e3b0bf5bSEd Tanous properties_iface->register_method(
335e3b0bf5bSEd Tanous "GetAll", {"interface_name"}, {"properties"},
336e3b0bf5bSEd Tanous [&](const std::string& interface_name) {
337377e76abSEd Tanous auto interface_it = interfaces.find(interface_name);
338377e76abSEd Tanous if (interface_it == interfaces.end()) {
339377e76abSEd Tanous // Interface not found error
340377e76abSEd Tanous throw std::runtime_error("interface not found");
341377e76abSEd Tanous } else {
342fee38731SEd Tanous return interface_it->second->get_properties_map();
343377e76abSEd Tanous }
344377e76abSEd Tanous });
345377e76abSEd Tanous properties_iface->register_method(
346e3b0bf5bSEd Tanous "Set", {"interface_name", "properties_name", "value"}, {},
347377e76abSEd Tanous [&](const std::string& interface_name, const std::string& property_name,
348377e76abSEd Tanous const dbus_variant& value) {
349377e76abSEd Tanous auto interface_it = interfaces.find(interface_name);
350377e76abSEd Tanous if (interface_it == interfaces.end()) {
351377e76abSEd Tanous // Interface not found error
352377e76abSEd Tanous throw std::runtime_error("interface not found");
353377e76abSEd Tanous } else {
354377e76abSEd Tanous // Todo, the set propery (signular) interface should support
355a04118e8SEd Tanous // handing a variant. The below is expensive
356377e76abSEd Tanous std::vector<std::pair<std::string, dbus_variant>> v;
357377e76abSEd Tanous v.emplace_back(property_name, value);
358377e76abSEd Tanous interface_it->second->set_properties(v);
359377e76abSEd Tanous return std::tuple<>();
360377e76abSEd Tanous }
361377e76abSEd Tanous });
362e3b0bf5bSEd Tanous
363e3b0bf5bSEd Tanous properties_iface->register_signal<
364e3b0bf5bSEd Tanous std::string, std::vector<std::pair<std::string, dbus_variant>>,
365e3b0bf5bSEd Tanous std::vector<std::string>>(
366e3b0bf5bSEd Tanous "PropertiesChanged",
367e3b0bf5bSEd Tanous {"interface_name", "changed_properties", "invalidated_properties"});
368377e76abSEd Tanous }
369377e76abSEd Tanous
add_interface(const std::string & name)370377e76abSEd Tanous std::shared_ptr<DbusInterface> add_interface(const std::string& name) {
371377e76abSEd Tanous auto x = std::make_shared<DbusInterface>(name, conn);
372377e76abSEd Tanous register_interface(x);
373377e76abSEd Tanous return x;
374377e76abSEd Tanous }
375377e76abSEd Tanous
register_interface(std::shared_ptr<DbusInterface> & interface)376377e76abSEd Tanous void register_interface(std::shared_ptr<DbusInterface>& interface) {
377377e76abSEd Tanous interfaces[interface->get_interface_name()] = interface;
378377e76abSEd Tanous interface->object_name = object_name;
379377e76abSEd Tanous const static dbus::endpoint endpoint("", object_name,
380377e76abSEd Tanous "org.freedesktop.DBus.ObjectManager");
381377e76abSEd Tanous
382e3b0bf5bSEd Tanous auto m = message::new_signal(endpoint, "InterfacesAdded");
383377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus_variant>> properties_dict;
384377e76abSEd Tanous std::vector<std::pair<std::string, properties_dict>> sig;
385377e76abSEd Tanous sig.emplace_back(interface->get_interface_name(), properties_dict());
386377e76abSEd Tanous auto& prop_dict = sig.back().second;
387377e76abSEd Tanous for (auto& property : interface->get_properties_map()) {
388377e76abSEd Tanous prop_dict.emplace_back(property);
389377e76abSEd Tanous }
390377e76abSEd Tanous
391377e76abSEd Tanous m.pack(object_name, sig);
392f76e5e0bSFeist, James
393f76e5e0bSFeist, James conn->send(m, std::chrono::seconds(0));
394377e76abSEd Tanous }
395377e76abSEd Tanous
get_interfaces()396377e76abSEd Tanous auto get_interfaces() { return interfaces; }
397377e76abSEd Tanous
call(dbus::message & m)398377e76abSEd Tanous void call(dbus::message& m) {
399377e76abSEd Tanous auto interface = interfaces.find(m.get_interface());
400377e76abSEd Tanous if (interface != interfaces.end()) {
401377e76abSEd Tanous interface->second->call(m);
402377e76abSEd Tanous } // TODO(ed) send something when interface doesn't exist?
403377e76abSEd Tanous }
404377e76abSEd Tanous
405377e76abSEd Tanous std::string object_name;
406377e76abSEd Tanous std::shared_ptr<dbus::connection> conn;
407377e76abSEd Tanous
408377e76abSEd Tanous // dbus::filter properties_filter;
409377e76abSEd Tanous std::shared_ptr<DbusInterface> properties_iface;
410377e76abSEd Tanous
411e3b0bf5bSEd Tanous std::shared_ptr<DbusInterface> object_manager_iface;
412e3b0bf5bSEd Tanous
413377e76abSEd Tanous std::function<void(boost::system::error_code, message)> callback;
414377e76abSEd Tanous boost::container::flat_map<std::string, std::shared_ptr<DbusInterface>>
415377e76abSEd Tanous interfaces;
416377e76abSEd Tanous };
417377e76abSEd Tanous
418377e76abSEd Tanous class DbusObjectServer {
419377e76abSEd Tanous public:
DbusObjectServer(std::shared_ptr<dbus::connection> & conn)420377e76abSEd Tanous DbusObjectServer(std::shared_ptr<dbus::connection>& conn) : conn(conn) {
421377e76abSEd Tanous introspect_filter =
422377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) {
423377e76abSEd Tanous if (m.get_type() != "method_call") {
424377e76abSEd Tanous return false;
425377e76abSEd Tanous }
426377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.Introspectable") {
427377e76abSEd Tanous return false;
428377e76abSEd Tanous }
429377e76abSEd Tanous if (m.get_member() != "Introspect") {
430377e76abSEd Tanous return false;
431377e76abSEd Tanous };
432377e76abSEd Tanous return true;
433377e76abSEd Tanous });
434377e76abSEd Tanous
435377e76abSEd Tanous introspect_filter->async_dispatch(
436377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) {
437377e76abSEd Tanous on_introspect(ec, m);
438377e76abSEd Tanous });
439377e76abSEd Tanous
440377e76abSEd Tanous object_manager_filter =
441377e76abSEd Tanous std::make_unique<dbus::filter>(conn, [](dbus::message m) {
442377e76abSEd Tanous
443377e76abSEd Tanous if (m.get_type() != "method_call") {
444377e76abSEd Tanous return false;
445377e76abSEd Tanous }
446377e76abSEd Tanous if (m.get_interface() != "org.freedesktop.DBus.ObjectManager") {
447377e76abSEd Tanous return false;
448377e76abSEd Tanous }
449377e76abSEd Tanous if (m.get_member() != "GetManagedObjects") {
450377e76abSEd Tanous return false;
451377e76abSEd Tanous };
452377e76abSEd Tanous return true;
453377e76abSEd Tanous });
454377e76abSEd Tanous
455377e76abSEd Tanous object_manager_filter->async_dispatch(
456377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) {
457377e76abSEd Tanous on_get_managed_objects(ec, m);
458377e76abSEd Tanous });
459377e76abSEd Tanous
460377e76abSEd Tanous method_filter = std::make_unique<dbus::filter>(conn, [](dbus::message m) {
461377e76abSEd Tanous
462377e76abSEd Tanous if (m.get_type() != "method_call") {
463377e76abSEd Tanous return false;
464377e76abSEd Tanous }
465377e76abSEd Tanous return true;
466377e76abSEd Tanous });
467377e76abSEd Tanous
468377e76abSEd Tanous method_filter->async_dispatch(
469377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) {
470377e76abSEd Tanous on_method_call(ec, m);
471377e76abSEd Tanous });
472377e76abSEd Tanous };
473377e76abSEd Tanous
get_connection()474377e76abSEd Tanous std::shared_ptr<dbus::connection>& get_connection() { return conn; }
on_introspect(const boost::system::error_code ec,dbus::message m)475377e76abSEd Tanous void on_introspect(const boost::system::error_code ec, dbus::message m) {
476377e76abSEd Tanous auto xml = get_xml_for_path(m.get_path());
477377e76abSEd Tanous auto ret = dbus::message::new_return(m);
478377e76abSEd Tanous ret.pack(xml);
479377e76abSEd Tanous conn->async_send(
480377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {});
481377e76abSEd Tanous
482377e76abSEd Tanous introspect_filter->async_dispatch(
483377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) {
484377e76abSEd Tanous on_introspect(ec, m);
485377e76abSEd Tanous });
486377e76abSEd Tanous }
487377e76abSEd Tanous
on_method_call(const boost::system::error_code ec,dbus::message m)488377e76abSEd Tanous void on_method_call(const boost::system::error_code ec, dbus::message m) {
489377e76abSEd Tanous if (ec) {
490377e76abSEd Tanous std::cerr << "on_method_call error: " << ec << "\n";
491377e76abSEd Tanous } else {
492377e76abSEd Tanous auto path = m.get_path();
493377e76abSEd Tanous // TODO(ed) objects should be a map
494377e76abSEd Tanous for (auto& object : objects) {
495377e76abSEd Tanous if (object->object_name == path) {
496377e76abSEd Tanous object->call(m);
497377e76abSEd Tanous break;
498377e76abSEd Tanous }
499377e76abSEd Tanous }
500377e76abSEd Tanous }
501377e76abSEd Tanous method_filter->async_dispatch(
502377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) {
503377e76abSEd Tanous on_method_call(ec, m);
504377e76abSEd Tanous });
505377e76abSEd Tanous }
506377e76abSEd Tanous
on_get_managed_objects(const boost::system::error_code ec,dbus::message m)507377e76abSEd Tanous void on_get_managed_objects(const boost::system::error_code ec,
508377e76abSEd Tanous dbus::message m) {
509377e76abSEd Tanous typedef std::vector<std::pair<std::string, dbus::dbus_variant>>
510377e76abSEd Tanous properties_dict;
511377e76abSEd Tanous
512377e76abSEd Tanous typedef std::vector<std::pair<std::string, properties_dict>>
513377e76abSEd Tanous interfaces_dict;
514377e76abSEd Tanous
51570d534bfSFeist, James std::vector<std::pair<object_path, interfaces_dict>> dict;
516377e76abSEd Tanous
517377e76abSEd Tanous for (auto& object : objects) {
518377e76abSEd Tanous interfaces_dict i;
519377e76abSEd Tanous for (auto& interface : object->get_interfaces()) {
520377e76abSEd Tanous properties_dict p;
521377e76abSEd Tanous
522377e76abSEd Tanous for (auto& property : interface.second->get_properties_map()) {
523377e76abSEd Tanous p.push_back(property);
524377e76abSEd Tanous }
525377e76abSEd Tanous i.emplace_back(interface.second->get_interface_name(), std::move(p));
526377e76abSEd Tanous }
52770d534bfSFeist, James dict.emplace_back(object_path{object->object_name}, std::move(i));
528377e76abSEd Tanous }
529377e76abSEd Tanous auto ret = dbus::message::new_return(m);
530377e76abSEd Tanous ret.pack(dict);
531377e76abSEd Tanous conn->async_send(
532377e76abSEd Tanous ret, [](const boost::system::error_code ec, dbus::message r) {});
533377e76abSEd Tanous
534377e76abSEd Tanous object_manager_filter->async_dispatch(
535377e76abSEd Tanous [&](const boost::system::error_code ec, dbus::message m) {
536377e76abSEd Tanous on_get_managed_objects(ec, m);
537377e76abSEd Tanous });
538377e76abSEd Tanous }
539377e76abSEd Tanous
add_object(const std::string & name)540377e76abSEd Tanous std::shared_ptr<DbusObject> add_object(const std::string& name) {
541377e76abSEd Tanous auto x = std::make_shared<DbusObject>(conn, name);
542377e76abSEd Tanous register_object(x);
543377e76abSEd Tanous return x;
544377e76abSEd Tanous }
545377e76abSEd Tanous
register_object(std::shared_ptr<DbusObject> object)546377e76abSEd Tanous void register_object(std::shared_ptr<DbusObject> object) {
547377e76abSEd Tanous objects.emplace_back(object);
548377e76abSEd Tanous }
549377e76abSEd Tanous
remove_object(std::shared_ptr<DbusObject> object)550ddc0c513SJames Feist void remove_object(std::shared_ptr<DbusObject> object) {
551ddc0c513SJames Feist std::remove(objects.begin(), objects.end(), object);
552ddc0c513SJames Feist }
553ddc0c513SJames Feist
flush(void)554*70f79f4dSJames Feist void flush(void) { conn->flush(); }
555*70f79f4dSJames Feist
get_xml_for_path(const std::string & path)556377e76abSEd Tanous std::string get_xml_for_path(const std::string& path) {
557377e76abSEd Tanous std::string newpath(path);
558377e76abSEd Tanous
559377e76abSEd Tanous if (newpath == "/") {
560377e76abSEd Tanous newpath.assign("");
561377e76abSEd Tanous }
562377e76abSEd Tanous
563377e76abSEd Tanous boost::container::flat_set<std::string> node_names;
564377e76abSEd Tanous std::string xml(
565377e76abSEd Tanous "<!DOCTYPE node PUBLIC "
566377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
567377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/"
568377e76abSEd Tanous "introspect.dtd\">\n<node>");
569377e76abSEd Tanous for (auto& object : objects) {
570377e76abSEd Tanous std::string& object_name = object->object_name;
571377e76abSEd Tanous // exact match
572377e76abSEd Tanous if (object->object_name == newpath) {
573377e76abSEd Tanous xml +=
574377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.Peer\">"
575377e76abSEd Tanous " <method name=\"Ping\"/>"
576377e76abSEd Tanous " <method name=\"GetMachineId\">"
577377e76abSEd Tanous " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>"
578377e76abSEd Tanous " </method>"
579377e76abSEd Tanous " </interface>";
580377e76abSEd Tanous
581377e76abSEd Tanous xml +=
582377e76abSEd Tanous " <interface name=\"org.freedesktop.DBus.ObjectManager\">"
583377e76abSEd Tanous " <method name=\"GetManagedObjects\">"
584377e76abSEd Tanous " <arg type=\"a{oa{sa{sv}}}\" "
585377e76abSEd Tanous " name=\"object_paths_interfaces_and_properties\" "
586377e76abSEd Tanous " direction=\"out\"/>"
587377e76abSEd Tanous " </method>"
588377e76abSEd Tanous " <signal name=\"InterfacesAdded\">"
589377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>"
590377e76abSEd Tanous " <arg type=\"a{sa{sv}}\" "
591377e76abSEd Tanous "name=\"interfaces_and_properties\"/>"
592377e76abSEd Tanous " </signal>"
593377e76abSEd Tanous " <signal name=\"InterfacesRemoved\">"
594377e76abSEd Tanous " <arg type=\"o\" name=\"object_path\"/>"
595377e76abSEd Tanous " <arg type=\"as\" name=\"interfaces\"/>"
596377e76abSEd Tanous " </signal>"
597377e76abSEd Tanous " </interface>";
598377e76abSEd Tanous
599e3b0bf5bSEd Tanous xml +=
600e3b0bf5bSEd Tanous "<interface name=\"org.freedesktop.DBus.Introspectable\">"
601e3b0bf5bSEd Tanous " <method name=\"Introspect\">"
602e3b0bf5bSEd Tanous " <arg type=\"s\" name=\"xml_data\" direction=\"out\"/>"
603e3b0bf5bSEd Tanous " </method>"
604e3b0bf5bSEd Tanous "</interface>";
605e3b0bf5bSEd Tanous
606377e76abSEd Tanous for (auto& interface_pair : object->interfaces) {
607377e76abSEd Tanous xml += "<interface name=\"";
608377e76abSEd Tanous xml += interface_pair.first;
609377e76abSEd Tanous xml += "\">";
610377e76abSEd Tanous for (auto& method : interface_pair.second->get_methods()) {
611377e76abSEd Tanous xml += "<method name=\"";
612377e76abSEd Tanous xml += method.first;
613377e76abSEd Tanous xml += "\">";
614377e76abSEd Tanous for (auto& arg : method.second->get_args()) {
615377e76abSEd Tanous xml += "<arg name=\"";
616377e76abSEd Tanous xml += arg.name;
617377e76abSEd Tanous xml += "\" type=\"";
618377e76abSEd Tanous xml += arg.type;
619377e76abSEd Tanous xml += "\" direction=\"";
620377e76abSEd Tanous xml += arg.direction;
621377e76abSEd Tanous xml += "\"/>";
622377e76abSEd Tanous }
623377e76abSEd Tanous xml += "</method>";
624377e76abSEd Tanous }
625377e76abSEd Tanous
626377e76abSEd Tanous for (auto& signal : interface_pair.second->get_signals()) {
627377e76abSEd Tanous xml += "<signal name=\"";
628377e76abSEd Tanous xml += signal.first;
629377e76abSEd Tanous xml += "\">";
630377e76abSEd Tanous for (auto& arg : signal.second->get_args()) {
631377e76abSEd Tanous xml += "<arg name=\"";
632377e76abSEd Tanous xml += arg.name;
633377e76abSEd Tanous xml += "\" type=\"";
634377e76abSEd Tanous xml += arg.type;
635377e76abSEd Tanous xml += "\"/>";
636377e76abSEd Tanous }
637377e76abSEd Tanous
638377e76abSEd Tanous xml += "</signal>";
639377e76abSEd Tanous }
640377e76abSEd Tanous
641377e76abSEd Tanous for (auto& property : interface_pair.second->get_properties_map()) {
642377e76abSEd Tanous xml += "<property name=\"";
643377e76abSEd Tanous xml += property.first;
644377e76abSEd Tanous xml += "\" type=\"";
645377e76abSEd Tanous
646377e76abSEd Tanous std::string type = std::string(boost::apply_visitor(
647377e76abSEd Tanous [&](auto val) {
648377e76abSEd Tanous static const auto constexpr sig =
649377e76abSEd Tanous element_signature<decltype(val)>::code;
650377e76abSEd Tanous return &sig[0];
651377e76abSEd Tanous },
652377e76abSEd Tanous property.second));
653377e76abSEd Tanous xml += type;
654e3b0bf5bSEd Tanous xml += "\" access=\"";
655377e76abSEd Tanous // TODO direction can be readwrite, read, or write. Need to
656377e76abSEd Tanous // make this configurable
657377e76abSEd Tanous xml += "readwrite";
658377e76abSEd Tanous xml += "\"/>";
659377e76abSEd Tanous }
660377e76abSEd Tanous xml += "</interface>";
661377e76abSEd Tanous }
662377e76abSEd Tanous } else if (boost::starts_with(object_name, newpath)) {
663377e76abSEd Tanous auto slash_index = object_name.find("/", newpath.size() + 1);
664377e76abSEd Tanous auto subnode = object_name.substr(newpath.size() + 1,
665377e76abSEd Tanous slash_index - newpath.size() - 1);
666377e76abSEd Tanous if (node_names.find(subnode) == node_names.end()) {
667377e76abSEd Tanous node_names.insert(subnode);
668377e76abSEd Tanous xml += "<node name=\"";
669377e76abSEd Tanous xml += subnode;
670377e76abSEd Tanous xml += "\">";
671377e76abSEd Tanous xml += "</node>";
672377e76abSEd Tanous }
673377e76abSEd Tanous }
674377e76abSEd Tanous }
675377e76abSEd Tanous xml += "</node>";
676377e76abSEd Tanous return xml;
677377e76abSEd Tanous }
678377e76abSEd Tanous
679377e76abSEd Tanous private:
680377e76abSEd Tanous std::shared_ptr<dbus::connection> conn;
681377e76abSEd Tanous std::vector<std::shared_ptr<DbusObject>> objects;
682377e76abSEd Tanous std::unique_ptr<dbus::filter> introspect_filter;
683377e76abSEd Tanous std::unique_ptr<dbus::filter> object_manager_filter;
684377e76abSEd Tanous std::unique_ptr<dbus::filter> method_filter;
685377e76abSEd Tanous };
686377e76abSEd Tanous }
687b2432980SVernon Mauery
688b2432980SVernon Mauery #endif /* DBUS_PROPERTIES_HPP */
689