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