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