• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This file provides a way to call D-Bus methods on objects in remote processes
6 // as if they were native C++ function calls.
7 
8 // CallMethodAndBlock (along with CallMethodAndBlockWithTimeout) lets you call
9 // a D-Bus method synchronously and pass all the required parameters as C++
10 // function arguments. CallMethodAndBlock relies on automatic C++ to D-Bus data
11 // serialization implemented in brillo/dbus/data_serialization.h.
12 // CallMethodAndBlock invokes the D-Bus method and returns the Response.
13 
14 // The method call response should be parsed with ExtractMethodCallResults().
15 // The method takes an optional list of pointers to the expected return values
16 // of the D-Bus method.
17 
18 // CallMethod and CallMethodWithTimeout are similar to CallMethodAndBlock but
19 // make the calls asynchronously. They take two callbacks: one for successful
20 // method invocation and the second is for error conditions.
21 
22 // Here is an example of synchronous calls:
23 // Call "std::string MyInterface::MyMethod(int, double)" over D-Bus:
24 
25 //  using brillo::dbus_utils::CallMethodAndBlock;
26 //  using brillo::dbus_utils::ExtractMethodCallResults;
27 //
28 //  brillo::ErrorPtr error;
29 //  auto resp = CallMethodAndBlock(obj,
30 //                                 "org.chromium.MyService.MyInterface",
31 //                                 "MyMethod",
32 //                                 &error,
33 //                                 2, 8.7);
34 //  std::string return_value;
35 //  if (resp && ExtractMethodCallResults(resp.get(), &error, &return_value)) {
36 //    // Use the |return_value|.
37 //  } else {
38 //    // An error occurred. Use |error| to get details.
39 //  }
40 
41 // And here is how to call D-Bus methods asynchronously:
42 // Call "std::string MyInterface::MyMethod(int, double)" over D-Bus:
43 
44 //  using brillo::dbus_utils::CallMethod;
45 //  using brillo::dbus_utils::ExtractMethodCallResults;
46 //
47 //  void OnSuccess(const std::string& return_value) {
48 //    // Use the |return_value|.
49 //  }
50 //
51 //  void OnError(brillo::Error* error) {
52 //    // An error occurred. Use |error| to get details.
53 //  }
54 //
55 //  brillo::dbus_utils::CallMethod(obj,
56 //                                   "org.chromium.MyService.MyInterface",
57 //                                   "MyMethod",
58 //                                   base::Bind(OnSuccess),
59 //                                   base::Bind(OnError),
60 //                                   2, 8.7);
61 
62 #ifndef LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_
63 #define LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_
64 
65 #include <memory>
66 #include <string>
67 #include <tuple>
68 
69 #include <base/bind.h>
70 #include <base/files/scoped_file.h>
71 #include <brillo/dbus/dbus_param_reader.h>
72 #include <brillo/dbus/dbus_param_writer.h>
73 #include <brillo/dbus/file_descriptor.h>
74 #include <brillo/dbus/utils.h>
75 #include <brillo/errors/error.h>
76 #include <brillo/errors/error_codes.h>
77 #include <brillo/brillo_export.h>
78 #include <dbus/message.h>
79 #include <dbus/object_proxy.h>
80 
81 namespace brillo {
82 namespace dbus_utils {
83 
84 // A helper method to dispatch a blocking D-Bus method call. Can specify
85 // zero or more method call arguments in |args| which will be sent over D-Bus.
86 // This method sends a D-Bus message and blocks for a time period specified
87 // in |timeout_ms| while waiting for a reply. The time out is in milliseconds or
88 // -1 (DBUS_TIMEOUT_USE_DEFAULT) for default, or DBUS_TIMEOUT_INFINITE for no
89 // timeout. If a timeout occurs, the response object contains an error object
90 // with DBUS_ERROR_NO_REPLY error code (those constants come from libdbus
91 // [dbus/dbus.h]).
92 // Returns a dbus::Response object on success. On failure, returns nullptr and
93 // fills in additional error details into the |error| object.
94 template<typename... Args>
CallMethodAndBlockWithTimeout(int timeout_ms,dbus::ObjectProxy * object,const std::string & interface_name,const std::string & method_name,ErrorPtr * error,const Args &...args)95 inline std::unique_ptr<dbus::Response> CallMethodAndBlockWithTimeout(
96     int timeout_ms,
97     dbus::ObjectProxy* object,
98     const std::string& interface_name,
99     const std::string& method_name,
100     ErrorPtr* error,
101     const Args&... args) {
102   dbus::MethodCall method_call(interface_name, method_name);
103   // Add method arguments to the message buffer.
104   dbus::MessageWriter writer(&method_call);
105   DBusParamWriter::Append(&writer, args...);
106   dbus::ScopedDBusError dbus_error;
107   auto response = object->CallMethodAndBlockWithErrorDetails(
108       &method_call, timeout_ms, &dbus_error);
109   if (!response) {
110     if (dbus_error.is_set()) {
111       Error::AddTo(error,
112                    FROM_HERE,
113                    errors::dbus::kDomain,
114                    dbus_error.name(),
115                    dbus_error.message());
116     } else {
117       Error::AddToPrintf(error,
118                          FROM_HERE,
119                          errors::dbus::kDomain,
120                          DBUS_ERROR_FAILED,
121                          "Failed to call D-Bus method: %s.%s",
122                          interface_name.c_str(),
123                          method_name.c_str());
124     }
125   }
126   return response;
127 }
128 
129 // Same as CallMethodAndBlockWithTimeout() but uses a default timeout value.
130 template<typename... Args>
CallMethodAndBlock(dbus::ObjectProxy * object,const std::string & interface_name,const std::string & method_name,ErrorPtr * error,const Args &...args)131 inline std::unique_ptr<dbus::Response> CallMethodAndBlock(
132     dbus::ObjectProxy* object,
133     const std::string& interface_name,
134     const std::string& method_name,
135     ErrorPtr* error,
136     const Args&... args) {
137   return CallMethodAndBlockWithTimeout(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
138                                        object,
139                                        interface_name,
140                                        method_name,
141                                        error,
142                                        args...);
143 }
144 
145 namespace internal {
146 // In order to support non-copyable file descriptor types, we have this
147 // internal::HackMove() helper function that does really nothing for normal
148 // types but uses Pass() for file descriptors so we can move them out from
149 // the temporaries created inside DBusParamReader<...>::Invoke().
150 // If only libchrome supported real rvalues so we can just do std::move() and
151 // be done with it.
152 template <typename T>
HackMove(const T & val)153 inline const T& HackMove(const T& val) {
154   return val;
155 }
156 
157 // Even though |val| here is passed as const&, the actual value is created
158 // inside DBusParamReader<...>::Invoke() and is temporary in nature, so it is
159 // safe to move the file descriptor out of |val|. That's why we are doing
160 // const_cast here. It is a bit hacky, but there is no negative side effects.
HackMove(const base::ScopedFD & val)161 inline base::ScopedFD HackMove(const base::ScopedFD& val) {
162   return std::move(const_cast<base::ScopedFD&>(val));
163 }
HackMove(const FileDescriptor & val)164 inline FileDescriptor HackMove(const FileDescriptor& val) {
165   return std::move(const_cast<FileDescriptor&>(val));
166 }
167 }  // namespace internal
168 
169 // Extracts the parameters of |ResultTypes...| types from the message reader
170 // and puts the values in the resulting |tuple|. Returns false on error and
171 // provides additional error details in |error| object.
172 template<typename... ResultTypes>
ExtractMessageParametersAsTuple(dbus::MessageReader * reader,ErrorPtr * error,std::tuple<ResultTypes...> * val_tuple)173 inline bool ExtractMessageParametersAsTuple(
174     dbus::MessageReader* reader,
175     ErrorPtr* error,
176     std::tuple<ResultTypes...>* val_tuple) {
177   auto callback = [val_tuple](const ResultTypes&... params) {
178     *val_tuple = std::forward_as_tuple(params...);
179   };
180   return DBusParamReader<false, ResultTypes...>::Invoke(
181       callback, reader, error);
182 }
183 // Overload of ExtractMessageParametersAsTuple to handle reference types in
184 // tuples created with std::tie().
185 template<typename... ResultTypes>
ExtractMessageParametersAsTuple(dbus::MessageReader * reader,ErrorPtr * error,std::tuple<ResultTypes &...> * ref_tuple)186 inline bool ExtractMessageParametersAsTuple(
187     dbus::MessageReader* reader,
188     ErrorPtr* error,
189     std::tuple<ResultTypes&...>* ref_tuple) {
190   auto callback = [ref_tuple](const ResultTypes&... params) {
191     *ref_tuple = std::forward_as_tuple(params...);
192   };
193   return DBusParamReader<false, ResultTypes...>::Invoke(
194       callback, reader, error);
195 }
196 
197 // A helper method to extract a list of values from a message buffer.
198 // The function will return false and provide detailed error information on
199 // failure. It fails if the D-Bus message buffer (represented by the |reader|)
200 // contains too many, too few parameters or the parameters are of wrong types
201 // (signatures).
202 // The usage pattern is as follows:
203 //
204 //  int32_t data1;
205 //  std::string data2;
206 //  ErrorPtr error;
207 //  if (ExtractMessageParameters(reader, &error, &data1, &data2)) { ... }
208 //
209 // The above example extracts an Int32 and a String from D-Bus message buffer.
210 template<typename... ResultTypes>
ExtractMessageParameters(dbus::MessageReader * reader,ErrorPtr * error,ResultTypes * ...results)211 inline bool ExtractMessageParameters(dbus::MessageReader* reader,
212                                      ErrorPtr* error,
213                                      ResultTypes*... results) {
214   auto ref_tuple = std::tie(*results...);
215   return ExtractMessageParametersAsTuple<ResultTypes...>(
216       reader, error, &ref_tuple);
217 }
218 
219 // Convenient helper method to extract return value(s) of a D-Bus method call.
220 // |results| must be zero or more pointers to data expected to be returned
221 // from the method called. If an error occurs, returns false and provides
222 // additional details in |error| object.
223 //
224 // It is OK to call this method even if the D-Bus method doesn't expect
225 // any return values. Just do not specify any output |results|. In this case,
226 // ExtractMethodCallResults() will verify that the method didn't return any
227 // data in the |message|.
228 template<typename... ResultTypes>
ExtractMethodCallResults(dbus::Message * message,ErrorPtr * error,ResultTypes * ...results)229 inline bool ExtractMethodCallResults(dbus::Message* message,
230                                      ErrorPtr* error,
231                                      ResultTypes*... results) {
232   CHECK(message) << "Unable to extract parameters from a NULL message.";
233 
234   dbus::MessageReader reader(message);
235   if (message->GetMessageType() == dbus::Message::MESSAGE_ERROR) {
236     std::string error_message;
237     if (ExtractMessageParameters(&reader, error, &error_message))
238       AddDBusError(error, message->GetErrorName(), error_message);
239     return false;
240   }
241   return ExtractMessageParameters(&reader, error, results...);
242 }
243 
244 //////////////////////////////////////////////////////////////////////////////
245 // Asynchronous method invocation support
246 
247 using AsyncErrorCallback = base::Callback<void(Error* error)>;
248 
249 // A helper function that translates dbus::ErrorResponse response
250 // from D-Bus into brillo::Error* and invokes the |callback|.
251 void BRILLO_EXPORT TranslateErrorResponse(const AsyncErrorCallback& callback,
252                                             dbus::ErrorResponse* resp);
253 
254 // A helper function that translates dbus::Response from D-Bus into
255 // a list of C++ values passed as parameters to |success_callback|. If the
256 // response message doesn't have the correct number of parameters, or they
257 // are of wrong types, an error is sent to |error_callback|.
258 template<typename... OutArgs>
TranslateSuccessResponse(const base::Callback<void (OutArgs...)> & success_callback,const AsyncErrorCallback & error_callback,dbus::Response * resp)259 void TranslateSuccessResponse(
260     const base::Callback<void(OutArgs...)>& success_callback,
261     const AsyncErrorCallback& error_callback,
262     dbus::Response* resp) {
263   auto callback = [&success_callback](const OutArgs&... params) {
264     if (!success_callback.is_null()) {
265       success_callback.Run(params...);
266     }
267   };
268   ErrorPtr error;
269   dbus::MessageReader reader(resp);
270   if (!DBusParamReader<false, OutArgs...>::Invoke(callback, &reader, &error) &&
271       !error_callback.is_null()) {
272     error_callback.Run(error.get());
273   }
274 }
275 
276 // A helper method to dispatch a non-blocking D-Bus method call. Can specify
277 // zero or more method call arguments in |params| which will be sent over D-Bus.
278 // This method sends a D-Bus message and returns immediately.
279 // When the remote method returns successfully, the success callback is
280 // invoked with the return value(s), if any.
281 // On error, the error callback is called. Note, the error callback can be
282 // called synchronously (before CallMethodWithTimeout returns) if there was
283 // a problem invoking a method (e.g. object or method doesn't exist).
284 // If the response is not received within |timeout_ms|, an error callback is
285 // called with DBUS_ERROR_NO_REPLY error code.
286 template<typename... InArgs, typename... OutArgs>
CallMethodWithTimeout(int timeout_ms,dbus::ObjectProxy * object,const std::string & interface_name,const std::string & method_name,const base::Callback<void (OutArgs...)> & success_callback,const AsyncErrorCallback & error_callback,const InArgs &...params)287 inline void CallMethodWithTimeout(
288     int timeout_ms,
289     dbus::ObjectProxy* object,
290     const std::string& interface_name,
291     const std::string& method_name,
292     const base::Callback<void(OutArgs...)>& success_callback,
293     const AsyncErrorCallback& error_callback,
294     const InArgs&... params) {
295   dbus::MethodCall method_call(interface_name, method_name);
296   dbus::MessageWriter writer(&method_call);
297   DBusParamWriter::Append(&writer, params...);
298 
299   dbus::ObjectProxy::ErrorCallback dbus_error_callback =
300       base::Bind(&TranslateErrorResponse, error_callback);
301   dbus::ObjectProxy::ResponseCallback dbus_success_callback = base::Bind(
302       &TranslateSuccessResponse<OutArgs...>, success_callback, error_callback);
303 
304   object->CallMethodWithErrorCallback(
305       &method_call, timeout_ms, dbus_success_callback, dbus_error_callback);
306 }
307 
308 // Same as CallMethodWithTimeout() but uses a default timeout value.
309 template<typename... InArgs, typename... OutArgs>
CallMethod(dbus::ObjectProxy * object,const std::string & interface_name,const std::string & method_name,const base::Callback<void (OutArgs...)> & success_callback,const AsyncErrorCallback & error_callback,const InArgs &...params)310 inline void CallMethod(dbus::ObjectProxy* object,
311                        const std::string& interface_name,
312                        const std::string& method_name,
313                        const base::Callback<void(OutArgs...)>& success_callback,
314                        const AsyncErrorCallback& error_callback,
315                        const InArgs&... params) {
316   return CallMethodWithTimeout(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
317                                object,
318                                interface_name,
319                                method_name,
320                                success_callback,
321                                error_callback,
322                                params...);
323 }
324 
325 }  // namespace dbus_utils
326 }  // namespace brillo
327 
328 #endif  // LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_
329