xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision e62be329)
1 // Copyright (c) Benjamin Kietzman (github.com/bkietz)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef DBUS_MESSAGE_HPP
7 #define DBUS_MESSAGE_HPP
8 
9 #include <dbus/dbus.h>
10 #include <dbus/element.hpp>
11 #include <dbus/endpoint.hpp>
12 #include <dbus/impl/message_iterator.hpp>
13 #include <iostream>
14 #include <vector>
15 #include <boost/intrusive_ptr.hpp>
16 #include <boost/mpl/for_each.hpp>
17 #include <boost/utility/enable_if.hpp>
18 
19 inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
20 
21 inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); }
22 
23 namespace dbus {
24 
25 class message {
26  private:
27   boost::intrusive_ptr<DBusMessage> message_;
28 
29  public:
30   /// Create a method call message
31   static message new_call(const endpoint& destination) {
32     auto x = message(dbus_message_new_method_call(
33         destination.get_process_name().c_str(), destination.get_path().c_str(),
34         destination.get_interface().c_str(), destination.get_member().c_str()));
35     dbus_message_unref(x.message_.get());
36     return x;
37   }
38 
39   /// Create a method call message
40   static message new_call(const endpoint& destination,
41                           const string& method_name) {
42     auto x = message(dbus_message_new_method_call(
43         destination.get_process_name().c_str(), destination.get_path().c_str(),
44         destination.get_interface().c_str(), method_name.c_str()));
45     dbus_message_unref(x.message_.get());
46     return x;
47   }
48 
49   /// Create a method return message
50   static message new_return(message& call) {
51     auto x = message(dbus_message_new_method_return(call));
52     dbus_message_unref(x.message_.get());
53     return x;
54   }
55 
56   /// Create an error message
57   static message new_error(message& call, const string& error_name,
58                            const string& error_message) {
59     auto x = message(dbus_message_new_error(call, error_name.c_str(),
60                                             error_message.c_str()));
61     dbus_message_unref(x.message_.get());
62     return x;
63   }
64 
65   /// Create a signal message
66   static message new_signal(const endpoint& origin, const string& signal_name) {
67     auto x = message(dbus_message_new_signal(origin.get_path().c_str(),
68                                              origin.get_interface().c_str(),
69                                              signal_name.c_str()));
70     dbus_message_unref(x.message_.get());
71     return x;
72   }
73 
74   message() = delete;
75 
76   message(DBusMessage* m) : message_(m) {}
77 
78   operator DBusMessage*() { return message_.get(); }
79 
80   operator const DBusMessage*() const { return message_.get(); }
81 
82   string get_path() const {
83     return sanitize(dbus_message_get_path(message_.get()));
84   }
85 
86   string get_interface() const {
87     return sanitize(dbus_message_get_interface(message_.get()));
88   }
89 
90   string get_member() const {
91     return sanitize(dbus_message_get_member(message_.get()));
92   }
93 
94   string get_type() const {
95     return sanitize(
96         dbus_message_type_to_string(dbus_message_get_type(message_.get())));
97   }
98 
99   string get_signature() const {
100     return sanitize(dbus_message_get_signature(message_.get()));
101   }
102 
103   string get_sender() const {
104     return sanitize(dbus_message_get_sender(message_.get()));
105   }
106 
107   string get_destination() const {
108     return sanitize(dbus_message_get_destination(message_.get()));
109   }
110 
111   uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
112 
113   message& set_serial(uint32 serial) {
114     dbus_message_set_serial(message_.get(), serial);
115     return *this;
116   }
117 
118   uint32 get_reply_serial() {
119     return dbus_message_get_reply_serial(message_.get());
120   }
121 
122   message& set_reply_serial(uint32 reply_serial) {
123     dbus_message_set_reply_serial(message_.get(), reply_serial);
124     return *this;
125   }
126 
127   struct packer {
128     impl::message_iterator iter_;
129     packer(message& m) { impl::message_iterator::init_append(m, iter_); }
130     packer(){};
131 
132     template <typename Element, typename... Args>
133     bool pack(const Element& e, const Args&... args) {
134       if (this->pack(e) == false) {
135         return false;
136       } else {
137         return pack(args...);
138       }
139     }
140 
141     template <typename Element>
142     typename boost::enable_if<is_fixed_type<Element>, bool>::type pack(
143         const Element& e) {
144       return iter_.append_basic(element<Element>::code, &e);
145     }
146 
147     // Specialization used to represent "dict" in dbus.
148     // TODO(ed) generalize for all "map like" types instead of using vector
149     template <typename Key, typename Value>
150     bool pack(const std::vector<std::pair<Key, Value>>& v) {
151       static const constexpr auto sig =
152           element_signature<typename std::decay<decltype(v)>::type>::code;
153 
154       static_assert(std::tuple_size<decltype(sig)>::value > 2,
155                     "Signature size must be greater than 2 characters long");
156       // Skip over the array part "a" of the signature to get the element
157       // signature.
158       // Open container expects JUST the portion after the "a" in the second arg
159       message::packer sub;
160       iter_.open_container(sig[0], &sig[1], sub.iter_);
161       for (auto& element : v) {
162         sub.pack(element);
163       }
164 
165       return iter_.close_container(sub.iter_);
166     }
167 
168     template <typename Element>
169     bool pack(const std::vector<Element>& v) {
170       message::packer sub;
171       static const constexpr auto signature =
172           element_signature<std::vector<Element>>::code;
173       static_assert(std::tuple_size<decltype(signature)>::value > 2,
174                     "Signature size must be greater than 2 characters long");
175       iter_.open_container(signature[0], &signature[1], sub.iter_);
176       for (auto& element : v) {
177         sub.pack(element);
178       }
179 
180       return iter_.close_container(sub.iter_);
181     }
182 
183     bool pack(const char* c) {
184       return iter_.append_basic(element<string>::code, &c);
185     }
186 
187     template <typename Key, typename Value>
188     bool pack(const std::pair<Key, Value> element) {
189       message::packer dict_entry;
190       if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) ==
191           false) {
192         return false;
193       }
194       if (dict_entry.pack(element.first) == false) {
195         return false;
196       };
197       if (dict_entry.pack(element.second) == false) {
198         return false;
199       };
200       return iter_.close_container(dict_entry.iter_);
201     }
202 
203     bool pack(const string& e) {
204       const char* c = e.c_str();
205       return pack(c);
206     }
207 
208     bool pack(const dbus_variant& v) {
209       // Get the dbus typecode  of the variant being packed
210       const char* type = boost::apply_visitor(
211           [&](auto val) {
212             static const constexpr auto sig =
213                 element_signature<decltype(val)>::code;
214             static_assert(
215                 std::tuple_size<decltype(sig)>::value == 2,
216                 "Element signature for dbus_variant too long.  Expected "
217                 "length of 1");
218             return &sig[0];
219           },
220           v);
221       message::packer sub;
222       iter_.open_container(element<dbus_variant>::code, type, sub.iter_);
223       boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v);
224       iter_.close_container(sub.iter_);
225 
226       return true;
227     }
228   };
229 
230   template <typename... Args>
231   bool pack(const Args&... args) {
232     return packer(*this).pack(args...);
233   }
234 
235   // noop for template functions that have no arguments
236   bool pack() { return true; }
237 
238   struct unpacker {
239     impl::message_iterator iter_;
240     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
241     unpacker() {}
242 
243     template <typename Element, typename... Args>
244     bool unpack(Element& e, Args&... args) {
245       if (unpack(e) == false) {
246         return false;
247       }
248       return unpack(args...);
249     }
250 
251     // Basic type unpack
252     template <typename Element>
253     typename boost::enable_if<is_fixed_type<Element>, bool>::type unpack(
254         Element& e) {
255       if (iter_.get_arg_type() != element<Element>::code) {
256         return false;
257       }
258       iter_.get_basic(&e);
259       // ignoring return code here, as we might hit last element, and don't
260       // really care because get_arg_type will return invalid if we call it
261       // after we're over the struct boundary
262       iter_.next();
263       return true;
264     }
265 
266     // std::string unpack specialization
267     bool unpack(string& s) {
268       if (iter_.get_arg_type() != element<string>::code) {
269         return false;
270       }
271       const char* c;
272       iter_.get_basic(&c);
273       s.assign(c);
274       iter_.next();
275       return true;
276     }
277 
278     // object_path unpack specialization
279     bool unpack(object_path& s) {
280       if (iter_.get_arg_type() != element<object_path>::code) {
281         return false;
282       }
283       const char* c;
284       iter_.get_basic(&c);
285       s.value.assign(c);
286       iter_.next();
287       return true;
288     }
289 
290     // object_path unpack specialization
291     bool unpack(signature& s) {
292       if (iter_.get_arg_type() != element<signature>::code) {
293         return false;
294       }
295       const char* c;
296       iter_.get_basic(&c);
297       s.value.assign(c);
298       iter_.next();
299       return true;
300     }
301 
302     // variant unpack specialization
303     bool unpack(dbus_variant& v) {
304       if (iter_.get_arg_type() != element<dbus_variant>::code) {
305         return false;
306       }
307       message::unpacker sub;
308       iter_.recurse(sub.iter_);
309 
310       char arg_type = sub.iter_.get_arg_type();
311 
312       boost::mpl::for_each<dbus_variant::types>([&](auto t) {
313         if (arg_type == element<decltype(t)>::code) {
314           decltype(t) val_to_fill;
315           sub.unpack(val_to_fill);
316           v = val_to_fill;
317         }
318       });
319 
320       iter_.next();
321       return true;
322     }
323 
324     // dict entry unpack specialization
325     template <typename Key, typename Value>
326     bool unpack(std::pair<Key, Value>& v) {
327       auto this_code = iter_.get_arg_type();
328       // This can't use element<std::pair> because there is a difference between
329       // the dbus type code 'e' and the dbus signature code for dict entries
330       // '{'.  Element_signature will return the full signature, and will never
331       // return e, but we still want to type check it before recursing
332       if (this_code != DBUS_TYPE_DICT_ENTRY) {
333         return false;
334       }
335       message::unpacker sub;
336       iter_.recurse(sub.iter_);
337       if (!sub.unpack(v.first)) {
338         return false;
339       }
340       if (!sub.unpack(v.second)) {
341         return false;
342       }
343 
344       iter_.next();
345       return true;
346     }
347 
348     // dbus array / c++ vector unpack specialization
349     template <typename Element>
350     bool unpack(std::vector<Element>& s) {
351       auto top_level_arg_type = iter_.get_arg_type();
352       if (top_level_arg_type != element<std::vector<Element>>::code) {
353         return false;
354       }
355       message::unpacker sub;
356 
357       iter_.recurse(sub.iter_);
358       auto arg_type = sub.iter_.get_arg_type();
359       while (arg_type != DBUS_TYPE_INVALID) {
360         s.emplace_back();
361         if (!sub.unpack(s.back())) {
362           return false;
363         }
364         arg_type = sub.iter_.get_arg_type();
365       }
366       iter_.next();
367       return true;
368     }
369   };
370 
371   template <typename... Args>
372   bool unpack(Args&... args) {
373     return unpacker(*this).unpack(args...);
374   }
375 
376  private:
377   static std::string sanitize(const char* str) {
378     return (str == NULL) ? "(null)" : str;
379   }
380 };
381 
382 inline std::ostream& operator<<(std::ostream& os, const message& m) {
383   os << "type='" << m.get_type() << "',"
384      << "sender='" << m.get_sender() << "',"
385      << "interface='" << m.get_interface() << "',"
386      << "member='" << m.get_member() << "',"
387      << "path='" << m.get_path() << "',"
388      << "destination='" << m.get_destination() << "'";
389   return os;
390 }
391 
392 }  // namespace dbus
393 
394 // primary template.
395 template <class T>
396 struct function_traits : function_traits<decltype(&T::operator())> {};
397 
398 // partial specialization for function type
399 template <class R, class... Args>
400 struct function_traits<R(Args...)> {
401   using result_type = R;
402   using argument_types = std::tuple<Args...>;
403   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
404 };
405 
406 // partial specialization for function pointer
407 template <class R, class... Args>
408 struct function_traits<R (*)(Args...)> {
409   using result_type = R;
410   using argument_types = std::tuple<Args...>;
411   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
412 };
413 
414 // partial specialization for std::function
415 template <class R, class... Args>
416 struct function_traits<std::function<R(Args...)>> {
417   using result_type = R;
418   using argument_types = std::tuple<Args...>;
419   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
420 };
421 
422 // partial specialization for pointer-to-member-function (i.e., operator()'s)
423 template <class T, class R, class... Args>
424 struct function_traits<R (T::*)(Args...)> {
425   using result_type = R;
426   using argument_types = std::tuple<Args...>;
427   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
428 };
429 
430 template <class T, class R, class... Args>
431 struct function_traits<R (T::*)(Args...) const> {
432   using result_type = R;
433   using argument_types = std::tuple<Args...>;
434   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
435 };
436 
437 template <class F, size_t... Is>
438 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) {
439   return f(std::integral_constant<size_t, Is>{}...);
440 }
441 
442 template <size_t N, class F>
443 constexpr auto index_apply(F f) {
444   return index_apply_impl(f, std::make_index_sequence<N>{});
445 }
446 
447 template <class Tuple, class F>
448 constexpr auto apply(F f, Tuple& t) {
449   return index_apply<std::tuple_size<Tuple>{}>(
450       [&](auto... Is) { return f(std::get<Is>(t)...); });
451 }
452 
453 template <class Tuple>
454 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) {
455   return index_apply<std::tuple_size<Tuple>{}>(
456       [&](auto... Is) { return m.unpack(std::get<Is>(t)...); });
457 }
458 
459 // Specialization for empty tuples.  No need to unpack if no arguments
460 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) {
461   return true;
462 }
463 
464 template <typename... Args>
465 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) {
466   return index_apply<std::tuple_size<std::tuple<Args...>>{}>(
467       [&](auto... Is) { return m.pack(std::get<Is>(t)...); });
468 }
469 
470 // Specialization for empty tuples.  No need to pack if no arguments
471 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {
472   return true;
473 }
474 
475 // Specialization for single types.  Used when callbacks simply return one value
476 template <typename Element>
477 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) {
478   return m.pack(t);
479 }
480 
481 #include <dbus/impl/message_iterator.ipp>
482 
483 #endif  // DBUS_MESSAGE_HPP
484