xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 458a9c10)
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   struct unpacker {
118     impl::message_iterator iter_;
119     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
120     unpacker() {}
121 
122     template <typename Element>
123     unpacker& unpack(Element& e) {
124       return *this >> e;
125     }
126   };
127 
128   template <typename Element>
129   packer pack(const Element& e) {
130     return packer(*this).pack(e);
131   }
132 
133   template <typename Element>
134   unpacker unpack(Element& e) {
135     return unpacker(*this).unpack(e);
136   }
137 
138  private:
139   static std::string sanitize(const char* str) {
140     return (str == NULL) ? "(null)" : str;
141   }
142 };
143 
144 template <typename Element>
145 message::packer operator<<(message m, const Element& e) {
146   return message::packer(m).pack(e);
147 }
148 
149 template <typename Element>
150 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
151 operator<<(message::packer& p, const Element& e) {
152   p.iter_.append_basic(element<Element>::code, &e);
153   return p;
154 }
155 
156 template <typename Key, typename Value>
157 message::packer& operator<<(message::packer& p,
158                             const std::vector<std::pair<Key, Value>>& v) {
159   message::packer sub;
160   char signature[] = {'{', element<Key>::code, element<Value>::code, '}', 0};
161 
162   p.iter_.open_container(DBUS_TYPE_ARRAY, signature, sub.iter_);
163   for (auto& element : v) {
164     sub << element;
165   }
166 
167   p.iter_.close_container(sub.iter_);
168   return p;
169 }
170 
171 template <typename Element>
172 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) {
173   message::packer sub;
174   char signature[] = {element<Element>::code, 0};
175   p.iter_.open_container(element<std::vector<Element>>::code, signature,
176                          sub.iter_);
177   for (auto& element : v) {
178     sub << element;
179   }
180 
181   p.iter_.close_container(sub.iter_);
182   return p;
183 }
184 
185 inline message::packer& operator<<(message::packer& p, const char* c) {
186   p.iter_.append_basic(element<string>::code, &c);
187   return p;
188 }
189 
190 template <typename Key, typename Value>
191 inline message::packer& operator<<(message::packer& p,
192                                    const std::pair<Key, Value> element) {
193   message::packer dict_entry;
194   p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_);
195   dict_entry << element.first;
196   dict_entry << element.second;
197   p.iter_.close_container(dict_entry.iter_);
198   return p;
199 }
200 
201 inline message::packer& operator<<(message::packer& p, const string& e) {
202   const char* c = e.c_str();
203   return p << c;
204 }
205 
206 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) {
207   message::packer sub;
208   char type = 0;
209   // TODO(ed) there must be a better (more typesafe) way to do this
210   switch (v.which()) {
211     case 0:
212       type = element<std::string>::code;
213       break;
214     case 1:
215       type = element<bool>::code;
216       break;
217     case 2:
218       type = element<byte>::code;
219       break;
220     case 3:
221       type = element<int16>::code;
222       break;
223     case 4:
224       type = element<uint16>::code;
225       break;
226     case 5:
227       type = element<int32>::code;
228       break;
229     case 6:
230       type = element<uint32>::code;
231       break;
232     case 7:
233       type = element<int64>::code;
234       break;
235     case 8:
236       type = element<uint64>::code;
237       break;
238     case 9:
239       type = element<double>::code;
240       break;
241 
242     default:
243       // TODO(ed) throw exception
244       break;
245   }
246   char signature[] = {type, 0};
247 
248   p.iter_.open_container(element<dbus_variant>::code, signature, sub.iter_);
249   boost::apply_visitor([&](auto val) { sub << val; }, v);
250   // sub << element;
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   auto arg_type = sub.iter_.get_arg_type();
282   // sub.iter_.get_basic(&c);
283   // Todo(ed) find a better way to do this lookup table
284   switch (arg_type) {
285     case element<std::string>::code: {
286       std::string s;
287       sub >> s;
288       v = s;
289     } break;
290     case element<bool>::code: {
291       bool b;
292       sub >> b;
293       v = b;
294     } break;
295     case element<byte>::code: {
296       byte b;
297       sub >> b;
298       v = b;
299     } break;
300     case element<int16>::code: {
301       int16 b;
302       sub >> b;
303       v = b;
304     } break;
305     case element<uint16>::code: {
306       uint16 b;
307       sub >> b;
308       v = b;
309     } break;
310     case element<int32>::code: {
311       int32 b;
312       sub >> b;
313       v = b;
314     } break;
315     case element<uint32>::code: {
316       uint32 b;
317       sub >> b;
318       v = b;
319     } break;
320     case element<int64>::code: {
321       int64 b;
322       sub >> b;
323       v = b;
324     } break;
325     case element<uint64>::code: {
326       uint64 b;
327       sub >> b;
328       v = b;
329     } break;
330     case element<double>::code: {
331       double b;
332       sub >> b;
333       v = b;
334     } break;
335 
336     default:
337       // TODO(ed) throw exception
338       break;
339   }
340   u.iter_.next();
341   return u;
342 }
343 
344 template <typename Key, typename Value>
345 inline message::unpacker& operator>>(message::unpacker& u,
346                                      std::pair<Key, Value>& v) {
347   message::unpacker sub;
348   u.iter_.recurse(sub.iter_);
349   sub >> v.first;
350   sub >> v.second;
351 
352   u.iter_.next();
353   return u;
354 }
355 
356 template <typename Element>
357 inline message::unpacker& operator>>(message::unpacker& u,
358                                      std::vector<Element>& s) {
359   message::unpacker sub;
360 
361   u.iter_.recurse(sub.iter_);
362   auto arg_type = sub.iter_.get_arg_type();
363   while (arg_type != DBUS_TYPE_INVALID) {
364     s.emplace_back();
365     sub >> s.back();
366     arg_type = sub.iter_.get_arg_type();
367   }
368   u.iter_.next();
369   return u;
370 }
371 
372 inline std::ostream& operator<<(std::ostream& os, const message& m) {
373   os << "type='" << m.get_type() << "',"
374      << "sender='" << m.get_sender() << "',"
375      << "interface='" << m.get_interface() << "',"
376      << "member='" << m.get_member() << "',"
377      << "path='" << m.get_path() << "',"
378      << "destination='" << m.get_destination() << "'";
379   return os;
380 }
381 
382 }  // namespace dbus
383 
384 #include <dbus/impl/message_iterator.ipp>
385 
386 #endif  // DBUS_MESSAGE_HPP
387