// Copyright 2014 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file provides generic method to parse function call arguments from // D-Bus message buffer and subsequently invokes a provided native C++ callback // with the parameter values passed as the callback arguments. // This functionality is achieved by parsing method arguments one by one, // left to right from the C++ callback's type signature, and moving the parsed // arguments to the back to the next call to DBusInvoke::Invoke's arguments as // const refs. Each iteration has one fewer template specialization arguments, // until there is only the return type remaining and we fall through to either // the void or the non-void final specialization. #ifndef LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_ #define LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_ #include <type_traits> #include <brillo/dbus/data_serialization.h> #include <brillo/dbus/utils.h> #include <brillo/errors/error.h> #include <brillo/errors/error_codes.h> #include <dbus/message.h> namespace brillo { namespace dbus_utils { // A generic DBusParamReader stub class which allows us to specialize on // a variable list of expected function parameters later on. // This struct in itself is not used. But its concrete template specializations // defined below are. // |allow_out_params| controls whether DBusParamReader allows the parameter // list to contain OUT parameters (pointers). template<bool allow_out_params, typename...> struct DBusParamReader; // A generic specialization of DBusParamReader to handle variable function // parameters. This specialization pops one parameter off the D-Bus message // buffer and calls other specializations of DBusParamReader with fewer // parameters to pop the remaining parameters. // CurrentParam - the type of the current method parameter we are processing. // RestOfParams - the types of remaining parameters to be processed. template<bool allow_out_params, typename CurrentParam, typename... RestOfParams> struct DBusParamReader<allow_out_params, CurrentParam, RestOfParams...> { // DBusParamReader::Invoke() is a member function that actually extracts the // current parameter from the message buffer. // handler - the C++ callback functor to be called when all the // parameters are processed. // method_call - D-Bus method call object we are processing. // reader - D-Bus message reader to pop the current argument value from. // args... - the callback parameters processed so far. template<typename CallbackType, typename... Args> static bool Invoke(const CallbackType& handler, dbus::MessageReader* reader, ErrorPtr* error, const Args&... args) { return InvokeHelper<CurrentParam, CallbackType, Args...>( handler, reader, error, static_cast<const Args&>(args)...); } // // There are two specializations of this function: // 1. For the case where ParamType is a value type (D-Bus IN parameter). // 2. For the case where ParamType is a pointer (D-Bus OUT parameter). // In the second case, the parameter is not popped off the message reader, // since we do not expect the client to provide any data for it. // However after the final handler is called, the values for the OUT // parameters should be sent back in the method call response message. // Overload 1: ParamType is not a pointer. template<typename ParamType, typename CallbackType, typename... Args> static typename std::enable_if<!std::is_pointer<ParamType>::value, bool>::type InvokeHelper(const CallbackType& handler, dbus::MessageReader* reader, ErrorPtr* error, const Args&... args) { if (!reader->HasMoreData()) { Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "Too few parameters in a method call"); return false; } // ParamType could be a reference type (e.g. 'const std::string&'). // Here we need a value type so we can create an object of this type and // pop the value off the message buffer into. Using std::decay<> to get // the value type. If ParamType is already a value type, ParamValueType will // be the same as ParamType. using ParamValueType = typename std::decay<ParamType>::type; // The variable to hold the value of the current parameter we reading from // the message buffer. ParamValueType current_param; if (!DBusType<ParamValueType>::Read(reader, ¤t_param)) { Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "Method parameter type mismatch"); return false; } // Call DBusParamReader::Invoke() to process the rest of parameters. // Note that this is not a recursive call because it is calling a different // method of a different class. We exclude the current parameter type // (ParamType) from DBusParamReader<> template parameter list and forward // all the parameters to the arguments of Invoke() and append the current // parameter to the end of the parameter list. We pass it as a const // reference to allow to use move-only types such as std::unique_ptr<> and // to eliminate unnecessarily copying data. return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( handler, reader, error, static_cast<const Args&>(args)..., static_cast<const ParamValueType&>(current_param)); } // Overload 2: ParamType is a pointer. template<typename ParamType, typename CallbackType, typename... Args> static typename std::enable_if<allow_out_params && std::is_pointer<ParamType>::value, bool>::type InvokeHelper(const CallbackType& handler, dbus::MessageReader* reader, ErrorPtr* error, const Args&... args) { // ParamType is a pointer. This is expected to be an output parameter. // Create storage for it and the handler will provide a value for it. using ParamValueType = typename std::remove_pointer<ParamType>::type; // The variable to hold the value of the current parameter we are passing // to the handler. ParamValueType current_param{}; // Default-initialize the value. // Call DBusParamReader::Invoke() to process the rest of parameters. // Note that this is not a recursive call because it is calling a different // method of a different class. We exclude the current parameter type // (ParamType) from DBusParamReader<> template parameter list and forward // all the parameters to the arguments of Invoke() and append the current // parameter to the end of the parameter list. return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( handler, reader, error, static_cast<const Args&>(args)..., ¤t_param); } }; // struct DBusParamReader<ParamType, RestOfParams...> // The final specialization of DBusParamReader<> used when no more parameters // are expected in the message buffer. Actually dispatches the call to the // handler with all the accumulated arguments. template<bool allow_out_params> struct DBusParamReader<allow_out_params> { template<typename CallbackType, typename... Args> static bool Invoke(const CallbackType& handler, dbus::MessageReader* reader, ErrorPtr* error, const Args&... args) { if (reader->HasMoreData()) { Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "Too many parameters in a method call"); return false; } handler(args...); return true; } }; // struct DBusParamReader<> } // namespace dbus_utils } // namespace brillo #endif // LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_