xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 165e3f02)
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 
intrusive_ptr_add_ref(DBusMessage * m)19 inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
20 
intrusive_ptr_release(DBusMessage * m)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
new_call(const endpoint & destination)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
new_call(const endpoint & destination,const string & method_name)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
new_return(message & call)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
new_error(message & call,const string & error_name,const string & 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
new_signal(const endpoint & origin,const string & signal_name)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 
message(DBusMessage * m)76   message(DBusMessage* m) : message_(m) {}
77 
operator DBusMessage*()78   operator DBusMessage*() { return message_.get(); }
79 
80   operator const DBusMessage*() const { return message_.get(); }
81 
get_path() const82   string get_path() const {
83     return sanitize(dbus_message_get_path(message_.get()));
84   }
85 
get_interface() const86   string get_interface() const {
87     return sanitize(dbus_message_get_interface(message_.get()));
88   }
89 
get_member() const90   string get_member() const {
91     return sanitize(dbus_message_get_member(message_.get()));
92   }
93 
get_type() const94   string get_type() const {
95     return sanitize(
96         dbus_message_type_to_string(dbus_message_get_type(message_.get())));
97   }
98 
get_signature() const99   string get_signature() const {
100     return sanitize(dbus_message_get_signature(message_.get()));
101   }
102 
get_sender() const103   string get_sender() const {
104     return sanitize(dbus_message_get_sender(message_.get()));
105   }
106 
get_destination() const107   string get_destination() const {
108     return sanitize(dbus_message_get_destination(message_.get()));
109   }
110 
get_serial()111   uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
112 
set_serial(uint32 serial)113   message& set_serial(uint32 serial) {
114     dbus_message_set_serial(message_.get(), serial);
115     return *this;
116   }
117 
get_reply_serial()118   uint32 get_reply_serial() {
119     return dbus_message_get_reply_serial(message_.get());
120   }
121 
set_reply_serial(uint32 reply_serial)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_;
packerdbus::message::packer129     packer(message& m) { impl::message_iterator::init_append(m, iter_); }
packerdbus::message::packer130     packer(){};
131 
132     template <typename Element, typename... Args>
packdbus::message::packer133     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>
packdbus::message::packer142     typename std::enable_if<is_fixed_type<Element>::value, bool>::type pack(
143         const Element& e) {
144       return iter_.append_basic(element<Element>::code, &e);
145     }
146 
147     template <typename Element>
packdbus::message::packer148     typename std::enable_if<std::is_pointer<Element>::value, bool>::type pack(
149         const Element e) {
150       return pack(*e);
151     }
152 
153     template <typename Container>
154     typename std::enable_if<has_const_iterator<Container>::value &&
155                                 !is_string_type<Container>::value,
156                             bool>::type
packdbus::message::packer157     pack(const Container& c) {
158       message::packer sub;
159 
160       static const constexpr auto signature =
161           element_signature<Container>::code;
162       if (iter_.open_container(signature[0], &signature[1], sub.iter_) ==
163           false) {
164         return false;
165       }
166       for (auto& element : c) {
167         if (!sub.pack(element)) {
168           return false;
169         }
170       }
171       return iter_.close_container(sub.iter_);
172     }
173 
packdbus::message::packer174     bool pack(const char* c) {
175       return iter_.append_basic(element<string>::code, &c);
176     }
177 
178     // bool pack specialization
packdbus::message::packer179     bool pack(bool c) {
180       int v = c;
181       return iter_.append_basic(element<bool>::code, &v);
182     }
183 
184     template <typename Key, typename Value>
packdbus::message::packer185     bool pack(const std::pair<Key, Value> element) {
186       message::packer dict_entry;
187       if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) ==
188           false) {
189         return false;
190       }
191       if (dict_entry.pack(element.first) == false) {
192         return false;
193       };
194       if (dict_entry.pack(element.second) == false) {
195         return false;
196       };
197       return iter_.close_container(dict_entry.iter_);
198     }
199 
packdbus::message::packer200     bool pack(const object_path& e) {
201       const char* c = e.value.c_str();
202       return iter_.append_basic(element<object_path>::code, &c);
203     }
204 
packdbus::message::packer205     bool pack(const string& e) {
206       const char* c = e.c_str();
207       return pack(c);
208     }
209 
packdbus::message::packer210     bool pack(const dbus_variant& v) {
211       // Get the dbus typecode  of the variant being packed
212       const char* type = boost::apply_visitor(
213           [&](auto val) {
214             static const constexpr auto sig =
215                 element_signature<decltype(val)>::code;
216             return &sig[0];
217           },
218           v);
219       message::packer sub;
220       iter_.open_container(element<dbus_variant>::code, type, sub.iter_);
221       boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v);
222       iter_.close_container(sub.iter_);
223 
224       return true;
225     }
226   };
227 
228   template <typename... Args>
pack(const Args &...args)229   bool pack(const Args&... args) {
230     return packer(*this).pack(args...);
231   }
232 
233   // noop for template functions that have no arguments
pack()234   bool pack() { return true; }
235 
236   struct unpacker {
237     impl::message_iterator iter_;
unpackerdbus::message::unpacker238     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
unpackerdbus::message::unpacker239     unpacker() {}
240 
241     template <typename Element, typename... Args>
unpackdbus::message::unpacker242     bool unpack(Element& e, Args&... args) {
243       if (unpack(e) == false) {
244         return false;
245       }
246       return unpack(args...);
247     }
248 
249     // Basic type unpack
250     template <typename Element>
unpackdbus::message::unpacker251     typename std::enable_if<is_fixed_type<Element>::value, bool>::type unpack(
252         Element& e) {
253       if (iter_.get_arg_type() != element<Element>::code) {
254         return false;
255       }
256       iter_.get_basic(&e);
257       // ignoring return code here, as we might hit last element, and don't
258       // really care because get_arg_type will return invalid if we call it
259       // after we're over the struct boundary
260       iter_.next();
261       return true;
262     }
263 
264     // bool unpack specialization
unpackdbus::message::unpacker265     bool unpack(bool& s) {
266       if (iter_.get_arg_type() != element<bool>::code) {
267         return false;
268       }
269       int c;
270       iter_.get_basic(&c);
271       s = c;
272       iter_.next();
273       return true;
274     }
275 
276     // std::string unpack specialization
unpackdbus::message::unpacker277     bool unpack(string& s) {
278       if (iter_.get_arg_type() != element<string>::code) {
279         return false;
280       }
281       const char* c;
282       iter_.get_basic(&c);
283       s.assign(c);
284       iter_.next();
285       return true;
286     }
287 
288     // object_path unpack specialization
unpackdbus::message::unpacker289     bool unpack(object_path& s) {
290       if (iter_.get_arg_type() != element<object_path>::code) {
291         return false;
292       }
293       const char* c;
294       iter_.get_basic(&c);
295       s.value.assign(c);
296       iter_.next();
297       return true;
298     }
299 
300     // object_path unpack specialization
unpackdbus::message::unpacker301     bool unpack(signature& s) {
302       if (iter_.get_arg_type() != element<signature>::code) {
303         return false;
304       }
305       const char* c;
306       iter_.get_basic(&c);
307       s.value.assign(c);
308       iter_.next();
309       return true;
310     }
311 
312     // variant unpack specialization
unpackdbus::message::unpacker313     bool unpack(dbus_variant& v) {
314       if (iter_.get_arg_type() != element<dbus_variant>::code) {
315         return false;
316       }
317       message::unpacker sub;
318       iter_.recurse(sub.iter_);
319 
320       char arg_type = sub.iter_.get_arg_type();
321 
322       boost::mpl::for_each<dbus_variant::types>([&](auto t) {
323         if (arg_type == element<decltype(t)>::code) {
324           decltype(t) val_to_fill;
325           sub.unpack(val_to_fill);
326           v = val_to_fill;
327         }
328       });
329 
330       iter_.next();
331       return true;
332     }
333 
334     // dict entry unpack specialization
335     template <typename Key, typename Value>
unpackdbus::message::unpacker336     bool unpack(std::pair<Key, Value>& v) {
337       auto this_code = iter_.get_arg_type();
338       // This can't use element<std::pair> because there is a difference between
339       // the dbus type code 'e' and the dbus signature code for dict entries
340       // '{'.  Element_signature will return the full signature, and will never
341       // return e, but we still want to type check it before recursing
342       if (this_code != DBUS_TYPE_DICT_ENTRY) {
343         return false;
344       }
345       message::unpacker sub;
346       iter_.recurse(sub.iter_);
347       if (!sub.unpack(v.first)) {
348         return false;
349       }
350       if (!sub.unpack(v.second)) {
351         return false;
352       }
353 
354       iter_.next();
355       return true;
356     }
357 
358     template <typename T>
359     struct has_emplace_method
360 
361     {
362       struct dummy {};
363 
364       template <typename C, typename P>
365       static auto test(P* p)
366           -> decltype(std::declval<C>().emplace(*p), std::true_type());
367 
368       template <typename, typename>
369       static std::false_type test(...);
370 
371       typedef decltype(test<T, dummy>(nullptr)) type;
372 
373       static constexpr bool value =
374           std::is_same<std::true_type,
375                        decltype(test<T, dummy>(nullptr))>::value;
376     };
377 
378     template <typename T>
379     struct has_emplace_back_method
380 
381     {
382       struct dummy {};
383 
384       template <typename C, typename P>
385       static auto test(P* p)
386           -> decltype(std::declval<C>().emplace_back(*p), std::true_type());
387 
388       template <typename, typename>
389       static std::false_type test(...);
390 
391       typedef decltype(test<T, dummy>(nullptr)) type;
392 
393       static constexpr bool value =
394           std::is_same<std::true_type,
395                        decltype(test<T, dummy>(nullptr))>::value;
396     };
397 
398     template <typename Container>
399     typename std::enable_if<has_emplace_back_method<Container>::value &&
400                                 !is_string_type<Container>::value,
401                             bool>::type
unpackdbus::message::unpacker402     unpack(Container& c) {
403       auto top_level_arg_type = iter_.get_arg_type();
404       constexpr auto type = element_signature<Container>::code[0];
405       if (top_level_arg_type != type) {
406         return false;
407       }
408       message::unpacker sub;
409 
410       iter_.recurse(sub.iter_);
411       while (sub.iter_.get_arg_type() != DBUS_TYPE_INVALID) {
412         c.emplace_back();
413         if (!sub.unpack(c.back())) {
414           return false;
415         }
416       }
417       iter_.next();
418       return true;
419     }
420 
421     template <typename Container>
422     typename std::enable_if<has_emplace_method<Container>::value &&
423                                 !is_string_type<Container>::value,
424                             bool>::type
unpackdbus::message::unpacker425     unpack(Container& c) {
426       auto top_level_arg_type = iter_.get_arg_type();
427       constexpr auto type = element_signature<Container>::code[0];
428       if (top_level_arg_type != type) {
429         return false;
430       }
431       message::unpacker sub;
432 
433       iter_.recurse(sub.iter_);
434       while (sub.iter_.get_arg_type() != DBUS_TYPE_INVALID) {
435         // TODO(ed) this is done as an unpack to a stack variable, then an
436         // emplace move into the container (as the key isn't known until the
437         // unpack is done)  This could be made more efficient by unpacking only
438         // the key type into a temporary, using find on the temporary, then
439         // unpacking directly into the map type, instead of unpacking both key
440         // and value.
441 
442         typename Container::value_type t;
443         if (!sub.unpack(t)) {
444           return false;
445         }
446         c.emplace(std::move(t));
447       }
448       iter_.next();
449       return true;
450     }
451   };
452 
453   template <typename... Args>
unpack(Args &...args)454   bool unpack(Args&... args) {
455     return unpacker(*this).unpack(args...);
456   }
457 
458  private:
sanitize(const char * str)459   static std::string sanitize(const char* str) {
460     return (str == NULL) ? "(null)" : str;
461   }
462 };
463 
operator <<(std::ostream & os,const message & m)464 inline std::ostream& operator<<(std::ostream& os, const message& m) {
465   os << "type='" << m.get_type() << "',"
466      << "sender='" << m.get_sender() << "',"
467      << "interface='" << m.get_interface() << "',"
468      << "member='" << m.get_member() << "',"
469      << "path='" << m.get_path() << "',"
470      << "destination='" << m.get_destination() << "'";
471   return os;
472 }
473 
474 }  // namespace dbus
475 
476 // primary template.
477 template <class T>
478 struct function_traits : function_traits<decltype(&T::operator())> {};
479 
480 // partial specialization for function type
481 template <class R, class... Args>
482 struct function_traits<R(Args...)> {
483   using result_type = R;
484   using argument_types = std::tuple<Args...>;
485   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
486 };
487 
488 // partial specialization for function pointer
489 template <class R, class... Args>
490 struct function_traits<R (*)(Args...)> {
491   using result_type = R;
492   using argument_types = std::tuple<Args...>;
493   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
494 };
495 
496 // partial specialization for std::function
497 template <class R, class... Args>
498 struct function_traits<std::function<R(Args...)>> {
499   using result_type = R;
500   using argument_types = std::tuple<Args...>;
501   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
502 };
503 
504 // partial specialization for pointer-to-member-function (i.e., operator()'s)
505 template <class T, class R, class... Args>
506 struct function_traits<R (T::*)(Args...)> {
507   using result_type = R;
508   using argument_types = std::tuple<Args...>;
509   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
510 };
511 
512 template <class T, class R, class... Args>
513 struct function_traits<R (T::*)(Args...) const> {
514   using result_type = R;
515   using argument_types = std::tuple<Args...>;
516   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
517 };
518 
519 template <class F, size_t... Is>
index_apply_impl(F f,std::index_sequence<Is...>)520 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) {
521   return f(std::integral_constant<size_t, Is>{}...);
522 }
523 
524 template <size_t N, class F>
index_apply(F f)525 constexpr auto index_apply(F f) {
526   return index_apply_impl(f, std::make_index_sequence<N>{});
527 }
528 
529 template <class Tuple, class F>
apply(F f,Tuple & t)530 constexpr auto apply(F f, Tuple& t) {
531   return index_apply<std::tuple_size<Tuple>{}>(
532       [&](auto... Is) { return f(std::get<Is>(t)...); });
533 }
534 
535 template <class Tuple>
unpack_into_tuple(Tuple & t,dbus::message & m)536 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) {
537   return index_apply<std::tuple_size<Tuple>{}>(
538       [&](auto... Is) { return m.unpack(std::get<Is>(t)...); });
539 }
540 
541 // Specialization for empty tuples.  No need to unpack if no arguments
unpack_into_tuple(std::tuple<> & t,dbus::message & m)542 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) {
543   return true;
544 }
545 
546 template <typename... Args>
pack_tuple_into_msg(std::tuple<Args...> & t,dbus::message & m)547 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) {
548   return index_apply<std::tuple_size<std::tuple<Args...>>{}>(
549       [&](auto... Is) { return m.pack(std::get<Is>(t)...); });
550 }
551 
552 // Specialization for empty tuples.  No need to pack if no arguments
pack_tuple_into_msg(std::tuple<> & t,dbus::message & m)553 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {
554   return true;
555 }
556 
557 // Specialization for single types.  Used when callbacks simply return one value
558 template <typename Element>
pack_tuple_into_msg(Element & t,dbus::message & m)559 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) {
560   return m.pack(t);
561 }
562 
563 #include <dbus/impl/message_iterator.ipp>
564 
565 #endif  // DBUS_MESSAGE_HPP
566