xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 0d6f56d2)
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/utility/enable_if.hpp>
17 
18 inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
19 
20 inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); }
21 
22 namespace dbus {
23 
24 class message {
25   boost::intrusive_ptr<DBusMessage> message_;
26 
27  public:
28   /// Create a method call message
29   static message new_call(const endpoint& destination,
30                           const string& method_name) {
31     return dbus_message_new_method_call(
32         destination.get_process_name().c_str(), destination.get_path().c_str(),
33         destination.get_interface().c_str(), method_name.c_str());
34   }
35 
36   /// Create a method return message
37   static message new_return(message& call) {
38     return dbus_message_new_method_return(call);
39   }
40 
41   /// Create an error message
42   static message new_error(message& call, const string& error_name,
43                            const string& error_message) {
44     return dbus_message_new_error(call, error_name.c_str(),
45                                   error_message.c_str());
46   }
47 
48   /// Create a signal message
49   static message new_signal(const endpoint& origin, const string& signal_name) {
50     return dbus_message_new_signal(origin.get_path().c_str(),
51                                    origin.get_interface().c_str(),
52                                    signal_name.c_str());
53   }
54 
55   message() {}
56 
57   message(DBusMessage* m) : message_(dbus_message_ref(m)) {}
58 
59   operator DBusMessage*() { return message_.get(); }
60 
61   operator const DBusMessage*() const { return message_.get(); }
62 
63   string get_path() const {
64     return sanitize(dbus_message_get_path(message_.get()));
65   }
66 
67   string get_interface() const {
68     return sanitize(dbus_message_get_interface(message_.get()));
69   }
70 
71   string get_member() const {
72     return sanitize(dbus_message_get_member(message_.get()));
73   }
74 
75   string get_type() const {
76     return sanitize(
77         dbus_message_type_to_string(dbus_message_get_type(message_.get())));
78   }
79 
80   string get_signature() const {
81     return sanitize(dbus_message_get_signature(message_.get()));
82   }
83 
84   string get_sender() const {
85     return sanitize(dbus_message_get_sender(message_.get()));
86   }
87 
88   string get_destination() const {
89     return sanitize(dbus_message_get_destination(message_.get()));
90   }
91 
92   uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
93 
94   message& set_serial(uint32 serial) {
95     dbus_message_set_serial(message_.get(), serial);
96     return *this;
97   }
98 
99   uint32 get_reply_serial() {
100     return dbus_message_get_reply_serial(message_.get());
101   }
102 
103   message& set_reply_serial(uint32 reply_serial) {
104     dbus_message_set_reply_serial(message_.get(), reply_serial);
105     return *this;
106   }
107 
108   struct packer {
109     impl::message_iterator iter_;
110     packer(message& m) { impl::message_iterator::init_append(m, iter_); }
111     packer(){};
112     template <typename Element>
113     packer& pack(const Element& e) {
114       return *this << e;
115     }
116   };
117 
118   template <typename Element>
119   packer pack(const Element& e) {
120     return packer(*this).pack(e);
121   }
122 
123   template <typename Element, typename... Args>
124   packer pack(const Element& e, Args&... args) {
125     return packer(*this).pack(e).pack(args...);
126   }
127 
128   struct unpacker {
129     impl::message_iterator iter_;
130     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
131     unpacker() {}
132 
133     template <typename Element>
134     unpacker& unpack(Element& e) {
135       return *this >> e;
136     }
137   };
138 
139   template <typename Element>
140   unpacker unpack(Element& e) {
141     return unpacker(*this).unpack(e);
142   }
143 
144   template <typename Element, typename... Args>
145   unpacker& unpack(Element& e, Args&... args) {
146     return unpack(e).unpack(args...);
147   }
148 
149  private:
150   static std::string sanitize(const char* str) {
151     return (str == NULL) ? "(null)" : str;
152   }
153 };
154 
155 template <typename Element>
156 message::packer operator<<(message m, const Element& e) {
157   return message::packer(m).pack(e);
158 }
159 
160 template <typename Element>
161 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
162 operator<<(message::packer& p, const Element& e) {
163   p.iter_.append_basic(element<Element>::code, &e);
164   return p;
165 }
166 
167 template <typename Key, typename Value>
168 message::packer& operator<<(message::packer& p,
169                             const std::vector<std::pair<Key, Value>>& v) {
170   message::packer sub;
171   char signature[] = {'{', element<Key>::code, element<Value>::code, '}', 0};
172 
173   p.iter_.open_container(DBUS_TYPE_ARRAY, signature, sub.iter_);
174   for (auto& element : v) {
175     sub << element;
176   }
177 
178   p.iter_.close_container(sub.iter_);
179   return p;
180 }
181 
182 template <typename Element>
183 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) {
184   message::packer sub;
185   char signature[] = {element<Element>::code, 0};
186   p.iter_.open_container(element<std::vector<Element>>::code, signature,
187                          sub.iter_);
188   for (auto& element : v) {
189     sub << element;
190   }
191 
192   p.iter_.close_container(sub.iter_);
193   return p;
194 }
195 
196 inline message::packer& operator<<(message::packer& p, const char* c) {
197   p.iter_.append_basic(element<string>::code, &c);
198   return p;
199 }
200 
201 template <typename Key, typename Value>
202 inline message::packer& operator<<(message::packer& p,
203                                    const std::pair<Key, Value> element) {
204   message::packer dict_entry;
205   p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_);
206   dict_entry << element.first;
207   dict_entry << element.second;
208   p.iter_.close_container(dict_entry.iter_);
209   return p;
210 }
211 
212 inline message::packer& operator<<(message::packer& p, const string& e) {
213   const char* c = e.c_str();
214   return p << c;
215 }
216 
217 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) {
218   message::packer sub;
219   char type = 0;
220   // TODO(ed) there must be a better (more typesafe) way to do this
221   switch (v.which()) {
222     case 0:
223       type = element<std::string>::code;
224       break;
225     case 1:
226       type = element<bool>::code;
227       break;
228     case 2:
229       type = element<byte>::code;
230       break;
231     case 3:
232       type = element<int16>::code;
233       break;
234     case 4:
235       type = element<uint16>::code;
236       break;
237     case 5:
238       type = element<int32>::code;
239       break;
240     case 6:
241       type = element<uint32>::code;
242       break;
243     case 7:
244       type = element<int64>::code;
245       break;
246     case 8:
247       type = element<uint64>::code;
248       break;
249     case 9:
250       type = element<double>::code;
251       break;
252 
253     default:
254       // TODO(ed) throw exception
255       break;
256   }
257   char signature[] = {type, 0};
258 
259   p.iter_.open_container(element<dbus_variant>::code, signature, sub.iter_);
260   boost::apply_visitor([&](auto val) { sub << val; }, v);
261   // sub << element;
262   p.iter_.close_container(sub.iter_);
263 
264   return p;
265 }
266 
267 template <typename Element>
268 message::unpacker operator>>(message m, Element& e) {
269   return message::unpacker(m).unpack(e);
270 }
271 
272 template <typename Element>
273 typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type
274 operator>>(message::unpacker& u, Element& e) {
275   u.iter_.get_basic(&e);
276   u.iter_.next();
277   return u;
278 }
279 
280 inline message::unpacker& operator>>(message::unpacker& u, string& s) {
281   const char* c;
282   u.iter_.get_basic(&c);
283   s.assign(c);
284   u.iter_.next();
285   return u;
286 }
287 
288 inline message::unpacker& operator>>(message::unpacker& u, dbus_variant& v) {
289   message::unpacker sub;
290   u.iter_.recurse(sub.iter_);
291 
292   auto arg_type = sub.iter_.get_arg_type();
293   // sub.iter_.get_basic(&c);
294   // Todo(ed) find a better way to do this lookup table
295   switch (arg_type) {
296     case element<std::string>::code: {
297       std::string s;
298       sub >> s;
299       v = s;
300     } break;
301     case element<bool>::code: {
302       bool b;
303       sub >> b;
304       v = b;
305     } break;
306     case element<byte>::code: {
307       byte b;
308       sub >> b;
309       v = b;
310     } break;
311     case element<int16>::code: {
312       int16 b;
313       sub >> b;
314       v = b;
315     } break;
316     case element<uint16>::code: {
317       uint16 b;
318       sub >> b;
319       v = b;
320     } break;
321     case element<int32>::code: {
322       int32 b;
323       sub >> b;
324       v = b;
325     } break;
326     case element<uint32>::code: {
327       uint32 b;
328       sub >> b;
329       v = b;
330     } break;
331     case element<int64>::code: {
332       int64 b;
333       sub >> b;
334       v = b;
335     } break;
336     case element<uint64>::code: {
337       uint64 b;
338       sub >> b;
339       v = b;
340     } break;
341     case element<double>::code: {
342       double b;
343       sub >> b;
344       v = b;
345     } break;
346 
347     default:
348       // TODO(ed) throw exception
349       break;
350   }
351   u.iter_.next();
352   return u;
353 }
354 
355 template <typename Key, typename Value>
356 inline message::unpacker& operator>>(message::unpacker& u,
357                                      std::pair<Key, Value>& v) {
358   message::unpacker sub;
359   u.iter_.recurse(sub.iter_);
360   sub >> v.first;
361   sub >> v.second;
362 
363   u.iter_.next();
364   return u;
365 }
366 
367 template <typename Element>
368 inline message::unpacker& operator>>(message::unpacker& u,
369                                      std::vector<Element>& s) {
370   message::unpacker sub;
371 
372   u.iter_.recurse(sub.iter_);
373   auto arg_type = sub.iter_.get_arg_type();
374   while (arg_type != DBUS_TYPE_INVALID) {
375     s.emplace_back();
376     sub >> s.back();
377     arg_type = sub.iter_.get_arg_type();
378   }
379   u.iter_.next();
380   return u;
381 }
382 
383 inline std::ostream& operator<<(std::ostream& os, const message& m) {
384   os << "type='" << m.get_type() << "',"
385      << "sender='" << m.get_sender() << "',"
386      << "interface='" << m.get_interface() << "',"
387      << "member='" << m.get_member() << "',"
388      << "path='" << m.get_path() << "',"
389      << "destination='" << m.get_destination() << "'";
390   return os;
391 }
392 
393 }  // namespace dbus
394 
395 #include <dbus/impl/message_iterator.ipp>
396 
397 #endif  // DBUS_MESSAGE_HPP
398