xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision a04118e8)
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                           const string& method_name) {
33     auto x = message(dbus_message_new_method_call(
34         destination.get_process_name().c_str(), destination.get_path().c_str(),
35         destination.get_interface().c_str(), method_name.c_str()));
36     dbus_message_unref(x.message_.get());
37     return x;
38   }
39 
40   /// Create a method return message
41   static message new_return(message& call) {
42     auto x = message(dbus_message_new_method_return(call));
43     dbus_message_unref(x.message_.get());
44     return x;
45   }
46 
47   /// Create an error message
48   static message new_error(message& call, const string& error_name,
49                            const string& error_message) {
50     auto x = message(dbus_message_new_error(call, error_name.c_str(),
51                                             error_message.c_str()));
52     dbus_message_unref(x.message_.get());
53     return x;
54   }
55 
56   /// Create a signal message
57   static message new_signal(const endpoint& origin, const string& signal_name) {
58     auto x = message(dbus_message_new_signal(origin.get_path().c_str(),
59                                              origin.get_interface().c_str(),
60                                              signal_name.c_str()));
61     dbus_message_unref(x.message_.get());
62     return x;
63   }
64 
65   message() = delete;
66 
67   message(DBusMessage* m) : message_(m) {}
68 
69   operator DBusMessage*() { return message_.get(); }
70 
71   operator const DBusMessage*() const { return message_.get(); }
72 
73   string get_path() const {
74     return sanitize(dbus_message_get_path(message_.get()));
75   }
76 
77   string get_interface() const {
78     return sanitize(dbus_message_get_interface(message_.get()));
79   }
80 
81   string get_member() const {
82     return sanitize(dbus_message_get_member(message_.get()));
83   }
84 
85   string get_type() const {
86     return sanitize(
87         dbus_message_type_to_string(dbus_message_get_type(message_.get())));
88   }
89 
90   string get_signature() const {
91     return sanitize(dbus_message_get_signature(message_.get()));
92   }
93 
94   string get_sender() const {
95     return sanitize(dbus_message_get_sender(message_.get()));
96   }
97 
98   string get_destination() const {
99     return sanitize(dbus_message_get_destination(message_.get()));
100   }
101 
102   uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
103 
104   message& set_serial(uint32 serial) {
105     dbus_message_set_serial(message_.get(), serial);
106     return *this;
107   }
108 
109   uint32 get_reply_serial() {
110     return dbus_message_get_reply_serial(message_.get());
111   }
112 
113   message& set_reply_serial(uint32 reply_serial) {
114     dbus_message_set_reply_serial(message_.get(), reply_serial);
115     return *this;
116   }
117 
118   struct packer {
119     impl::message_iterator iter_;
120     packer(message& m) { impl::message_iterator::init_append(m, iter_); }
121     packer(){};
122 
123     template <typename Element, typename... Args>
124     bool pack(const Element& e, const Args&... args) {
125       if (this->pack(e) == false) {
126         return false;
127       } else {
128         return pack(args...);
129       }
130     }
131 
132     template <typename Element>
133     typename boost::enable_if<is_fixed_type<Element>, bool>::type pack(
134         const Element& e) {
135       return iter_.append_basic(element<Element>::code, &e);
136     }
137 
138     // Specialization used to represent "dict" in dbus.
139     // TODO(ed) generalize for all "map like" types instead of using vector
140     template <typename Key, typename Value>
141     bool pack(const std::vector<std::pair<Key, Value>>& v) {
142       static const constexpr auto sig =
143           element_signature<typename std::decay<decltype(v)>::type>::code;
144 
145       static_assert(std::tuple_size<decltype(sig)>::value > 2,
146                     "Signature size must be greater than 2 characters long");
147       // Skip over the array part "a" of the signature to get the element
148       // signature.
149       // Open container expects JUST the portion after the "a" in the second arg
150       message::packer sub;
151       iter_.open_container(sig[0], &sig[1], sub.iter_);
152       for (auto& element : v) {
153         sub.pack(element);
154       }
155 
156       return iter_.close_container(sub.iter_);
157     }
158 
159     template <typename Element>
160     bool pack(const std::vector<Element>& v) {
161       message::packer sub;
162       static const constexpr auto signature =
163           element_signature<std::vector<Element>>::code;
164       static_assert(std::tuple_size<decltype(signature)>::value > 2,
165                     "Signature size must be greater than 2 characters long");
166       iter_.open_container(signature[0], &signature[1], sub.iter_);
167       for (auto& element : v) {
168         sub.pack(element);
169       }
170 
171       return iter_.close_container(sub.iter_);
172     }
173 
174     bool pack(const char* c) {
175       return iter_.append_basic(element<string>::code, &c);
176     }
177 
178     template <typename Key, typename Value>
179     bool pack(const std::pair<Key, Value> element) {
180       message::packer dict_entry;
181       if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) ==
182           false) {
183         return false;
184       }
185       if (dict_entry.pack(element.first) == false) {
186         return false;
187       };
188       if (dict_entry.pack(element.second) == false) {
189         return false;
190       };
191       return iter_.close_container(dict_entry.iter_);
192     }
193 
194     bool pack(const string& e) {
195       const char* c = e.c_str();
196       return pack(c);
197     }
198 
199     bool pack(const dbus_variant& v) {
200       // Get the dbus typecode  of the variant being packed
201       const char* type = boost::apply_visitor(
202           [&](auto val) {
203             static const constexpr auto sig =
204                 element_signature<decltype(val)>::code;
205             static_assert(
206                 std::tuple_size<decltype(sig)>::value == 2,
207                 "Element signature for dbus_variant too long.  Expected "
208                 "length of 1");
209             return &sig[0];
210           },
211           v);
212       message::packer sub;
213       iter_.open_container(element<dbus_variant>::code, type, sub.iter_);
214       boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v);
215       iter_.close_container(sub.iter_);
216 
217       return true;
218     }
219   };
220 
221   template <typename... Args>
222   bool pack(const Args&... args) {
223     return packer(*this).pack(args...);
224   }
225 
226   struct unpacker {
227     impl::message_iterator iter_;
228     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
229     unpacker() {}
230 
231     template <typename Element, typename... Args>
232     bool unpack(Element& e, Args&... args) {
233       if (unpack(e) == false) {
234         return false;
235       }
236       return unpack(args...);
237     }
238 
239     // Basic type unpack
240     template <typename Element>
241     typename boost::enable_if<is_fixed_type<Element>, bool>::type unpack(
242         Element& e) {
243       if (iter_.get_arg_type() != element<Element>::code) {
244         return false;
245       }
246       iter_.get_basic(&e);
247       // ignoring return code here, as we might hit last element, and don't
248       // really care because get_arg_type will return invalid if we call it
249       // after we're over the struct boundary
250       iter_.next();
251       return true;
252     }
253 
254     // std::string unpack specialization
255     bool unpack(string& s) {
256       if (iter_.get_arg_type() != element<string>::code) {
257         return false;
258       }
259       const char* c;
260       iter_.get_basic(&c);
261       s.assign(c);
262       iter_.next();
263       return true;
264     }
265 
266     // object_path unpack specialization
267     bool unpack(object_path& s) {
268       if (iter_.get_arg_type() != element<object_path>::code) {
269         return false;
270       }
271       const char* c;
272       iter_.get_basic(&c);
273       s.value.assign(c);
274       iter_.next();
275       return true;
276     }
277 
278     // object_path unpack specialization
279     bool unpack(signature& s) {
280       if (iter_.get_arg_type() != element<signature>::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     // variant unpack specialization
291     bool unpack(dbus_variant& v) {
292       if (iter_.get_arg_type() != element<dbus_variant>::code) {
293         return false;
294       }
295       message::unpacker sub;
296       iter_.recurse(sub.iter_);
297 
298       char arg_type = sub.iter_.get_arg_type();
299 
300       boost::mpl::for_each<dbus_variant::types>([&](auto t) {
301         if (arg_type == element<decltype(t)>::code) {
302           decltype(t) val_to_fill;
303           sub.unpack(val_to_fill);
304           v = val_to_fill;
305         }
306       });
307 
308       iter_.next();
309       return true;
310     }
311 
312     // dict entry unpack specialization
313     template <typename Key, typename Value>
314     bool unpack(std::pair<Key, Value>& v) {
315       auto this_code = iter_.get_arg_type();
316       // This can't use element<std::pair> because there is a difference between
317       // the dbus type code 'e' and the dbus signature code for dict entries
318       // '{'.  Element_signature will return the full signature, and will never
319       // return e, but we still want to type check it before recursing
320       if (this_code != DBUS_TYPE_DICT_ENTRY) {
321         return false;
322       }
323       message::unpacker sub;
324       iter_.recurse(sub.iter_);
325       if (!sub.unpack(v.first)) {
326         return false;
327       }
328       if (!sub.unpack(v.second)) {
329         return false;
330       }
331 
332       iter_.next();
333       return true;
334     }
335 
336     // dbus array / c++ vector unpack specialization
337     template <typename Element>
338     bool unpack(std::vector<Element>& s) {
339       auto top_level_arg_type = iter_.get_arg_type();
340       if (top_level_arg_type != element<std::vector<Element>>::code) {
341         return false;
342       }
343       message::unpacker sub;
344 
345       iter_.recurse(sub.iter_);
346       auto arg_type = sub.iter_.get_arg_type();
347       while (arg_type != DBUS_TYPE_INVALID) {
348         s.emplace_back();
349         if (!sub.unpack(s.back())) {
350           return false;
351         }
352         arg_type = sub.iter_.get_arg_type();
353       }
354       iter_.next();
355       return true;
356     }
357   };
358 
359   template <typename... Args>
360   bool unpack(Args&... args) {
361     return unpacker(*this).unpack(args...);
362   }
363 
364  private:
365   static std::string sanitize(const char* str) {
366     return (str == NULL) ? "(null)" : str;
367   }
368 };
369 
370 inline std::ostream& operator<<(std::ostream& os, const message& m) {
371   os << "type='" << m.get_type() << "',"
372      << "sender='" << m.get_sender() << "',"
373      << "interface='" << m.get_interface() << "',"
374      << "member='" << m.get_member() << "',"
375      << "path='" << m.get_path() << "',"
376      << "destination='" << m.get_destination() << "'";
377   return os;
378 }
379 
380 }  // namespace dbus
381 
382 #include <dbus/impl/message_iterator.ipp>
383 
384 #endif  // DBUS_MESSAGE_HPP
385