xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 631fa83a)
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 string& e) {
202       const char* c = e.c_str();
203       return pack(c);
204     }
205 
206     bool pack(const dbus_variant& v) {
207       // Get the dbus typecode  of the variant being packed
208       const char* type = boost::apply_visitor(
209           [&](auto val) {
210             static const constexpr auto sig =
211                 element_signature<decltype(val)>::code;
212             static_assert(
213                 std::tuple_size<decltype(sig)>::value == 2,
214                 "Element signature for dbus_variant too long.  Expected "
215                 "length of 1");
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>
229   bool pack(const Args&... args) {
230     return packer(*this).pack(args...);
231   }
232 
233   // noop for template functions that have no arguments
234   bool pack() { return true; }
235 
236   struct unpacker {
237     impl::message_iterator iter_;
238     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
239     unpacker() {}
240 
241     template <typename Element, typename... Args>
242     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>
251     typename boost::enable_if<is_fixed_type<Element>, 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
265     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
277     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
289     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
301     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
313     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>
336     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 Container>
359     typename std::enable_if<has_const_iterator<Container>::value &&
360                                 !is_string_type<Container>::value,
361                             bool>::type
362     unpack(Container& c) {
363       std::cout << "test\n";
364       auto top_level_arg_type = iter_.get_arg_type();
365       constexpr auto type = element_signature<Container>::code[0];
366       if (top_level_arg_type != type) {
367         return false;
368       }
369       message::unpacker sub;
370 
371       iter_.recurse(sub.iter_);
372       auto arg_type = sub.iter_.get_arg_type();
373       while (arg_type != DBUS_TYPE_INVALID) {
374         c.emplace_back();
375         if (!sub.unpack(c.back())) {
376           return false;
377         }
378         arg_type = sub.iter_.get_arg_type();
379       }
380       iter_.next();
381       return true;
382     }
383   };
384 
385   template <typename... Args>
386   bool unpack(Args&... args) {
387     return unpacker(*this).unpack(args...);
388   }
389 
390  private:
391   static std::string sanitize(const char* str) {
392     return (str == NULL) ? "(null)" : str;
393   }
394 };
395 
396 inline std::ostream& operator<<(std::ostream& os, const message& m) {
397   os << "type='" << m.get_type() << "',"
398      << "sender='" << m.get_sender() << "',"
399      << "interface='" << m.get_interface() << "',"
400      << "member='" << m.get_member() << "',"
401      << "path='" << m.get_path() << "',"
402      << "destination='" << m.get_destination() << "'";
403   return os;
404 }
405 
406 }  // namespace dbus
407 
408 // primary template.
409 template <class T>
410 struct function_traits : function_traits<decltype(&T::operator())> {};
411 
412 // partial specialization for function type
413 template <class R, class... Args>
414 struct function_traits<R(Args...)> {
415   using result_type = R;
416   using argument_types = std::tuple<Args...>;
417   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
418 };
419 
420 // partial specialization for function pointer
421 template <class R, class... Args>
422 struct function_traits<R (*)(Args...)> {
423   using result_type = R;
424   using argument_types = std::tuple<Args...>;
425   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
426 };
427 
428 // partial specialization for std::function
429 template <class R, class... Args>
430 struct function_traits<std::function<R(Args...)>> {
431   using result_type = R;
432   using argument_types = std::tuple<Args...>;
433   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
434 };
435 
436 // partial specialization for pointer-to-member-function (i.e., operator()'s)
437 template <class T, class R, class... Args>
438 struct function_traits<R (T::*)(Args...)> {
439   using result_type = R;
440   using argument_types = std::tuple<Args...>;
441   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
442 };
443 
444 template <class T, class R, class... Args>
445 struct function_traits<R (T::*)(Args...) const> {
446   using result_type = R;
447   using argument_types = std::tuple<Args...>;
448   using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
449 };
450 
451 template <class F, size_t... Is>
452 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) {
453   return f(std::integral_constant<size_t, Is>{}...);
454 }
455 
456 template <size_t N, class F>
457 constexpr auto index_apply(F f) {
458   return index_apply_impl(f, std::make_index_sequence<N>{});
459 }
460 
461 template <class Tuple, class F>
462 constexpr auto apply(F f, Tuple& t) {
463   return index_apply<std::tuple_size<Tuple>{}>(
464       [&](auto... Is) { return f(std::get<Is>(t)...); });
465 }
466 
467 template <class Tuple>
468 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) {
469   return index_apply<std::tuple_size<Tuple>{}>(
470       [&](auto... Is) { return m.unpack(std::get<Is>(t)...); });
471 }
472 
473 // Specialization for empty tuples.  No need to unpack if no arguments
474 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) {
475   return true;
476 }
477 
478 template <typename... Args>
479 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) {
480   return index_apply<std::tuple_size<std::tuple<Args...>>{}>(
481       [&](auto... Is) { return m.pack(std::get<Is>(t)...); });
482 }
483 
484 // Specialization for empty tuples.  No need to pack if no arguments
485 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {
486   return true;
487 }
488 
489 // Specialization for single types.  Used when callbacks simply return one value
490 template <typename Element>
491 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) {
492   return m.pack(t);
493 }
494 
495 #include <dbus/impl/message_iterator.ipp>
496 
497 #endif  // DBUS_MESSAGE_HPP
498