1e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// Copyright 2014 The Chromium OS Authors. All rights reserved. 2e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// Use of this source code is governed by a BSD-style license that can be 3e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// found in the LICENSE file. 4e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 5e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// This file provides generic method to parse function call arguments from 6e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// D-Bus message buffer and subsequently invokes a provided native C++ callback 7e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// with the parameter values passed as the callback arguments. 8e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 9e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// This functionality is achieved by parsing method arguments one by one, 10e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// left to right from the C++ callback's type signature, and moving the parsed 11e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// arguments to the back to the next call to DBusInvoke::Invoke's arguments as 12e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// const refs. Each iteration has one fewer template specialization arguments, 13e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// until there is only the return type remaining and we fall through to either 14e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// the void or the non-void final specialization. 15e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 16fed60b0c640828b320f56293c8bebc43fd2b1da8Alex Vakulenko#ifndef LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_ 17fed60b0c640828b320f56293c8bebc43fd2b1da8Alex Vakulenko#define LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_ 18e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 19e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko#include <type_traits> 20e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 219ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/dbus/data_serialization.h> 229ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/dbus/utils.h> 239ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/errors/error.h> 249ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/errors/error_codes.h> 25e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko#include <dbus/message.h> 26e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 279ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenkonamespace brillo { 28e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenkonamespace dbus_utils { 29e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 30e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// A generic DBusParamReader stub class which allows us to specialize on 31e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// a variable list of expected function parameters later on. 32e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// This struct in itself is not used. But its concrete template specializations 33e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// defined below are. 34f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko// |allow_out_params| controls whether DBusParamReader allows the parameter 35f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko// list to contain OUT parameters (pointers). 36f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkotemplate<bool allow_out_params, typename...> 37e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenkostruct DBusParamReader; 38e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 39e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// A generic specialization of DBusParamReader to handle variable function 40e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// parameters. This specialization pops one parameter off the D-Bus message 41e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// buffer and calls other specializations of DBusParamReader with fewer 42e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// parameters to pop the remaining parameters. 4390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko// CurrentParam - the type of the current method parameter we are processing. 44e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// RestOfParams - the types of remaining parameters to be processed. 45f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkotemplate<bool allow_out_params, typename CurrentParam, typename... RestOfParams> 46f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkostruct DBusParamReader<allow_out_params, CurrentParam, RestOfParams...> { 47e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // DBusParamReader::Invoke() is a member function that actually extracts the 48e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // current parameter from the message buffer. 49e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // handler - the C++ callback functor to be called when all the 50e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // parameters are processed. 51e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // method_call - D-Bus method call object we are processing. 52e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // reader - D-Bus message reader to pop the current argument value from. 53e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // args... - the callback parameters processed so far. 54e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko template<typename CallbackType, typename... Args> 55e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko static bool Invoke(const CallbackType& handler, 56e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko dbus::MessageReader* reader, 57e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko ErrorPtr* error, 58e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko const Args&... args) { 5990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko return InvokeHelper<CurrentParam, CallbackType, Args...>( 6090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko handler, reader, error, static_cast<const Args&>(args)...); 6190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko } 6290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko 6390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // 6490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // There are two specializations of this function: 6590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // 1. For the case where ParamType is a value type (D-Bus IN parameter). 6690f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // 2. For the case where ParamType is a pointer (D-Bus OUT parameter). 6790f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // In the second case, the parameter is not popped off the message reader, 6890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // since we do not expect the client to provide any data for it. 6990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // However after the final handler is called, the values for the OUT 7090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // parameters should be sent back in the method call response message. 7190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko 7290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // Overload 1: ParamType is not a pointer. 7390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko template<typename ParamType, typename CallbackType, typename... Args> 7490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko static typename std::enable_if<!std::is_pointer<ParamType>::value, bool>::type 7505d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko InvokeHelper(const CallbackType& handler, 7605d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko dbus::MessageReader* reader, 7705d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko ErrorPtr* error, 7805d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko const Args&... args) { 79e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko if (!reader->HasMoreData()) { 808f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 818f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko DBUS_ERROR_INVALID_ARGS, 82e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko "Too few parameters in a method call"); 83e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko return false; 84e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko } 85e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // ParamType could be a reference type (e.g. 'const std::string&'). 86e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // Here we need a value type so we can create an object of this type and 87e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // pop the value off the message buffer into. Using std::decay<> to get 88e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // the value type. If ParamType is already a value type, ParamValueType will 89e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // be the same as ParamType. 90e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko using ParamValueType = typename std::decay<ParamType>::type; 91e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // The variable to hold the value of the current parameter we reading from 92e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // the message buffer. 93e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko ParamValueType current_param; 94003e3bb092961d8d0236fd09efaf0ded404828cbAlex Vakulenko if (!DBusType<ParamValueType>::Read(reader, ¤t_param)) { 958f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 968f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko DBUS_ERROR_INVALID_ARGS, 97e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko "Method parameter type mismatch"); 98e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko return false; 99e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko } 100e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // Call DBusParamReader::Invoke() to process the rest of parameters. 101e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // Note that this is not a recursive call because it is calling a different 102e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // method of a different class. We exclude the current parameter type 103e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // (ParamType) from DBusParamReader<> template parameter list and forward 104e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // all the parameters to the arguments of Invoke() and append the current 105e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // parameter to the end of the parameter list. We pass it as a const 106e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // reference to allow to use move-only types such as std::unique_ptr<> and 107e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko // to eliminate unnecessarily copying data. 108f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( 109e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko handler, reader, error, 110e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko static_cast<const Args&>(args)..., 111e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko static_cast<const ParamValueType&>(current_param)); 112e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko } 11390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko 11490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // Overload 2: ParamType is a pointer. 11590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko template<typename ParamType, typename CallbackType, typename... Args> 116f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko static typename std::enable_if<allow_out_params && 117f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko std::is_pointer<ParamType>::value, bool>::type 11890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko InvokeHelper(const CallbackType& handler, 11990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko dbus::MessageReader* reader, 12090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko ErrorPtr* error, 12190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko const Args&... args) { 12290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // ParamType is a pointer. This is expected to be an output parameter. 12390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // Create storage for it and the handler will provide a value for it. 12490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko using ParamValueType = typename std::remove_pointer<ParamType>::type; 12590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // The variable to hold the value of the current parameter we are passing 12690f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // to the handler. 12790f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko ParamValueType current_param{}; // Default-initialize the value. 12890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // Call DBusParamReader::Invoke() to process the rest of parameters. 12990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // Note that this is not a recursive call because it is calling a different 13090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // method of a different class. We exclude the current parameter type 13190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // (ParamType) from DBusParamReader<> template parameter list and forward 13290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // all the parameters to the arguments of Invoke() and append the current 13390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko // parameter to the end of the parameter list. 134f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( 13590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko handler, reader, error, 13690f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko static_cast<const Args&>(args)..., 13790f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko ¤t_param); 13890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko } 139e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko}; // struct DBusParamReader<ParamType, RestOfParams...> 140e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 141e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// The final specialization of DBusParamReader<> used when no more parameters 142e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// are expected in the message buffer. Actually dispatches the call to the 143e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// handler with all the accumulated arguments. 144f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkotemplate<bool allow_out_params> 145f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkostruct DBusParamReader<allow_out_params> { 146e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko template<typename CallbackType, typename... Args> 147e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko static bool Invoke(const CallbackType& handler, 148e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko dbus::MessageReader* reader, 149e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko ErrorPtr* error, 150e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko const Args&... args) { 151e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko if (reader->HasMoreData()) { 1528f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 1538f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko DBUS_ERROR_INVALID_ARGS, 154e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko "Too many parameters in a method call"); 155e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko return false; 156e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko } 157e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko handler(args...); 158e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko return true; 159e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko } 160e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko}; // struct DBusParamReader<> 161e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 162e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko} // namespace dbus_utils 1639ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko} // namespace brillo 164e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko 165fed60b0c640828b320f56293c8bebc43fd2b1da8Alex Vakulenko#endif // LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_ 166