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