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