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>(
231             sdbusplus::message::variant_ns::get<T>(it->second)));
232     }
233 
234   private:
235     const char* _iface;
236     const char* _property;
237     U _condition;
238 };
239 
240 /** @struct PropertyConditionBase
241  *  @brief Match filter functor that tests a property value.
242  *
243  *  Base class for PropertyCondition - factored out code that
244  *  doesn't need to be templated.
245  */
246 struct PropertyConditionBase
247 {
248     PropertyConditionBase() = delete;
249     virtual ~PropertyConditionBase() = default;
250     PropertyConditionBase(const PropertyConditionBase&) = default;
251     PropertyConditionBase& operator=(const PropertyConditionBase&) = default;
252     PropertyConditionBase(PropertyConditionBase&&) = default;
253     PropertyConditionBase& operator=(PropertyConditionBase&&) = default;
254 
255     /** @brief Constructor
256      *
257      *  The service argument can be nullptr.  If something
258      *  else is provided the function will call the the
259      *  service directly.  If omitted, the function will
260      *  look up the service in the ObjectMapper.
261      *
262      *  @param path - The path of the object containing
263      *     the property to be tested.
264      *  @param iface - The interface hosting the property
265      *     to be tested.
266      *  @param property - The property to be tested.
267      *  @param service - The DBus service hosting the object.
268      */
269     PropertyConditionBase(const char* path, const char* iface,
270                           const char* property, const char* service) :
271         _path(path ? path : std::string()),
272         _iface(iface), _property(property), _service(service)
273     {
274     }
275 
276     /** @brief Forward comparison to type specific implementation. */
277     virtual bool eval(sdbusplus::message::message&) const = 0;
278 
279     /** @brief Forward comparison to type specific implementation. */
280     virtual bool eval(Manager&) const = 0;
281 
282     /** @brief Test a property value.
283      *
284      * Make a DBus call and test the value of any property.
285      */
286     bool operator()(sdbusplus::bus::bus&, sdbusplus::message::message&,
287                     Manager&) const;
288 
289     /** @brief Test a property value.
290      *
291      * Make a DBus call and test the value of any property.
292      */
293     bool operator()(const std::string&, sdbusplus::bus::bus&, Manager&) const;
294 
295   private:
296     std::string _path;
297     std::string _iface;
298     std::string _property;
299     const char* _service;
300 };
301 
302 /** @struct PropertyCondition
303  *  @brief Match filter functor that tests a property value.
304  *
305  *  @tparam T - The type of the property being tested.
306  *  @tparam U - The type of the condition checking functor.
307  *  @tparam V - The getProperty functor return type.
308  */
309 template <typename T, typename U, typename V>
310 struct PropertyCondition final : public PropertyConditionBase
311 {
312     PropertyCondition() = delete;
313     ~PropertyCondition() = default;
314     PropertyCondition(const PropertyCondition&) = default;
315     PropertyCondition& operator=(const PropertyCondition&) = default;
316     PropertyCondition(PropertyCondition&&) = default;
317     PropertyCondition& operator=(PropertyCondition&&) = default;
318 
319     /** @brief Constructor
320      *
321      *  The service & getProperty arguments can be nullptrs.
322      *  If something else is provided the function will call the the
323      *  service directly.  If omitted, the function will
324      *  look up the service in the ObjectMapper.
325      *  The getProperty function will be called to retrieve a property
326      *  value when given and the property is hosted by inventory manager.
327      *  When not given, the condition will default to return that the
328      *  condition failed and will not be executed.
329      *
330      *  @param path - The path of the object containing
331      *     the property to be tested.
332      *  @param iface - The interface hosting the property
333      *     to be tested.
334      *  @param property - The property to be tested.
335      *  @param condition - The test to run on the property.
336      *  @param service - The DBus service hosting the object.
337      *  @param getProperty - The function to get a property value
338      *     for the condition.
339      */
340     PropertyCondition(const char* path, const char* iface, const char* property,
341                       U&& condition, const char* service,
342                       GetProperty<V>&& getProperty = nullptr) :
343         PropertyConditionBase(path, iface, property, service),
344         _condition(std::forward<decltype(condition)>(condition)),
345         _getProperty(getProperty)
346     {
347     }
348 
349     /** @brief Test a property value.
350      *
351      * Make a DBus call and test the value of any property.
352      */
353     bool eval(sdbusplus::message::message& msg) const override
354     {
355         sdbusplus::message::variant<T> value;
356         msg.read(value);
357         return _condition(
358             std::forward<T>(sdbusplus::message::variant_ns::get<T>(value)));
359     }
360 
361     /** @brief Retrieve a property value from inventory and test it.
362      *
363      *  Get a property from the inventory manager and test the value.
364      *  Default to fail the test where no function is given to get the
365      *  property from the inventory manager.
366      */
367     bool eval(Manager& mgr) const override
368     {
369         if (_getProperty)
370         {
371             auto variant = _getProperty(mgr);
372             auto value = sdbusplus::message::variant_ns::get<T>(variant);
373             return _condition(std::forward<T>(value));
374         }
375         return false;
376     }
377 
378   private:
379     U _condition;
380     GetProperty<V> _getProperty;
381 };
382 
383 /** @brief Implicit type deduction for constructing PropertyChangedCondition. */
384 template <typename T>
385 auto propertyChangedTo(const char* iface, const char* property, T&& val)
386 {
387     auto condition = [val = std::forward<T>(val)](T&& arg) {
388         return arg == val;
389     };
390     using U = decltype(condition);
391     return PropertyChangedCondition<T, U>(iface, property,
392                                           std::move(condition));
393 }
394 
395 /** @brief Implicit type deduction for constructing PropertyCondition.  */
396 template <typename T, typename V = InterfaceVariantType>
397 auto propertyIs(const char* path, const char* iface, const char* property,
398                 T&& val, const char* service = nullptr,
399                 GetProperty<V>&& getProperty = nullptr)
400 {
401     auto condition = [val = std::forward<T>(val)](T&& arg) {
402         return arg == val;
403     };
404     using U = decltype(condition);
405     return PropertyCondition<T, U, V>(path, iface, property,
406                                       std::move(condition), service,
407                                       std::move(getProperty));
408 }
409 } // namespace functor
410 } // namespace manager
411 } // namespace inventory
412 } // namespace phosphor
413 
414 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
415