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, ¤t_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, 118 bool>::type 119 InvokeHelper(const CallbackType& handler, 120 ::dbus::MessageReader* reader, 121 ErrorPtr* error, 122 const Args&... args) { 123 // ParamType is a pointer. This is expected to be an output parameter. 124 // Create storage for it and the handler will provide a value for it. 125 using ParamValueType = typename std::remove_pointer<ParamType>::type; 126 // The variable to hold the value of the current parameter we are passing 127 // to the handler. 128 ParamValueType current_param{}; // Default-initialize the value. 129 // Call DBusParamReader::Invoke() to process the rest of parameters. 130 // Note that this is not a recursive call because it is calling a different 131 // method of a different class. We exclude the current parameter type 132 // (ParamType) from DBusParamReader<> template parameter list and forward 133 // all the parameters to the arguments of Invoke() and append the current 134 // parameter to the end of the parameter list. 135 return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( 136 handler, reader, error, 137 static_cast<const Args&>(args)..., 138 ¤t_param); 139 } 140 }; // struct DBusParamReader<ParamType, RestOfParams...> 141 142 // The final specialization of DBusParamReader<> used when no more parameters 143 // are expected in the message buffer. Actually dispatches the call to the 144 // handler with all the accumulated arguments. 145 template<bool allow_out_params> 146 struct DBusParamReader<allow_out_params> { 147 template <typename CallbackType, typename... Args> 148 static bool Invoke(const CallbackType& handler, 149 ::dbus::MessageReader* reader, 150 ErrorPtr* error, 151 const Args&... args) { 152 if (reader->HasMoreData()) { 153 Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 154 DBUS_ERROR_INVALID_ARGS, 155 "Too many parameters in a method call"); 156 return false; 157 } 158 handler(args...); 159 return true; 160 } 161 }; // struct DBusParamReader<> 162 163 } // namespace dbus_utils 164 } // namespace brillo 165 166 #endif // LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_ 167