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