xref: /openbmc/phosphor-fan-presence/control/functor.hpp (revision 33618bc1845e5e7c4e12a2d2ed7f1c47a6a03de1)
1 #pragma once
2 
3 #include "types.hpp"
4 #include "sdbusplus.hpp"
5 #include <phosphor-logging/log.hpp>
6 
7 namespace phosphor
8 {
9 namespace fan
10 {
11 namespace control
12 {
13 class Zone;
14 
15 using namespace phosphor::fan;
16 using namespace sdbusplus::bus::match;
17 using namespace phosphor::logging;
18 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
19                                 Error::InternalFailure;
20 
21 /**
22  * @brief Create a handler function object
23  *
24  * @param[in] handler - The handler being created
25  *
26  * @return - The created handler function object
27  */
28 template <typename T>
29 auto make_handler(T&& handler)
30 {
31     return Handler(std::forward<T>(handler));
32 }
33 
34 /**
35  * @brief Create an action function object
36  *
37  * @param[in] action - The action being created
38  *
39  * @return - The created action function object
40  */
41 template <typename T>
42 auto make_action(T&& action)
43 {
44     return Action(std::forward<T>(action));
45 }
46 
47 /**
48  * @struct Property Changed
49  * @brief A match filter functor for Dbus property value changed signals
50  *
51  * @tparam T - The type of the property value
52  * @tparam U - The type of the handler
53  */
54 template <typename T, typename U>
55 struct PropertyChanged
56 {
57     PropertyChanged() = delete;
58     ~PropertyChanged() = default;
59     PropertyChanged(const PropertyChanged&) = default;
60     PropertyChanged& operator=(const PropertyChanged&) = default;
61     PropertyChanged(PropertyChanged&&) = default;
62     PropertyChanged& operator=(PropertyChanged&&) = default;
63     PropertyChanged(const char* path,
64                     const char* iface,
65                     const char* property,
66                     U&& handler) :
67         _path(path),
68         _iface(iface),
69         _property(property),
70         _handler(std::forward<U>(handler)) { }
71 
72     /** @brief Run signal handler function
73      *
74      * Extract the property from the PropertiesChanged
75      * message (or read the property when the message is null)
76      * and run the handler function.
77      */
78     void operator()(sdbusplus::bus::bus& bus,
79                     sdbusplus::message::message& msg,
80                     Zone& zone) const
81     {
82         if (msg)
83         {
84             std::map<std::string, sdbusplus::message::variant<T>> properties;
85             std::string iface;
86 
87             msg.read(iface);
88             if (iface != _iface)
89             {
90                 return;
91             }
92 
93             msg.read(properties);
94             auto it = properties.find(_property);
95             if (it == properties.cend())
96             {
97                 log<level::ERR>("Unable to find property on interface",
98                                 entry("PROPERTY=%s", _property),
99                                 entry("INTERFACE=%s", _iface),
100                                 entry("PATH=%s", _path));
101                 return;
102             }
103 
104             _handler(zone, std::forward<T>(it->second.template get<T>()));
105         }
106         else
107         {
108             try
109             {
110                 auto service = zone.getService(_path, _iface);
111                 auto val = util::SDBusPlus::getProperty<T>(bus,
112                                                            service,
113                                                            _path,
114                                                            _iface,
115                                                            _property);
116                 _handler(zone, std::forward<T>(val));
117             }
118             catch (const InternalFailure& ife)
119             {
120                 // Property will not be used unless a property changed
121                 // signal message is received for this property.
122                 log<level::INFO>(
123                     "Property not used, unless PropertyChanged signal received",
124                     entry("PATH=%s", _path),
125                     entry("INTERFACE=%s", _iface),
126                     entry("PROPERTY=%s", _property));
127             }
128         }
129     }
130 
131 private:
132     const char* _path;
133     const char* _iface;
134     const char* _property;
135     U _handler;
136 };
137 
138 /**
139  * @brief Used to process a Dbus property changed signal event
140  *
141  * @param[in] path - Object path
142  * @param[in] iface - Object interface
143  * @param[in] property - Object property
144  * @param[in] handler - Handler function to perform
145  *
146  * @tparam T - The type of the property
147  * @tparam U - The type of the handler
148  */
149 template <typename T, typename U>
150 auto propertySignal(const char* path,
151                     const char* iface,
152                     const char* property,
153                     U&& handler)
154 {
155     return PropertyChanged<T, U>(path,
156                                  iface,
157                                  property,
158                                  std::forward<U>(handler));
159 }
160 
161 /**
162  * @struct Interface Added
163  * @brief A match filter functor for Dbus interface added signals
164  *
165  * @tparam T - The type of the property value
166  * @tparam U - The type of the handler
167  */
168 template <typename T, typename U>
169 struct InterfaceAdded
170 {
171     InterfaceAdded() = delete;
172     ~InterfaceAdded() = default;
173     InterfaceAdded(const InterfaceAdded&) = default;
174     InterfaceAdded& operator=(const InterfaceAdded&) = default;
175     InterfaceAdded(InterfaceAdded&&) = default;
176     InterfaceAdded& operator=(InterfaceAdded&&) = default;
177     InterfaceAdded(const char* path,
178                    const char* iface,
179                    const char* property,
180                    U&& handler) :
181         _path(path),
182         _iface(iface),
183         _property(property),
184         _handler(std::forward<U>(handler)) { }
185 
186     /** @brief Run signal handler function
187      *
188      * Extract the property from the InterfacesAdded
189      * message and run the handler function.
190      */
191     void operator()(sdbusplus::bus::bus&,
192                     sdbusplus::message::message& msg,
193                     Zone& zone) const
194     {
195         if (msg)
196         {
197             std::map<std::string,
198                      std::map<std::string,
199                               sdbusplus::message::variant<T>>> intfProp;
200             sdbusplus::message::object_path op;
201 
202             msg.read(op);
203             if (static_cast<const std::string&>(op) != _path)
204             {
205                 // Object path does not match this handler's path
206                 return;
207             }
208 
209             msg.read(intfProp);
210             auto itIntf = intfProp.find(_iface);
211             if (itIntf == intfProp.cend())
212             {
213                 // Interface not found on this handler's path
214                 return;
215             }
216             auto itProp = itIntf->second.find(_property);
217             if (itProp == itIntf->second.cend())
218             {
219                 // Property not found on this handler's path
220                 return;
221             }
222 
223             _handler(zone, std::forward<T>(itProp->second.template get<T>()));
224         }
225     }
226 
227 private:
228     const char* _path;
229     const char* _iface;
230     const char* _property;
231     U _handler;
232 };
233 
234 /**
235  * @brief Used to process a Dbus interface added signal event
236  *
237  * @param[in] path - Object path
238  * @param[in] iface - Object interface
239  * @param[in] property - Object property
240  * @param[in] handler - Handler function to perform
241  *
242  * @tparam T - The type of the property
243  * @tparam U - The type of the handler
244  */
245 template <typename T, typename U>
246 auto objectSignal(const char* path,
247                   const char* iface,
248                   const char* property,
249                   U&& handler)
250 {
251     return InterfaceAdded<T, U>(path,
252                                 iface,
253                                 property,
254                                 std::forward<U>(handler));
255 }
256 
257 /**
258  * @struct Interface Removed
259  * @brief A match filter functor for Dbus interface removed signals
260  *
261  * @tparam U - The type of the handler
262  */
263 template <typename U>
264 struct InterfaceRemoved
265 {
266     InterfaceRemoved() = delete;
267     ~InterfaceRemoved() = default;
268     InterfaceRemoved(const InterfaceRemoved&) = default;
269     InterfaceRemoved& operator=(const InterfaceRemoved&) = default;
270     InterfaceRemoved(InterfaceRemoved&&) = default;
271     InterfaceRemoved& operator=(InterfaceRemoved&&) = default;
272     InterfaceRemoved(const char* path,
273                      const char* iface,
274                      U&& handler) :
275         _path(path),
276         _iface(iface),
277         _handler(std::forward<U>(handler)) { }
278 
279     /** @brief Run signal handler function
280      *
281      * Extract the property from the InterfacesRemoved
282      * message and run the handler function.
283      */
284     void operator()(sdbusplus::bus::bus&,
285                     sdbusplus::message::message& msg,
286                     Zone& zone) const
287     {
288         if (msg)
289         {
290             std::vector<std::string> intfs;
291             sdbusplus::message::object_path op;
292 
293             msg.read(op);
294             if (static_cast<const std::string&>(op) != _path)
295             {
296                 // Object path does not match this handler's path
297                 return;
298             }
299 
300             msg.read(intfs);
301             auto itIntf = std::find(intfs.begin(), intfs.end(), _iface);
302             if (itIntf == intfs.cend())
303             {
304                 // Interface not found on this handler's path
305                 return;
306             }
307 
308             _handler(zone);
309         }
310     }
311 
312 private:
313     const char* _path;
314     const char* _iface;
315     U _handler;
316 };
317 
318 /**
319  * @brief Used to process a Dbus interface removed signal event
320  *
321  * @param[in] path - Object path
322  * @param[in] iface - Object interface
323  * @param[in] handler - Handler function to perform
324  *
325  * @tparam U - The type of the handler
326  */
327 template <typename U>
328 auto objectSignal(const char* path,
329                   const char* iface,
330                   U&& handler)
331 {
332     return InterfaceRemoved<U>(path,
333                                iface,
334                                std::forward<U>(handler));
335 }
336 
337 /**
338  * @struct Name Owner Changed
339  * @brief A match filter functor for Dbus name owner changed signals
340  *
341  * @tparam U - The type of the handler
342  */
343 template <typename U>
344 struct NameOwnerChanged
345 {
346     NameOwnerChanged() = delete;
347     ~NameOwnerChanged() = default;
348     NameOwnerChanged(const NameOwnerChanged&) = default;
349     NameOwnerChanged& operator=(const NameOwnerChanged&) = default;
350     NameOwnerChanged(NameOwnerChanged&&) = default;
351     NameOwnerChanged& operator=(NameOwnerChanged&&) = default;
352     NameOwnerChanged(const char* path,
353                      const char* iface,
354                      U&& handler) :
355         _path(path),
356         _iface(iface),
357         _handler(std::forward<U>(handler)) { }
358 
359     /** @brief Run signal handler function
360      *
361      * Extract the name owner from the NameOwnerChanged
362      * message (or read the name owner when the message is null)
363      * and run the handler function.
364      */
365     void operator()(sdbusplus::bus::bus& bus,
366                     sdbusplus::message::message& msg,
367                     Zone& zone) const
368     {
369         std::string name;
370         bool hasOwner = false;
371         if (msg)
372         {
373             // Handle NameOwnerChanged signals
374             msg.read(name);
375 
376             std::string oldOwn;
377             msg.read(oldOwn);
378 
379             std::string newOwn;
380             msg.read(newOwn);
381             if (!newOwn.empty())
382             {
383                 hasOwner = true;
384             }
385         }
386         else
387         {
388             try
389             {
390                 // Initialize NameOwnerChanged data store with service name
391                 name = zone.getService(_path, _iface);
392                 hasOwner = util::SDBusPlus::callMethodAndRead<bool>(
393                         bus,
394                         "org.freedesktop.DBus",
395                         "/org/freedesktop/DBus",
396                         "org.freedesktop.DBus",
397                         "NameHasOwner",
398                         name);
399             }
400             catch (const InternalFailure& ife)
401             {
402                 // Failed to get service name owner state
403                 hasOwner = false;
404             }
405         }
406 
407         _handler(zone, name, hasOwner);
408     }
409 
410 private:
411     const char* _path;
412     const char* _iface;
413     U _handler;
414 };
415 
416 /**
417  * @brief Used to process a Dbus name owner changed signal event
418  *
419  * @param[in] path - Object path
420  * @param[in] iface - Object interface
421  * @param[in] handler - Handler function to perform
422  *
423  * @tparam U - The type of the handler
424  *
425  * @return - The NameOwnerChanged signal struct
426  */
427 template <typename U>
428 auto ownerSignal(const char* path,
429                  const char* iface,
430                  U&& handler)
431 {
432     return NameOwnerChanged<U>(path,
433                                iface,
434                                std::forward<U>(handler));
435 }
436 
437 } // namespace control
438 } // namespace fan
439 } // namespace phosphor
440