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