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 <brillo/dbus/dbus_param_reader.h> 71 #include <brillo/dbus/dbus_param_writer.h> 72 #include <brillo/dbus/utils.h> 73 #include <brillo/errors/error.h> 74 #include <brillo/errors/error_codes.h> 75 #include <brillo/brillo_export.h> 76 #include <dbus/file_descriptor.h> 77 #include <dbus/message.h> 78 #include <dbus/object_proxy.h> 79 80 namespace brillo { 81 namespace dbus_utils { 82 83 // A helper method to dispatch a blocking D-Bus method call. Can specify 84 // zero or more method call arguments in |args| which will be sent over D-Bus. 85 // This method sends a D-Bus message and blocks for a time period specified 86 // in |timeout_ms| while waiting for a reply. The time out is in milliseconds or 87 // -1 (DBUS_TIMEOUT_USE_DEFAULT) for default, or DBUS_TIMEOUT_INFINITE for no 88 // timeout. If a timeout occurs, the response object contains an error object 89 // with DBUS_ERROR_NO_REPLY error code (those constants come from libdbus 90 // [dbus/dbus.h]). 91 // Returns a dbus::Response object on success. On failure, returns nullptr and 92 // fills in additional error details into the |error| object. 93 template<typename... Args> 94 inline std::unique_ptr<dbus::Response> CallMethodAndBlockWithTimeout( 95 int timeout_ms, 96 dbus::ObjectProxy* object, 97 const std::string& interface_name, 98 const std::string& method_name, 99 ErrorPtr* error, 100 const Args&... args) { 101 dbus::MethodCall method_call(interface_name, method_name); 102 // Add method arguments to the message buffer. 103 dbus::MessageWriter writer(&method_call); 104 DBusParamWriter::Append(&writer, args...); 105 dbus::ScopedDBusError dbus_error; 106 auto response = object->CallMethodAndBlockWithErrorDetails( 107 &method_call, timeout_ms, &dbus_error); 108 if (!response) { 109 if (dbus_error.is_set()) { 110 Error::AddTo(error, 111 FROM_HERE, 112 errors::dbus::kDomain, 113 dbus_error.name(), 114 dbus_error.message()); 115 } else { 116 Error::AddToPrintf(error, 117 FROM_HERE, 118 errors::dbus::kDomain, 119 DBUS_ERROR_FAILED, 120 "Failed to call D-Bus method: %s.%s", 121 interface_name.c_str(), 122 method_name.c_str()); 123 } 124 } 125 return std::unique_ptr<dbus::Response>(response.release()); 126 } 127 128 // Same as CallMethodAndBlockWithTimeout() but uses a default timeout value. 129 template<typename... Args> 130 inline std::unique_ptr<dbus::Response> CallMethodAndBlock( 131 dbus::ObjectProxy* object, 132 const std::string& interface_name, 133 const std::string& method_name, 134 ErrorPtr* error, 135 const Args&... args) { 136 return CallMethodAndBlockWithTimeout(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 137 object, 138 interface_name, 139 method_name, 140 error, 141 args...); 142 } 143 144 namespace internal { 145 // In order to support non-copyable dbus::FileDescriptor, we have this 146 // internal::HackMove() helper function that does really nothing for normal 147 // types but uses Pass() for file descriptors so we can move them out from 148 // the temporaries created inside DBusParamReader<...>::Invoke(). 149 // If only libchrome supported real rvalues so we can just do std::move() and 150 // be done with it. 151 template <typename T> 152 inline const T& HackMove(const T& val) { 153 return val; 154 } 155 156 // Even though |val| here is passed as const&, the actual value is created 157 // inside DBusParamReader<...>::Invoke() and is temporary in nature, so it is 158 // safe to move the file descriptor out of |val|. That's why we are doing 159 // const_cast here. It is a bit hacky, but there is no negative side effects. 160 inline dbus::FileDescriptor HackMove(const dbus::FileDescriptor& val) { 161 return std::move(const_cast<dbus::FileDescriptor&>(val)); 162 } 163 } // namespace internal 164 165 // Extracts the parameters of |ResultTypes...| types from the message reader 166 // and puts the values in the resulting |tuple|. Returns false on error and 167 // provides additional error details in |error| object. 168 template<typename... ResultTypes> 169 inline bool ExtractMessageParametersAsTuple( 170 dbus::MessageReader* reader, 171 ErrorPtr* error, 172 std::tuple<ResultTypes...>* val_tuple) { 173 auto callback = [val_tuple](const ResultTypes&... params) { 174 *val_tuple = std::forward_as_tuple(internal::HackMove(params)...); 175 }; 176 return DBusParamReader<false, ResultTypes...>::Invoke( 177 callback, reader, error); 178 } 179 // Overload of ExtractMessageParametersAsTuple to handle reference types in 180 // tuples created with std::tie(). 181 template<typename... ResultTypes> 182 inline bool ExtractMessageParametersAsTuple( 183 dbus::MessageReader* reader, 184 ErrorPtr* error, 185 std::tuple<ResultTypes&...>* ref_tuple) { 186 auto callback = [ref_tuple](const ResultTypes&... params) { 187 *ref_tuple = std::forward_as_tuple(internal::HackMove(params)...); 188 }; 189 return DBusParamReader<false, ResultTypes...>::Invoke( 190 callback, reader, error); 191 } 192 193 // A helper method to extract a list of values from a message buffer. 194 // The function will return false and provide detailed error information on 195 // failure. It fails if the D-Bus message buffer (represented by the |reader|) 196 // contains too many, too few parameters or the parameters are of wrong types 197 // (signatures). 198 // The usage pattern is as follows: 199 // 200 // int32_t data1; 201 // std::string data2; 202 // ErrorPtr error; 203 // if (ExtractMessageParameters(reader, &error, &data1, &data2)) { ... } 204 // 205 // The above example extracts an Int32 and a String from D-Bus message buffer. 206 template<typename... ResultTypes> 207 inline bool ExtractMessageParameters(dbus::MessageReader* reader, 208 ErrorPtr* error, 209 ResultTypes*... results) { 210 auto ref_tuple = std::tie(*results...); 211 return ExtractMessageParametersAsTuple<ResultTypes...>( 212 reader, error, &ref_tuple); 213 } 214 215 // Convenient helper method to extract return value(s) of a D-Bus method call. 216 // |results| must be zero or more pointers to data expected to be returned 217 // from the method called. If an error occurs, returns false and provides 218 // additional details in |error| object. 219 // 220 // It is OK to call this method even if the D-Bus method doesn't expect 221 // any return values. Just do not specify any output |results|. In this case, 222 // ExtractMethodCallResults() will verify that the method didn't return any 223 // data in the |message|. 224 template<typename... ResultTypes> 225 inline bool ExtractMethodCallResults(dbus::Message* message, 226 ErrorPtr* error, 227 ResultTypes*... results) { 228 CHECK(message) << "Unable to extract parameters from a NULL message."; 229 230 dbus::MessageReader reader(message); 231 if (message->GetMessageType() == dbus::Message::MESSAGE_ERROR) { 232 std::string error_message; 233 if (ExtractMessageParameters(&reader, error, &error_message)) 234 AddDBusError(error, message->GetErrorName(), error_message); 235 return false; 236 } 237 return ExtractMessageParameters(&reader, error, results...); 238 } 239 240 ////////////////////////////////////////////////////////////////////////////// 241 // Asynchronous method invocation support 242 243 using AsyncErrorCallback = base::Callback<void(Error* error)>; 244 245 // A helper function that translates dbus::ErrorResponse response 246 // from D-Bus into brillo::Error* and invokes the |callback|. 247 void BRILLO_EXPORT TranslateErrorResponse(const AsyncErrorCallback& callback, 248 dbus::ErrorResponse* resp); 249 250 // A helper function that translates dbus::Response from D-Bus into 251 // a list of C++ values passed as parameters to |success_callback|. If the 252 // response message doesn't have the correct number of parameters, or they 253 // are of wrong types, an error is sent to |error_callback|. 254 template<typename... OutArgs> 255 void TranslateSuccessResponse( 256 const base::Callback<void(OutArgs...)>& success_callback, 257 const AsyncErrorCallback& error_callback, 258 dbus::Response* resp) { 259 auto callback = [&success_callback](const OutArgs&... params) { 260 if (!success_callback.is_null()) { 261 success_callback.Run(params...); 262 } 263 }; 264 ErrorPtr error; 265 dbus::MessageReader reader(resp); 266 if (!DBusParamReader<false, OutArgs...>::Invoke(callback, &reader, &error) && 267 !error_callback.is_null()) { 268 error_callback.Run(error.get()); 269 } 270 } 271 272 // A helper method to dispatch a non-blocking D-Bus method call. Can specify 273 // zero or more method call arguments in |params| which will be sent over D-Bus. 274 // This method sends a D-Bus message and returns immediately. 275 // When the remote method returns successfully, the success callback is 276 // invoked with the return value(s), if any. 277 // On error, the error callback is called. Note, the error callback can be 278 // called synchronously (before CallMethodWithTimeout returns) if there was 279 // a problem invoking a method (e.g. object or method doesn't exist). 280 // If the response is not received within |timeout_ms|, an error callback is 281 // called with DBUS_ERROR_NO_REPLY error code. 282 template<typename... InArgs, typename... OutArgs> 283 inline void CallMethodWithTimeout( 284 int timeout_ms, 285 dbus::ObjectProxy* object, 286 const std::string& interface_name, 287 const std::string& method_name, 288 const base::Callback<void(OutArgs...)>& success_callback, 289 const AsyncErrorCallback& error_callback, 290 const InArgs&... params) { 291 dbus::MethodCall method_call(interface_name, method_name); 292 dbus::MessageWriter writer(&method_call); 293 DBusParamWriter::Append(&writer, params...); 294 295 dbus::ObjectProxy::ErrorCallback dbus_error_callback = 296 base::Bind(&TranslateErrorResponse, error_callback); 297 dbus::ObjectProxy::ResponseCallback dbus_success_callback = base::Bind( 298 &TranslateSuccessResponse<OutArgs...>, success_callback, error_callback); 299 300 object->CallMethodWithErrorCallback( 301 &method_call, timeout_ms, dbus_success_callback, dbus_error_callback); 302 } 303 304 // Same as CallMethodWithTimeout() but uses a default timeout value. 305 template<typename... InArgs, typename... OutArgs> 306 inline void CallMethod(dbus::ObjectProxy* object, 307 const std::string& interface_name, 308 const std::string& method_name, 309 const base::Callback<void(OutArgs...)>& success_callback, 310 const AsyncErrorCallback& error_callback, 311 const InArgs&... params) { 312 return CallMethodWithTimeout(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 313 object, 314 interface_name, 315 method_name, 316 success_callback, 317 error_callback, 318 params...); 319 } 320 321 } // namespace dbus_utils 322 } // namespace brillo 323 324 #endif // LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_ 325