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     };
37 
38     virtual ~Event() = default;
39     Event(const Event&) = delete;
40     Event& operator=(const Event&) = delete;
41     Event(Event&&) = default;
42     Event& operator=(Event&&) = default;
43 
44     /** @brief Event constructor.
45      *
46      *  @param[in] filters - An array of filter callbacks.
47      *  @param[in] t - The event type.
48      */
49     explicit Event(
50         const std::vector<FilterBasePtr>& filters, Type t) :
51         std::vector<FilterBasePtr>(filters),
52         type(t) {}
53 
54     /** @brief event class enumeration. */
55     Type type;
56 };
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<FilterBasePtr>& 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<T>::template make_shared<Filter<T>>(
100         std::forward<T>(filter));
101 }
102 } // namespace details
103 
104 namespace filters
105 {
106 namespace details
107 {
108 namespace property_condition
109 {
110 
111 /** @struct PropertyChangedCondition
112  *  @brief Match filter functor that tests a property value.
113  *
114  *  @tparam T - The type of the property being tested.
115  *  @tparam U - The type of the condition checking functor.
116  */
117 template <typename T, typename U>
118 struct PropertyChangedCondition
119 {
120         PropertyChangedCondition() = delete;
121         ~PropertyChangedCondition() = default;
122         PropertyChangedCondition(const PropertyChangedCondition&) = default;
123         PropertyChangedCondition& operator=(const PropertyChangedCondition&) = delete;
124         PropertyChangedCondition(PropertyChangedCondition&&) = default;
125         PropertyChangedCondition& operator=(PropertyChangedCondition&&) = default;
126         PropertyChangedCondition(const char* iface, const char* property,
127                                  U&& condition) :
128             _iface(iface),
129             _property(property),
130             _condition(std::forward<U>(condition)) { }
131 
132         /** @brief Test a property value.
133          *
134          * Extract the property from the PropertiesChanged
135          * message and run the condition test.
136          */
137         bool operator()(
138             sdbusplus::bus::bus&,
139             sdbusplus::message::message& msg,
140             Manager&) const
141         {
142             std::map <
143             std::string,
144                 sdbusplus::message::variant<T >> properties;
145             const char* iface = nullptr;
146 
147             msg.read(iface);
148             if (!iface || strcmp(iface, _iface))
149             {
150                 return false;
151             }
152 
153             msg.read(properties);
154             auto it = properties.find(_property);
155             if (it == properties.cend())
156             {
157                 return false;
158             }
159 
160             return _condition(
161                        std::forward<T>(it->second.template get<T>()));
162         }
163 
164     private:
165         const char* _iface;
166         const char* _property;
167         U _condition;
168 };
169 
170 /** @struct PropertyConditionBase
171  *  @brief Match filter functor that tests a property value.
172  *
173  *  Base class for PropertyCondition - factored out code that
174  *  doesn't need to be templated.
175  */
176 struct PropertyConditionBase
177 {
178         PropertyConditionBase() = delete;
179         virtual ~PropertyConditionBase() = default;
180         PropertyConditionBase(const PropertyConditionBase&) = delete;
181         PropertyConditionBase& operator=(const PropertyConditionBase&) = delete;
182         PropertyConditionBase(PropertyConditionBase&&) = default;
183         PropertyConditionBase& operator=(PropertyConditionBase&&) = default;
184 
185         /** @brief Constructor
186          *
187          *  The service argument can be nullptr.  If something
188          *  else is provided the function will call the the
189          *  service directly.  If omitted, the function will
190          *  look up the service in the ObjectMapper.
191          *
192          *  @param path - The path of the object containing
193          *     the property to be tested.
194          *  @param iface - The interface hosting the property
195          *     to be tested.
196          *  @param property - The property to be tested.
197          *  @param service - The DBus service hosting the object.
198          */
199         PropertyConditionBase(
200             const char* path,
201             const char* iface,
202             const char* property,
203             const char* service) :
204             _path(path),
205             _iface(iface),
206             _property(property),
207             _service(service) {}
208 
209         /** @brief Forward comparison to type specific implementation. */
210         virtual bool eval(sdbusplus::message::message&) const = 0;
211 
212         /** @brief Test a property value.
213          *
214          * Make a DBus call and test the value of any property.
215          */
216         bool operator()(
217             sdbusplus::bus::bus&,
218             sdbusplus::message::message&,
219             Manager&) const;
220 
221     private:
222         std::string _path;
223         std::string _iface;
224         std::string _property;
225         const char* _service;
226 };
227 
228 /** @struct PropertyCondition
229  *  @brief Match filter functor that tests a property value.
230  *
231  *  @tparam T - The type of the property being tested.
232  *  @tparam U - The type of the condition checking functor.
233  */
234 template <typename T, typename U>
235 struct PropertyCondition final : public PropertyConditionBase
236 {
237         PropertyCondition() = delete;
238         ~PropertyCondition() = default;
239         PropertyCondition(const PropertyCondition&) = delete;
240         PropertyCondition& operator=(const PropertyCondition&) = delete;
241         PropertyCondition(PropertyCondition&&) = default;
242         PropertyCondition& operator=(PropertyCondition&&) = default;
243 
244         /** @brief Constructor
245          *
246          *  The service argument can be nullptr.  If something
247          *  else is provided the function will call the the
248          *  service directly.  If omitted, the function will
249          *  look up the service in the ObjectMapper.
250          *
251          *  @param path - The path of the object containing
252          *     the property to be tested.
253          *  @param iface - The interface hosting the property
254          *     to be tested.
255          *  @param property - The property to be tested.
256          *  @param condition - The test to run on the property.
257          *  @param service - The DBus service hosting the object.
258          */
259         PropertyCondition(
260             const char* path,
261             const char* iface,
262             const char* property,
263             U&& condition,
264             const char* service) :
265             PropertyConditionBase(path, iface, property, service),
266             _condition(std::forward<decltype(condition)>(condition)) {}
267 
268         /** @brief Test a property value.
269          *
270          * Make a DBus call and test the value of any property.
271          */
272         bool eval(sdbusplus::message::message& msg) const override
273         {
274             sdbusplus::message::variant<T> value;
275             msg.read(value);
276             return _condition(
277                        std::forward<T>(value.template get<T>()));
278         }
279 
280     private:
281         U _condition;
282 };
283 
284 } // namespace property_condition
285 } // namespace details
286 
287 /** @brief Implicit type deduction for constructing PropertyChangedCondition.  */
288 template <typename T>
289 auto propertyChangedTo(
290     const char* iface,
291     const char* property,
292     T&& val)
293 {
294     auto condition = [val = std::forward<T>(val)](T && arg)
295     {
296         return arg == val;
297     };
298     using U = decltype(condition);
299     return details::property_condition::PropertyChangedCondition<T, U>(
300                iface, property, std::move(condition));
301 }
302 
303 /** @brief Implicit type deduction for constructing PropertyCondition.  */
304 template <typename T>
305 auto propertyIs(
306     const char* path,
307     const char* iface,
308     const char* property,
309     T&& val,
310     const char* service = nullptr)
311 {
312     auto condition = [val = std::forward<T>(val)](T && arg)
313     {
314         return arg == val;
315     };
316     using U = decltype(condition);
317     return details::property_condition::PropertyCondition<T, U>(
318                path, iface, property, std::move(condition), service);
319 }
320 
321 } // namespace filters
322 } // namespace manager
323 } // namespace inventory
324 } // namespace phosphor
325 
326 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
327