xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 6fb503a0)
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     template <typename Element>
148     typename boost::enable_if<std::is_pointer<Element>, bool>::type pack(
149         const Element e) {
150       return iter_.append_basic(
151           element<typename std::remove_pointer<Element>::type>::code, e);
152     }
153 
154     template <typename Container>
155     typename std::enable_if<has_const_iterator<Container>::value &&
156                                 !is_string_type<Container>::value,
157                             bool>::type
158     pack(const Container& c) {
159       message::packer sub;
160 
161       static const constexpr auto signature =
162           element_signature<Container>::code;
163       if (iter_.open_container(signature[0], &signature[1], sub.iter_) ==
164           false) {
165         return false;
166       }
167       for (auto& element : c) {
168         if (!sub.pack(element)) {
169           return false;
170         }
171       }
172       return iter_.close_container(sub.iter_);
173     }
174 
175     bool pack(const char* c) {
176       return iter_.append_basic(element<string>::code, &c);
177     }
178 
179     // bool pack specialization
180     bool pack(bool c) {
181       int v = c;
182       return iter_.append_basic(element<bool>::code, &v);
183     }
184 
185     template <typename Key, typename Value>
186     bool pack(const std::pair<Key, Value> element) {
187       message::packer dict_entry;
188       if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) ==
189           false) {
190         return false;
191       }
192       if (dict_entry.pack(element.first) == false) {
193         return false;
194       };
195       if (dict_entry.pack(element.second) == false) {
196         return false;
197       };
198       return iter_.close_container(dict_entry.iter_);
199     }
200 
201     bool pack(const object_path& e) {
202       const char* c = e.value.c_str();
203       return iter_.append_basic(element<object_path>::code, &c);
204     }
205 
206     bool pack(const string& e) {
207       const char* c = e.c_str();
208       return pack(c);
209     }
210 
211     bool pack(const dbus_variant& v) {
212       // Get the dbus typecode  of the variant being packed
213       const char* type = boost::apply_visitor(
214           [&](auto val) {
215             static const constexpr auto sig =
216                 element_signature<decltype(val)>::code;
217             static_assert(
218                 std::tuple_size<decltype(sig)>::value == 2,
219                 "Element signature for dbus_variant too long.  Expected "
220                 "length of 1");
221             return &sig[0];
222           },
223           v);
224       message::packer sub;
225       iter_.open_container(element<dbus_variant>::code, type, sub.iter_);
226       boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v);
227       iter_.close_container(sub.iter_);
228 
229       return true;
230     }
231   };
232 
233   template <typename... Args>
234   bool pack(const Args&... args) {
235     return packer(*this).pack(args...);
236   }
237 
238   // noop for template functions that have no arguments
239   bool pack() { return true; }
240 
241   struct unpacker {
242     impl::message_iterator iter_;
243     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
244     unpacker() {}
245 
246     template <typename Element, typename... Args>
247     bool unpack(Element& e, Args&... args) {
248       if (unpack(e) == false) {
249         return false;
250       }
251       return unpack(args...);
252     }
253 
254     // Basic type unpack
255     template <typename Element>
256     typename boost::enable_if<is_fixed_type<Element>, bool>::type unpack(
257         Element& e) {
258       if (iter_.get_arg_type() != element<Element>::code) {
259         return false;
260       }
261       iter_.get_basic(&e);
262       // ignoring return code here, as we might hit last element, and don't
263       // really care because get_arg_type will return invalid if we call it
264       // after we're over the struct boundary
265       iter_.next();
266       return true;
267     }
268 
269     // bool unpack specialization
270     bool unpack(bool& s) {
271       if (iter_.get_arg_type() != element<bool>::code) {
272         return false;
273       }
274       int c;
275       iter_.get_basic(&c);
276       s = c;
277       iter_.next();
278       return true;
279     }
280 
281     // std::string unpack specialization
282     bool unpack(string& s) {
283       if (iter_.get_arg_type() != element<string>::code) {
284         return false;
285       }
286       const char* c;
287       iter_.get_basic(&c);
288       s.assign(c);
289       iter_.next();
290       return true;
291     }
292 
293     // object_path unpack specialization
294     bool unpack(object_path& s) {
295       if (iter_.get_arg_type() != element<object_path>::code) {
296         return false;
297       }
298       const char* c;
299       iter_.get_basic(&c);
300       s.value.assign(c);
301       iter_.next();
302       return true;
303     }
304 
305     // object_path unpack specialization
306     bool unpack(signature& s) {
307       if (iter_.get_arg_type() != element<signature>::code) {
308         return false;
309       }
310       const char* c;
311       iter_.get_basic(&c);
312       s.value.assign(c);
313       iter_.next();
314       return true;
315     }
316 
317     // variant unpack specialization
318     bool unpack(dbus_variant& v) {
319       if (iter_.get_arg_type() != element<dbus_variant>::code) {
320         return false;
321       }
322       message::unpacker sub;
323       iter_.recurse(sub.iter_);
324 
325       char arg_type = sub.iter_.get_arg_type();
326 
327       boost::mpl::for_each<dbus_variant::types>([&](auto t) {
328         if (arg_type == element<decltype(t)>::code) {
329           decltype(t) val_to_fill;
330           sub.unpack(val_to_fill);
331           v = val_to_fill;
332         }
333       });
334 
335       iter_.next();
336       return true;
337     }
338 
339     // dict entry unpack specialization
340     template <typename Key, typename Value>
341     bool unpack(std::pair<Key, Value>& v) {
342       auto this_code = iter_.get_arg_type();
343       // This can't use element<std::pair> because there is a difference between
344       // the dbus type code 'e' and the dbus signature code for dict entries
345       // '{'.  Element_signature will return the full signature, and will never
346       // return e, but we still want to type check it before recursing
347       if (this_code != DBUS_TYPE_DICT_ENTRY) {
348         return false;
349       }
350       message::unpacker sub;
351       iter_.recurse(sub.iter_);
352       if (!sub.unpack(v.first)) {
353         return false;
354       }
355       if (!sub.unpack(v.second)) {
356         return false;
357       }
358 
359       iter_.next();
360       return true;
361     }
362 
363     template <typename Container>
364     typename std::enable_if<has_const_iterator<Container>::value &&
365                                 !is_string_type<Container>::value,
366                             bool>::type
367     unpack(Container& c) {
368       auto top_level_arg_type = iter_.get_arg_type();
369       constexpr auto type = element_signature<Container>::code[0];
370       if (top_level_arg_type != type) {
371         return false;
372       }
373       message::unpacker sub;
374 
375       iter_.recurse(sub.iter_);
376       auto arg_type = sub.iter_.get_arg_type();
377       while (arg_type != DBUS_TYPE_INVALID) {
378         c.emplace_back();
379         if (!sub.unpack(c.back())) {
380           return false;
381         }
382         arg_type = sub.iter_.get_arg_type();
383       }
384       iter_.next();
385       return true;
386     }
387   };
388 
389   template <typename... Args>
390   bool unpack(Args&... args) {
391     return unpacker(*this).unpack(args...);
392   }
393 
394  private:
395   static std::string sanitize(const char* str) {
396     return (str == NULL) ? "(null)" : str;
397   }
398 };
399 
400 inline std::ostream& operator<<(std::ostream& os, const message& m) {
401   os << "type='" << m.get_type() << "',"
402      << "sender='" << m.get_sender() << "',"
403      << "interface='" << m.get_interface() << "',"
404      << "member='" << m.get_member() << "',"
405      << "path='" << m.get_path() << "',"
406      << "destination='" << m.get_destination() << "'";
407   return os;
408 }
409 
410 }  // namespace dbus
411 
412 // primary template.
413 template <class T>
414 struct function_traits : function_traits<decltype(&T::operator())> {};
415 
416 // partial specialization for function type
417 template <class R, class... Args>
418 struct function_traits<R(Args...)> {
419   using result_type = R;
420   using argument_types = std::tuple<Args...>;
421   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
422 };
423 
424 // partial specialization for function pointer
425 template <class R, class... Args>
426 struct function_traits<R (*)(Args...)> {
427   using result_type = R;
428   using argument_types = std::tuple<Args...>;
429   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
430 };
431 
432 // partial specialization for std::function
433 template <class R, class... Args>
434 struct function_traits<std::function<R(Args...)>> {
435   using result_type = R;
436   using argument_types = std::tuple<Args...>;
437   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
438 };
439 
440 // partial specialization for pointer-to-member-function (i.e., operator()'s)
441 template <class T, class R, class... Args>
442 struct function_traits<R (T::*)(Args...)> {
443   using result_type = R;
444   using argument_types = std::tuple<Args...>;
445   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
446 };
447 
448 template <class T, class R, class... Args>
449 struct function_traits<R (T::*)(Args...) const> {
450   using result_type = R;
451   using argument_types = std::tuple<Args...>;
452   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
453 };
454 
455 template <class F, size_t... Is>
456 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) {
457   return f(std::integral_constant<size_t, Is>{}...);
458 }
459 
460 template <size_t N, class F>
461 constexpr auto index_apply(F f) {
462   return index_apply_impl(f, std::make_index_sequence<N>{});
463 }
464 
465 template <class Tuple, class F>
466 constexpr auto apply(F f, Tuple& t) {
467   return index_apply<std::tuple_size<Tuple>{}>(
468       [&](auto... Is) { return f(std::get<Is>(t)...); });
469 }
470 
471 template <class Tuple>
472 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) {
473   return index_apply<std::tuple_size<Tuple>{}>(
474       [&](auto... Is) { return m.unpack(std::get<Is>(t)...); });
475 }
476 
477 // Specialization for empty tuples.  No need to unpack if no arguments
478 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) {
479   return true;
480 }
481 
482 template <typename... Args>
483 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) {
484   return index_apply<std::tuple_size<std::tuple<Args...>>{}>(
485       [&](auto... Is) { return m.pack(std::get<Is>(t)...); });
486 }
487 
488 // Specialization for empty tuples.  No need to pack if no arguments
489 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {
490   return true;
491 }
492 
493 // Specialization for single types.  Used when callbacks simply return one value
494 template <typename Element>
495 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) {
496   return m.pack(t);
497 }
498 
499 #include <dbus/impl/message_iterator.ipp>
500 
501 #endif  // DBUS_MESSAGE_HPP
502