xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 377e76ab)
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     template <typename Element>
123     packer& pack(const Element& e) {
124       return *this << e;
125     }
126 
127     template <typename Element, typename... Args>
128     packer pack(const Element& e, const Args&... args) {
129       return this->pack(e).pack(args...);
130     }
131   };
132 
133   template <typename... Args>
134   packer pack(const Args&... args) {
135     return packer(*this).pack(args...);
136   }
137 
138   struct unpacker {
139     impl::message_iterator iter_;
140     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
141     unpacker() {}
142 
143     template <typename Element>
144     unpacker& unpack(Element& e) {
145       return *this >> e;
146     }
147 
148     template <typename Element, typename... Args>
149     unpacker& unpack(Element& e, Args&... args) {
150       return unpack(e).unpack(args...);
151     }
152   };
153 
154   template <typename... Args>
155   unpacker& unpack(Args&... args) {
156     return unpacker(*this).unpack(args...);
157   }
158 
159  private:
160   static std::string sanitize(const char* str) {
161     return (str == NULL) ? "(null)" : str;
162   }
163 };
164 
165 template <typename Element>
166 message::packer operator<<(message m, const Element& e) {
167   return message::packer(m).pack(e);
168 }
169 
170 template <typename Element>
171 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
172 operator<<(message::packer& p, const Element& e) {
173   p.iter_.append_basic(element<Element>::code, &e);
174   return p;
175 }
176 
177 // Specialization used to represent "dict" in dbus.
178 // TODO(ed) generalize for all "map like" types instead of using vector
179 template <typename Key, typename Value>
180 message::packer& operator<<(message::packer& p,
181                             const std::vector<std::pair<Key, Value>>& v) {
182   message::packer sub;
183   static const constexpr auto sig =
184       element_signature<std::vector<std::pair<Key, Value>>>::code;
185   static_assert(std::tuple_size<decltype(sig)>::value > 2,
186                 "Signature size must be greater than 2 characters long");
187   // Skip over the array part "a" of the signature to get the element signature.
188   // Open container expects JUST the portion after the "a"
189   p.iter_.open_container(sig[0], &sig[1], sub.iter_);
190   for (auto& element : v) {
191     sub << element;
192   }
193 
194   p.iter_.close_container(sub.iter_);
195   return p;
196 }
197 
198 template <typename Element>
199 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) {
200   message::packer sub;
201   static const constexpr auto signature =
202       element_signature<std::vector<Element>>::code;
203   static_assert(std::tuple_size<decltype(signature)>::value > 2,
204                 "Signature size must be greater than 2 characters long");
205   p.iter_.open_container(signature[0], &signature[1], sub.iter_);
206   for (auto& element : v) {
207     sub << element;
208   }
209 
210   p.iter_.close_container(sub.iter_);
211   return p;
212 }
213 
214 inline message::packer& operator<<(message::packer& p, const char* c) {
215   p.iter_.append_basic(element<string>::code, &c);
216   return p;
217 }
218 
219 template <typename Key, typename Value>
220 inline message::packer& operator<<(message::packer& p,
221                                    const std::pair<Key, Value> element) {
222   message::packer dict_entry;
223   p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_);
224   dict_entry << element.first;
225   dict_entry << element.second;
226   p.iter_.close_container(dict_entry.iter_);
227   return p;
228 }
229 
230 inline message::packer& operator<<(message::packer& p, const string& e) {
231   const char* c = e.c_str();
232   return p << c;
233 }
234 
235 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) {
236   // Get the dbus typecode  of the variant being packed
237   const char* type = boost::apply_visitor(
238       [&](auto val) {
239         static const constexpr auto sig =
240             element_signature<decltype(val)>::code;
241         static_assert(std::tuple_size<decltype(sig)>::value == 2,
242                       "Element signature for dbus_variant too long.  Expected "
243                       "length of 1");
244         return &sig[0];
245       },
246       v);
247 
248   message::packer sub;
249   p.iter_.open_container(element<dbus_variant>::code, type, sub.iter_);
250   boost::apply_visitor([&](auto val) { sub << val; }, v);
251   p.iter_.close_container(sub.iter_);
252 
253   return p;
254 }
255 
256 template <typename Element>
257 message::unpacker operator>>(message m, Element& e) {
258   return message::unpacker(m).unpack(e);
259 }
260 
261 template <typename Element>
262 typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type
263 operator>>(message::unpacker& u, Element& e) {
264   u.iter_.get_basic(&e);
265   u.iter_.next();
266   return u;
267 }
268 
269 inline message::unpacker& operator>>(message::unpacker& u, string& s) {
270   const char* c;
271   u.iter_.get_basic(&c);
272   s.assign(c);
273   u.iter_.next();
274   return u;
275 }
276 
277 inline message::unpacker& operator>>(message::unpacker& u, dbus_variant& v) {
278   message::unpacker sub;
279   u.iter_.recurse(sub.iter_);
280 
281   char arg_type = sub.iter_.get_arg_type();
282 
283   boost::mpl::for_each<dbus_variant::types>([&](auto t) {
284     if (arg_type == element<decltype(t)>::code) {
285       decltype(t) val_to_fill;
286       sub >> val_to_fill;
287       v = val_to_fill;
288     }
289   });
290 
291   u.iter_.next();
292   return u;
293 }
294 
295 template <typename Key, typename Value>
296 inline message::unpacker& operator>>(message::unpacker& u,
297                                      std::pair<Key, Value>& v) {
298   message::unpacker sub;
299   u.iter_.recurse(sub.iter_);
300   sub >> v.first;
301   sub >> v.second;
302 
303   u.iter_.next();
304   return u;
305 }
306 
307 template <typename Element>
308 inline message::unpacker& operator>>(message::unpacker& u,
309                                      std::vector<Element>& s) {
310   message::unpacker sub;
311 
312   u.iter_.recurse(sub.iter_);
313   auto arg_type = sub.iter_.get_arg_type();
314   while (arg_type != DBUS_TYPE_INVALID) {
315     s.emplace_back();
316     sub >> s.back();
317     arg_type = sub.iter_.get_arg_type();
318   }
319   u.iter_.next();
320   return u;
321 }
322 
323 inline std::ostream& operator<<(std::ostream& os, const message& m) {
324   os << "type='" << m.get_type() << "',"
325      << "sender='" << m.get_sender() << "',"
326      << "interface='" << m.get_interface() << "',"
327      << "member='" << m.get_member() << "',"
328      << "path='" << m.get_path() << "',"
329      << "destination='" << m.get_destination() << "'";
330   return os;
331 }
332 
333 }  // namespace dbus
334 
335 #include <dbus/impl/message_iterator.ipp>
336 
337 #endif  // DBUS_MESSAGE_HPP
338