xref: /openbmc/boost-dbus/include/dbus/message.hpp (revision 0250ca19)
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 #include <boost/mpl/for_each.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   boost::intrusive_ptr<DBusMessage> message_;
27 
28  public:
29   /// Create a method call message
30   static message new_call(const endpoint& destination,
31                           const string& method_name) {
32     return dbus_message_new_method_call(
33         destination.get_process_name().c_str(), destination.get_path().c_str(),
34         destination.get_interface().c_str(), method_name.c_str());
35   }
36 
37   /// Create a method return message
38   static message new_return(message& call) {
39     return dbus_message_new_method_return(call);
40   }
41 
42   /// Create an error message
43   static message new_error(message& call, const string& error_name,
44                            const string& error_message) {
45     return dbus_message_new_error(call, error_name.c_str(),
46                                   error_message.c_str());
47   }
48 
49   /// Create a signal message
50   static message new_signal(const endpoint& origin, const string& signal_name) {
51     return dbus_message_new_signal(origin.get_path().c_str(),
52                                    origin.get_interface().c_str(),
53                                    signal_name.c_str());
54   }
55 
56   message() {}
57 
58   message(DBusMessage* m) : message_(dbus_message_ref(m)) {}
59 
60   operator DBusMessage*() { return message_.get(); }
61 
62   operator const DBusMessage*() const { return message_.get(); }
63 
64   string get_path() const {
65     return sanitize(dbus_message_get_path(message_.get()));
66   }
67 
68   string get_interface() const {
69     return sanitize(dbus_message_get_interface(message_.get()));
70   }
71 
72   string get_member() const {
73     return sanitize(dbus_message_get_member(message_.get()));
74   }
75 
76   string get_type() const {
77     return sanitize(
78         dbus_message_type_to_string(dbus_message_get_type(message_.get())));
79   }
80 
81   string get_signature() const {
82     return sanitize(dbus_message_get_signature(message_.get()));
83   }
84 
85   string get_sender() const {
86     return sanitize(dbus_message_get_sender(message_.get()));
87   }
88 
89   string get_destination() const {
90     return sanitize(dbus_message_get_destination(message_.get()));
91   }
92 
93   uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
94 
95   message& set_serial(uint32 serial) {
96     dbus_message_set_serial(message_.get(), serial);
97     return *this;
98   }
99 
100   uint32 get_reply_serial() {
101     return dbus_message_get_reply_serial(message_.get());
102   }
103 
104   message& set_reply_serial(uint32 reply_serial) {
105     dbus_message_set_reply_serial(message_.get(), reply_serial);
106     return *this;
107   }
108 
109   struct packer {
110     impl::message_iterator iter_;
111     packer(message& m) { impl::message_iterator::init_append(m, iter_); }
112     packer(){};
113     template <typename Element>
114     packer& pack(const Element& e) {
115       return *this << e;
116     }
117 
118     template <typename Element, typename... Args>
119     packer pack(const Element& e, const Args&... args) {
120       return this->pack(e).pack(args...);
121     }
122   };
123 
124   template <typename... Args>
125   packer pack(const Args&... args) {
126     return packer(*this).pack(args...);
127   }
128 
129   struct unpacker {
130     impl::message_iterator iter_;
131     unpacker(message& m) { impl::message_iterator::init(m, iter_); }
132     unpacker() {}
133 
134     template <typename Element>
135     unpacker& unpack(Element& e) {
136       return *this >> e;
137     }
138 
139     template <typename Element, typename... Args>
140     unpacker& unpack(Element& e, Args&... args) {
141       return unpack(e).unpack(args...);
142     }
143   };
144 
145   template <typename... Args>
146   unpacker& unpack(Args&... args) {
147     return unpacker(*this).unpack(args...);
148   }
149 
150  private:
151   static std::string sanitize(const char* str) {
152     return (str == NULL) ? "(null)" : str;
153   }
154 };
155 
156 template <typename Element>
157 message::packer operator<<(message m, const Element& e) {
158   return message::packer(m).pack(e);
159 }
160 
161 template <typename Element>
162 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
163 operator<<(message::packer& p, const Element& e) {
164   p.iter_.append_basic(element<Element>::code, &e);
165   return p;
166 }
167 
168 template <typename Key, typename Value>
169 message::packer& operator<<(message::packer& p,
170                             const std::vector<std::pair<Key, Value>>& v) {
171   message::packer sub;
172   char signature[] = {'{', element<Key>::code, element<Value>::code, '}', 0};
173 
174   p.iter_.open_container(DBUS_TYPE_ARRAY, signature, sub.iter_);
175   for (auto& element : v) {
176     sub << element;
177   }
178 
179   p.iter_.close_container(sub.iter_);
180   return p;
181 }
182 
183 template <typename Element>
184 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) {
185   message::packer sub;
186   char signature[] = {element<Element>::code, 0};
187   p.iter_.open_container(element<std::vector<Element>>::code, signature,
188                          sub.iter_);
189   for (auto& element : v) {
190     sub << element;
191   }
192 
193   p.iter_.close_container(sub.iter_);
194   return p;
195 }
196 
197 inline message::packer& operator<<(message::packer& p, const char* c) {
198   p.iter_.append_basic(element<string>::code, &c);
199   return p;
200 }
201 
202 template <typename Key, typename Value>
203 inline message::packer& operator<<(message::packer& p,
204                                    const std::pair<Key, Value> element) {
205   message::packer dict_entry;
206   p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_);
207   dict_entry << element.first;
208   dict_entry << element.second;
209   p.iter_.close_container(dict_entry.iter_);
210   return p;
211 }
212 
213 inline message::packer& operator<<(message::packer& p, const string& e) {
214   const char* c = e.c_str();
215   return p << c;
216 }
217 
218 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) {
219   // Get the dbus typecode  of the variant being packed
220   char type = boost::apply_visitor([&](auto val) {
221     return element<decltype(val)>::code;
222   }, v);
223   char signature[] = {type, 0};
224 
225   message::packer sub;
226   p.iter_.open_container(element<dbus_variant>::code, signature, sub.iter_);
227   boost::apply_visitor([&](auto val) { sub << val; }, v);
228   p.iter_.close_container(sub.iter_);
229 
230   return p;
231 }
232 
233 template <typename Element>
234 message::unpacker operator>>(message m, Element& e) {
235   return message::unpacker(m).unpack(e);
236 }
237 
238 template <typename Element>
239 typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type
240 operator>>(message::unpacker& u, Element& e) {
241   u.iter_.get_basic(&e);
242   u.iter_.next();
243   return u;
244 }
245 
246 inline message::unpacker& operator>>(message::unpacker& u, string& s) {
247   const char* c;
248   u.iter_.get_basic(&c);
249   s.assign(c);
250   u.iter_.next();
251   return u;
252 }
253 
254 inline message::unpacker& operator>>(message::unpacker& u, dbus_variant& v) {
255   message::unpacker sub;
256   u.iter_.recurse(sub.iter_);
257 
258   char arg_type = sub.iter_.get_arg_type();
259 
260   boost::mpl::for_each<dbus_variant::types>([&](auto t) {
261     if (arg_type == element<decltype(t)>::code){
262       decltype(t) val_to_fill;
263       sub >> val_to_fill;
264       v = val_to_fill;
265     }
266   });
267 
268   u.iter_.next();
269   return u;
270 }
271 
272 template <typename Key, typename Value>
273 inline message::unpacker& operator>>(message::unpacker& u,
274                                      std::pair<Key, Value>& v) {
275   message::unpacker sub;
276   u.iter_.recurse(sub.iter_);
277   sub >> v.first;
278   sub >> v.second;
279 
280   u.iter_.next();
281   return u;
282 }
283 
284 template <typename Element>
285 inline message::unpacker& operator>>(message::unpacker& u,
286                                      std::vector<Element>& s) {
287   message::unpacker sub;
288 
289   u.iter_.recurse(sub.iter_);
290   auto arg_type = sub.iter_.get_arg_type();
291   while (arg_type != DBUS_TYPE_INVALID) {
292     s.emplace_back();
293     sub >> s.back();
294     arg_type = sub.iter_.get_arg_type();
295   }
296   u.iter_.next();
297   return u;
298 }
299 
300 inline std::ostream& operator<<(std::ostream& os, const message& m) {
301   os << "type='" << m.get_type() << "',"
302      << "sender='" << m.get_sender() << "',"
303      << "interface='" << m.get_interface() << "',"
304      << "member='" << m.get_member() << "',"
305      << "path='" << m.get_path() << "',"
306      << "destination='" << m.get_destination() << "'";
307   return os;
308 }
309 
310 }  // namespace dbus
311 
312 #include <dbus/impl/message_iterator.ipp>
313 
314 #endif  // DBUS_MESSAGE_HPP
315