1 #pragma once
2 
3 #include <utility>
4 #include <memory>
5 #include <sdbusplus/message.hpp>
6 #include "utils.hpp"
7 
8 namespace phosphor
9 {
10 namespace inventory
11 {
12 namespace manager
13 {
14 
15 class Manager;
16 namespace details
17 {
18 using FilterBase = holder::CallableBase <
19                    bool, sdbusplus::bus::bus&, sdbusplus::message::message&, Manager& >;
20 using FilterBasePtr = std::shared_ptr<FilterBase>;
21 template <typename T>
22 using Filter = holder::CallableHolder <
23                T, bool, sdbusplus::bus::bus&, sdbusplus::message::message&, Manager& >;
24 
25 /** @struct Event
26  *  @brief Event object interface.
27  *
28  *  The event base is an assocation of an event type
29  *  and an array of filter callbacks.
30  */
31 struct Event : public std::vector<FilterBasePtr>
32 {
33     enum class Type
34     {
35         DBUS_SIGNAL,
36         STARTUP,
37     };
38 
39     virtual ~Event() = default;
40     Event(const Event&) = delete;
41     Event& operator=(const Event&) = delete;
42     Event(Event&&) = default;
43     Event& operator=(Event&&) = default;
44 
45     /** @brief Event constructor.
46      *
47      *  @param[in] filters - An array of filter callbacks.
48      *  @param[in] t - The event type.
49      */
50     explicit Event(
51         const std::vector<FilterBasePtr>& filters, Type t = Type::STARTUP) :
52         std::vector<FilterBasePtr>(filters),
53         type(t) {}
54 
55     /** @brief event class enumeration. */
56     Type type;
57 };
58 
59 using StartupEvent = Event;
60 
61 using EventBasePtr = std::shared_ptr<Event>;
62 
63 /** @struct DbusSignal
64  *  @brief DBus signal event.
65  *
66  *  DBus signal events are an association of a match signature
67  *  and filtering function object.
68  */
69 struct DbusSignal final : public Event
70 {
71     ~DbusSignal() = default;
72     DbusSignal(const DbusSignal&) = delete;
73     DbusSignal& operator=(const DbusSignal&) = delete;
74     DbusSignal(DbusSignal&&) = default;
75     DbusSignal& operator=(DbusSignal&&) = default;
76 
77     /** @brief Import from signature and filter constructor.
78      *
79      *  @param[in] sig - The DBus match signature.
80      *  @param[in] filter - An array of DBus signal
81      *     match callback filtering functions.
82      */
83     DbusSignal(const char* sig, const std::vector<FilterBasePtr>& filters) :
84         Event(filters, Type::DBUS_SIGNAL),
85         signature(sig) {}
86 
87     const char* signature;
88 };
89 
90 /** @brief make_filter
91  *
92  *  Adapt a filter function object.
93  *
94  *  @param[in] filter - The filter being adapted.
95  *  @returns - The adapted filter.
96  *
97  *  @tparam T - The type of the filter being adapted.
98  */
99 template <typename T>
100 auto make_filter(T&& filter)
101 {
102     return Filter<T>::template make_shared<Filter<T>>(
103         std::forward<T>(filter));
104 }
105 } // namespace details
106 
107 namespace filters
108 {
109 namespace details
110 {
111 namespace property_condition
112 {
113 
114 /** @struct PropertyChangedCondition
115  *  @brief Match filter functor that tests a property value.
116  *
117  *  @tparam T - The type of the property being tested.
118  *  @tparam U - The type of the condition checking functor.
119  */
120 template <typename T, typename U>
121 struct PropertyChangedCondition
122 {
123         PropertyChangedCondition() = delete;
124         ~PropertyChangedCondition() = default;
125         PropertyChangedCondition(const PropertyChangedCondition&) = default;
126         PropertyChangedCondition& operator=(const PropertyChangedCondition&) = delete;
127         PropertyChangedCondition(PropertyChangedCondition&&) = default;
128         PropertyChangedCondition& operator=(PropertyChangedCondition&&) = default;
129         PropertyChangedCondition(const char* iface, const char* property,
130                                  U&& condition) :
131             _iface(iface),
132             _property(property),
133             _condition(std::forward<U>(condition)) { }
134 
135         /** @brief Test a property value.
136          *
137          * Extract the property from the PropertiesChanged
138          * message and run the condition test.
139          */
140         bool operator()(
141             sdbusplus::bus::bus&,
142             sdbusplus::message::message& msg,
143             Manager&) const
144         {
145             std::map <
146             std::string,
147                 sdbusplus::message::variant<T >> properties;
148             const char* iface = nullptr;
149 
150             msg.read(iface);
151             if (!iface || strcmp(iface, _iface))
152             {
153                 return false;
154             }
155 
156             msg.read(properties);
157             auto it = properties.find(_property);
158             if (it == properties.cend())
159             {
160                 return false;
161             }
162 
163             return _condition(
164                        std::forward<T>(it->second.template get<T>()));
165         }
166 
167     private:
168         const char* _iface;
169         const char* _property;
170         U _condition;
171 };
172 
173 /** @struct PropertyConditionBase
174  *  @brief Match filter functor that tests a property value.
175  *
176  *  Base class for PropertyCondition - factored out code that
177  *  doesn't need to be templated.
178  */
179 struct PropertyConditionBase
180 {
181         PropertyConditionBase() = delete;
182         virtual ~PropertyConditionBase() = default;
183         PropertyConditionBase(const PropertyConditionBase&) = delete;
184         PropertyConditionBase& operator=(const PropertyConditionBase&) = delete;
185         PropertyConditionBase(PropertyConditionBase&&) = default;
186         PropertyConditionBase& operator=(PropertyConditionBase&&) = default;
187 
188         /** @brief Constructor
189          *
190          *  The service argument can be nullptr.  If something
191          *  else is provided the function will call the the
192          *  service directly.  If omitted, the function will
193          *  look up the service in the ObjectMapper.
194          *
195          *  @param path - The path of the object containing
196          *     the property to be tested.
197          *  @param iface - The interface hosting the property
198          *     to be tested.
199          *  @param property - The property to be tested.
200          *  @param service - The DBus service hosting the object.
201          */
202         PropertyConditionBase(
203             const char* path,
204             const char* iface,
205             const char* property,
206             const char* service) :
207             _path(path),
208             _iface(iface),
209             _property(property),
210             _service(service) {}
211 
212         /** @brief Forward comparison to type specific implementation. */
213         virtual bool eval(sdbusplus::message::message&) const = 0;
214 
215         /** @brief Test a property value.
216          *
217          * Make a DBus call and test the value of any property.
218          */
219         bool operator()(
220             sdbusplus::bus::bus&,
221             sdbusplus::message::message&,
222             Manager&) const;
223 
224     private:
225         std::string _path;
226         std::string _iface;
227         std::string _property;
228         const char* _service;
229 };
230 
231 /** @struct PropertyCondition
232  *  @brief Match filter functor that tests a property value.
233  *
234  *  @tparam T - The type of the property being tested.
235  *  @tparam U - The type of the condition checking functor.
236  */
237 template <typename T, typename U>
238 struct PropertyCondition final : public PropertyConditionBase
239 {
240         PropertyCondition() = delete;
241         ~PropertyCondition() = default;
242         PropertyCondition(const PropertyCondition&) = delete;
243         PropertyCondition& operator=(const PropertyCondition&) = delete;
244         PropertyCondition(PropertyCondition&&) = default;
245         PropertyCondition& operator=(PropertyCondition&&) = default;
246 
247         /** @brief Constructor
248          *
249          *  The service argument can be nullptr.  If something
250          *  else is provided the function will call the the
251          *  service directly.  If omitted, the function will
252          *  look up the service in the ObjectMapper.
253          *
254          *  @param path - The path of the object containing
255          *     the property to be tested.
256          *  @param iface - The interface hosting the property
257          *     to be tested.
258          *  @param property - The property to be tested.
259          *  @param condition - The test to run on the property.
260          *  @param service - The DBus service hosting the object.
261          */
262         PropertyCondition(
263             const char* path,
264             const char* iface,
265             const char* property,
266             U&& condition,
267             const char* service) :
268             PropertyConditionBase(path, iface, property, service),
269             _condition(std::forward<decltype(condition)>(condition)) {}
270 
271         /** @brief Test a property value.
272          *
273          * Make a DBus call and test the value of any property.
274          */
275         bool eval(sdbusplus::message::message& msg) const override
276         {
277             sdbusplus::message::variant<T> value;
278             msg.read(value);
279             return _condition(
280                        std::forward<T>(value.template get<T>()));
281         }
282 
283     private:
284         U _condition;
285 };
286 
287 } // namespace property_condition
288 } // namespace details
289 
290 /** @brief Implicit type deduction for constructing PropertyChangedCondition.  */
291 template <typename T>
292 auto propertyChangedTo(
293     const char* iface,
294     const char* property,
295     T&& val)
296 {
297     auto condition = [val = std::forward<T>(val)](T && arg)
298     {
299         return arg == val;
300     };
301     using U = decltype(condition);
302     return details::property_condition::PropertyChangedCondition<T, U>(
303                iface, property, std::move(condition));
304 }
305 
306 /** @brief Implicit type deduction for constructing PropertyCondition.  */
307 template <typename T>
308 auto propertyIs(
309     const char* path,
310     const char* iface,
311     const char* property,
312     T&& val,
313     const char* service = nullptr)
314 {
315     auto condition = [val = std::forward<T>(val)](T && arg)
316     {
317         return arg == val;
318     };
319     using U = decltype(condition);
320     return details::property_condition::PropertyCondition<T, U>(
321                path, iface, property, std::move(condition), service);
322 }
323 
324 } // namespace filters
325 } // namespace manager
326 } // namespace inventory
327 } // namespace phosphor
328 
329 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
330