Home | History | Annotate | Download | only in dbus
      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