1 #pragma once
2 
3 #include "types.hpp"
4 #include "utils.hpp"
5 
6 #include <sdbusplus/bus.hpp>
7 
8 #include <memory>
9 #include <utility>
10 
11 namespace phosphor
12 {
13 namespace inventory
14 {
15 namespace manager
16 {
17 
18 class Manager;
19 
20 /** @brief make_action
21  *
22  *  Adapt an action function object.
23  *
24  *  @param[in] action - The action being adapted.
25  *  @returns - The adapted action.
26  *
27  *  @tparam T - The type of the action being adapted.
28  */
29 template <typename T>
30 auto make_action(T&& action)
31 {
32     return Action(std::forward<T>(action));
33 }
34 
35 /** @brief make_filter
36  *
37  *  Adapt a filter function object.
38  *
39  *  @param[in] filter - The filter being adapted.
40  *  @returns - The adapted filter.
41  *
42  *  @tparam T - The type of the filter being adapted.
43  */
44 template <typename T>
45 auto make_filter(T&& filter)
46 {
47     return Filter(std::forward<T>(filter));
48 }
49 
50 /** @brief make_path_condition
51  *
52  *  Adapt a path_condition function object.
53  *
54  *  @param[in] filter - The functor being adapted.
55  *  @returns - The adapted functor.
56  *
57  *  @tparam T - The type of the functor being adapted.
58  */
59 template <typename T>
60 auto make_path_condition(T&& condition)
61 {
62     return PathCondition(std::forward<T>(condition));
63 }
64 
65 /** @brief make_get_property
66  *
67  *  Adapt a get_property function object.
68  *
69  *  @param[in] method - The functor being adapted.
70  *  @returns - The adapted functor.
71  *
72  *  @tparam T - The return type of the function object.
73  *  @tparam U - The type of the functor being adapted.
74  */
75 template <typename T, typename U>
76 auto make_get_property(U&& method)
77 {
78     return GetProperty<T>(std::forward<U>(method));
79 }
80 
81 template <typename T, typename... Args>
82 auto callArrayWithStatus(T&& container, Args&&... args)
83 {
84     for (auto f : container)
85     {
86         if (!f(std::forward<Args>(args)...))
87         {
88             return false;
89         }
90     }
91     return true;
92 }
93 
94 namespace functor
95 {
96 
97 /** @brief Destroy objects action.  */
98 inline auto destroyObjects(std::vector<const char*>&& paths,
99                            std::vector<PathCondition>&& conditions)
100 {
101     return [=](auto& b, auto& m) {
102         for (const auto& p : paths)
103         {
104             if (callArrayWithStatus(conditions, p, b, m))
105             {
106                 m.destroyObjects({p});
107             }
108         }
109     };
110 }
111 
112 /** @brief Create objects action.  */
113 inline auto
114     createObjects(std::map<sdbusplus::message::object_path, Object>&& objs)
115 {
116     return [=](auto&, auto& m) { m.createObjects(objs); };
117 }
118 
119 /** @brief Set a property action.
120  *
121  *  Invoke the requested method with a reference to the requested
122  *  sdbusplus server binding interface as a parameter.
123  *
124  *  @tparam T - The sdbusplus server binding interface type.
125  *  @tparam U - The type of the sdbusplus server binding member
126  *      function that sets the property.
127  *  @tparam V - The property value type.
128  *
129  *  @param[in] paths - The DBus paths on which the property should
130  *      be set.
131  *  @param[in] iface - The DBus interface hosting the property.
132  *  @param[in] member - Pointer to sdbusplus server binding member.
133  *  @param[in] value - The value the property should be set to.
134  *
135  *  @returns - A function object that sets the requested property
136  *      to the requested value.
137  */
138 template <typename T, typename U, typename V>
139 auto setProperty(std::vector<const char*>&& paths,
140                  std::vector<PathCondition>&& conditions, const char* iface,
141                  U&& member, V&& value)
142 {
143     // The manager is the only parameter passed to actions.
144     // Bind the path, interface, interface member function pointer,
145     // and value to a lambda.  When it is called, forward the
146     // path, interface and value on to the manager member function.
147     return [paths, conditions = conditions, iface, member,
148             value = std::forward<V>(value)](auto& b, auto& m) {
149         for (auto p : paths)
150         {
151             if (callArrayWithStatus(conditions, p, b, m))
152             {
153                 m.template invokeMethod<T>(p, iface, member, value);
154             }
155         }
156     };
157 }
158 
159 /** @brief Get a property.
160  *
161  *  Invoke the requested method with a reference to the requested
162  *  sdbusplus server binding interface as a parameter.
163  *
164  *  @tparam T - The sdbusplus server binding interface type.
165  *  @tparam U - The type of the sdbusplus server binding member
166  *      function that sets the property.
167  *
168  *  @param[in] path - The DBus path to get the property from.
169  *  @param[in] iface - The DBus interface hosting the property.
170  *  @param[in] member - Pointer to sdbusplus server binding member.
171  *  @param[in] prop - The property name to get the value from.
172  *
173  *  @returns - A function object that gets the requested property.
174  */
175 template <typename T, typename U>
176 inline auto getProperty(const char* path, const char* iface, U&& member,
177                         const char* prop)
178 {
179     return [path, iface, member, prop](auto& mgr) {
180         return mgr.template invokeMethod<T>(path, iface, member, prop);
181     };
182 }
183 
184 /** @struct PropertyChangedCondition
185  *  @brief Match filter functor that tests a property value.
186  *
187  *  @tparam T - The type of the property being tested.
188  *  @tparam U - The type of the condition checking functor.
189  */
190 template <typename T, typename U>
191 struct PropertyChangedCondition
192 {
193     PropertyChangedCondition() = delete;
194     ~PropertyChangedCondition() = default;
195     PropertyChangedCondition(const PropertyChangedCondition&) = default;
196     PropertyChangedCondition&
197         operator=(const PropertyChangedCondition&) = default;
198     PropertyChangedCondition(PropertyChangedCondition&&) = default;
199     PropertyChangedCondition& operator=(PropertyChangedCondition&&) = default;
200     PropertyChangedCondition(const char* iface, const char* property,
201                              U&& condition) :
202         _iface(iface),
203         _property(property), _condition(std::forward<U>(condition))
204     {}
205 
206     /** @brief Test a property value.
207      *
208      * Extract the property from the PropertiesChanged
209      * message and run the condition test.
210      */
211     bool operator()(sdbusplus::bus::bus&, sdbusplus::message::message& msg,
212                     Manager&) const
213     {
214         std::map<std::string, std::variant<T>> properties;
215         const char* iface = nullptr;
216 
217         msg.read(iface);
218         if (!iface || strcmp(iface, _iface))
219         {
220             return false;
221         }
222 
223         msg.read(properties);
224         auto it = properties.find(_property);
225         if (it == properties.cend())
226         {
227             return false;
228         }
229 
230         return _condition(std::forward<T>(std::get<T>(it->second)));
231     }
232 
233   private:
234     const char* _iface;
235     const char* _property;
236     U _condition;
237 };
238 
239 /** @struct PropertyConditionBase
240  *  @brief Match filter functor that tests a property value.
241  *
242  *  Base class for PropertyCondition - factored out code that
243  *  doesn't need to be templated.
244  */
245 struct PropertyConditionBase
246 {
247     PropertyConditionBase() = delete;
248     virtual ~PropertyConditionBase() = default;
249     PropertyConditionBase(const PropertyConditionBase&) = default;
250     PropertyConditionBase& operator=(const PropertyConditionBase&) = default;
251     PropertyConditionBase(PropertyConditionBase&&) = default;
252     PropertyConditionBase& operator=(PropertyConditionBase&&) = default;
253 
254     /** @brief Constructor
255      *
256      *  The service argument can be nullptr.  If something
257      *  else is provided the function will call the the
258      *  service directly.  If omitted, the function will
259      *  look up the service in the ObjectMapper.
260      *
261      *  @param path - The path of the object containing
262      *     the property to be tested.
263      *  @param iface - The interface hosting the property
264      *     to be tested.
265      *  @param property - The property to be tested.
266      *  @param service - The DBus service hosting the object.
267      */
268     PropertyConditionBase(const char* path, const char* iface,
269                           const char* property, const char* service) :
270         _path(path ? path : std::string()),
271         _iface(iface), _property(property), _service(service)
272     {}
273 
274     /** @brief Forward comparison to type specific implementation. */
275     virtual bool eval(sdbusplus::message::message&) const = 0;
276 
277     /** @brief Forward comparison to type specific implementation. */
278     virtual bool eval(Manager&) const = 0;
279 
280     /** @brief Test a property value.
281      *
282      * Make a DBus call and test the value of any property.
283      */
284     bool operator()(sdbusplus::bus::bus&, sdbusplus::message::message&,
285                     Manager&) const;
286 
287     /** @brief Test a property value.
288      *
289      * Make a DBus call and test the value of any property.
290      */
291     bool operator()(const std::string&, sdbusplus::bus::bus&, Manager&) const;
292 
293   private:
294     std::string _path;
295     std::string _iface;
296     std::string _property;
297     const char* _service;
298 };
299 
300 /** @struct PropertyCondition
301  *  @brief Match filter functor that tests a property value.
302  *
303  *  @tparam T - The type of the property being tested.
304  *  @tparam U - The type of the condition checking functor.
305  *  @tparam V - The getProperty functor return type.
306  */
307 template <typename T, typename U, typename V>
308 struct PropertyCondition final : public PropertyConditionBase
309 {
310     PropertyCondition() = delete;
311     ~PropertyCondition() = default;
312     PropertyCondition(const PropertyCondition&) = default;
313     PropertyCondition& operator=(const PropertyCondition&) = default;
314     PropertyCondition(PropertyCondition&&) = default;
315     PropertyCondition& operator=(PropertyCondition&&) = default;
316 
317     /** @brief Constructor
318      *
319      *  The service & getProperty arguments can be nullptrs.
320      *  If something else is provided the function will call the the
321      *  service directly.  If omitted, the function will
322      *  look up the service in the ObjectMapper.
323      *  The getProperty function will be called to retrieve a property
324      *  value when given and the property is hosted by inventory manager.
325      *  When not given, the condition will default to return that the
326      *  condition failed and will not be executed.
327      *
328      *  @param path - The path of the object containing
329      *     the property to be tested.
330      *  @param iface - The interface hosting the property
331      *     to be tested.
332      *  @param property - The property to be tested.
333      *  @param condition - The test to run on the property.
334      *  @param service - The DBus service hosting the object.
335      *  @param getProperty - The function to get a property value
336      *     for the condition.
337      */
338     PropertyCondition(const char* path, const char* iface, const char* property,
339                       U&& condition, const char* service,
340                       GetProperty<V>&& getProperty = nullptr) :
341         PropertyConditionBase(path, iface, property, service),
342         _condition(std::forward<decltype(condition)>(condition)),
343         _getProperty(getProperty)
344     {}
345 
346     /** @brief Test a property value.
347      *
348      * Make a DBus call and test the value of any property.
349      */
350     bool eval(sdbusplus::message::message& msg) const override
351     {
352         std::variant<T> value;
353         msg.read(value);
354         return _condition(std::forward<T>(std::get<T>(value)));
355     }
356 
357     /** @brief Retrieve a property value from inventory and test it.
358      *
359      *  Get a property from the inventory manager and test the value.
360      *  Default to fail the test where no function is given to get the
361      *  property from the inventory manager.
362      */
363     bool eval(Manager& mgr) const override
364     {
365         if (_getProperty)
366         {
367             auto variant = _getProperty(mgr);
368             auto value = std::get<T>(variant);
369             return _condition(std::forward<T>(value));
370         }
371         return false;
372     }
373 
374   private:
375     U _condition;
376     GetProperty<V> _getProperty;
377 };
378 
379 /** @brief Implicit type deduction for constructing PropertyChangedCondition. */
380 template <typename T>
381 auto propertyChangedTo(const char* iface, const char* property, T&& val)
382 {
383     auto condition = [val = std::forward<T>(val)](T&& arg) {
384         return arg == val;
385     };
386     using U = decltype(condition);
387     return PropertyChangedCondition<T, U>(iface, property,
388                                           std::move(condition));
389 }
390 
391 /** @brief Implicit type deduction for constructing PropertyCondition.  */
392 template <typename T, typename V = InterfaceVariantType>
393 auto propertyIs(const char* path, const char* iface, const char* property,
394                 T&& val, const char* service = nullptr,
395                 GetProperty<V>&& getProperty = nullptr)
396 {
397     auto condition = [val = std::forward<T>(val)](T&& arg) {
398         return arg == val;
399     };
400     using U = decltype(condition);
401     return PropertyCondition<T, U, V>(path, iface, property,
402                                       std::move(condition), service,
403                                       std::move(getProperty));
404 }
405 } // namespace functor
406 } // namespace manager
407 } // namespace inventory
408 } // namespace phosphor
409 
410 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
411