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 generic method to parse function call arguments from
      6 // D-Bus message buffer and subsequently invokes a provided native C++ callback
      7 // with the parameter values passed as the callback arguments.
      8 
      9 // This functionality is achieved by parsing method arguments one by one,
     10 // left to right from the C++ callback's type signature, and moving the parsed
     11 // arguments to the back to the next call to DBusInvoke::Invoke's arguments as
     12 // const refs.  Each iteration has one fewer template specialization arguments,
     13 // until there is only the return type remaining and we fall through to either
     14 // the void or the non-void final specialization.
     15 
     16 #ifndef LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_
     17 #define LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_
     18 
     19 #include <type_traits>
     20 
     21 #include <brillo/dbus/data_serialization.h>
     22 #include <brillo/dbus/utils.h>
     23 #include <brillo/errors/error.h>
     24 #include <brillo/errors/error_codes.h>
     25 #include <dbus/message.h>
     26 
     27 namespace brillo {
     28 namespace dbus_utils {
     29 
     30 // A generic DBusParamReader stub class which allows us to specialize on
     31 // a variable list of expected function parameters later on.
     32 // This struct in itself is not used. But its concrete template specializations
     33 // defined below are.
     34 // |allow_out_params| controls whether DBusParamReader allows the parameter
     35 // list to contain OUT parameters (pointers).
     36 template<bool allow_out_params, typename...>
     37 struct DBusParamReader;
     38 
     39 // A generic specialization of DBusParamReader to handle variable function
     40 // parameters. This specialization pops one parameter off the D-Bus message
     41 // buffer and calls other specializations of DBusParamReader with fewer
     42 // parameters to pop the remaining parameters.
     43 //  CurrentParam  - the type of the current method parameter we are processing.
     44 //  RestOfParams  - the types of remaining parameters to be processed.
     45 template<bool allow_out_params, typename CurrentParam, typename... RestOfParams>
     46 struct DBusParamReader<allow_out_params, CurrentParam, RestOfParams...> {
     47   // DBusParamReader::Invoke() is a member function that actually extracts the
     48   // current parameter from the message buffer.
     49   //  handler     - the C++ callback functor to be called when all the
     50   //                parameters are processed.
     51   //  method_call - D-Bus method call object we are processing.
     52   //  reader      - D-Bus message reader to pop the current argument value from.
     53   //  args...     - the callback parameters processed so far.
     54   template<typename CallbackType, typename... Args>
     55   static bool Invoke(const CallbackType& handler,
     56                      dbus::MessageReader* reader,
     57                      ErrorPtr* error,
     58                      const Args&... args) {
     59     return InvokeHelper<CurrentParam, CallbackType, Args...>(
     60         handler, reader, error, static_cast<const Args&>(args)...);
     61   }
     62 
     63   //
     64   // There are two specializations of this function:
     65   //  1. For the case where ParamType is a value type (D-Bus IN parameter).
     66   //  2. For the case where ParamType is a pointer (D-Bus OUT parameter).
     67   // In the second case, the parameter is not popped off the message reader,
     68   // since we do not expect the client to provide any data for it.
     69   // However after the final handler is called, the values for the OUT
     70   // parameters should be sent back in the method call response message.
     71 
     72   // Overload 1: ParamType is not a pointer.
     73   template<typename ParamType, typename CallbackType, typename... Args>
     74   static typename std::enable_if<!std::is_pointer<ParamType>::value, bool>::type
     75   InvokeHelper(const CallbackType& handler,
     76                dbus::MessageReader* reader,
     77                ErrorPtr* error,
     78                const Args&... args) {
     79     if (!reader->HasMoreData()) {
     80       Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
     81                    DBUS_ERROR_INVALID_ARGS,
     82                    "Too few parameters in a method call");
     83       return false;
     84     }
     85     // ParamType could be a reference type (e.g. 'const std::string&').
     86     // Here we need a value type so we can create an object of this type and
     87     // pop the value off the message buffer into. Using std::decay<> to get
     88     // the value type. If ParamType is already a value type, ParamValueType will
     89     // be the same as ParamType.
     90     using ParamValueType = typename std::decay<ParamType>::type;
     91     // The variable to hold the value of the current parameter we reading from
     92     // the message buffer.
     93     ParamValueType current_param;
     94     if (!DBusType<ParamValueType>::Read(reader, &current_param)) {
     95       Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
     96                    DBUS_ERROR_INVALID_ARGS,
     97                    "Method parameter type mismatch");
     98       return false;
     99     }
    100     // Call DBusParamReader::Invoke() to process the rest of parameters.
    101     // Note that this is not a recursive call because it is calling a different
    102     // method of a different class. We exclude the current parameter type
    103     // (ParamType) from DBusParamReader<> template parameter list and forward
    104     // all the parameters to the arguments of Invoke() and append the current
    105     // parameter to the end of the parameter list. We pass it as a const
    106     // reference to allow to use move-only types such as std::unique_ptr<> and
    107     // to eliminate unnecessarily copying data.
    108     return DBusParamReader<allow_out_params, RestOfParams...>::Invoke(
    109         handler, reader, error,
    110         static_cast<const Args&>(args)...,
    111         static_cast<const ParamValueType&>(current_param));
    112   }
    113 
    114   // Overload 2: ParamType is a pointer.
    115   template<typename ParamType, typename CallbackType, typename... Args>
    116   static typename std::enable_if<allow_out_params &&
    117                                  std::is_pointer<ParamType>::value, bool>::type
    118       InvokeHelper(const CallbackType& handler,
    119                    dbus::MessageReader* reader,
    120                    ErrorPtr* error,
    121                    const Args&... args) {
    122     // ParamType is a pointer. This is expected to be an output parameter.
    123     // Create storage for it and the handler will provide a value for it.
    124     using ParamValueType = typename std::remove_pointer<ParamType>::type;
    125     // The variable to hold the value of the current parameter we are passing
    126     // to the handler.
    127     ParamValueType current_param{};  // Default-initialize the value.
    128     // Call DBusParamReader::Invoke() to process the rest of parameters.
    129     // Note that this is not a recursive call because it is calling a different
    130     // method of a different class. We exclude the current parameter type
    131     // (ParamType) from DBusParamReader<> template parameter list and forward
    132     // all the parameters to the arguments of Invoke() and append the current
    133     // parameter to the end of the parameter list.
    134     return DBusParamReader<allow_out_params, RestOfParams...>::Invoke(
    135         handler, reader, error,
    136         static_cast<const Args&>(args)...,
    137         &current_param);
    138   }
    139 };  // struct DBusParamReader<ParamType, RestOfParams...>
    140 
    141 // The final specialization of DBusParamReader<> used when no more parameters
    142 // are expected in the message buffer. Actually dispatches the call to the
    143 // handler with all the accumulated arguments.
    144 template<bool allow_out_params>
    145 struct DBusParamReader<allow_out_params> {
    146   template<typename CallbackType, typename... Args>
    147   static bool Invoke(const CallbackType& handler,
    148                      dbus::MessageReader* reader,
    149                      ErrorPtr* error,
    150                      const Args&... args) {
    151     if (reader->HasMoreData()) {
    152       Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
    153                    DBUS_ERROR_INVALID_ARGS,
    154                    "Too many parameters in a method call");
    155       return false;
    156     }
    157     handler(args...);
    158     return true;
    159   }
    160 };  // struct DBusParamReader<>
    161 
    162 }  // namespace dbus_utils
    163 }  // namespace brillo
    164 
    165 #endif  // LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_
    166