xref: /aosp_15_r20/external/libbrillo/brillo/dbus/dbus_param_reader.h (revision 1a96fba65179ea7d3f56207137718607415c5953)
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, &current_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         &current_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