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