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