xref: /openbmc/phosphor-fan-presence/control/functor.hpp (revision 8e5d197b840d4498dcb714b60cc1d38202a7a7a7)
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 val = util::SDBusPlus::getProperty<T>(bus,
111                                                            _path,
112                                                            _iface,
113                                                            _property);
114                 _handler(zone, std::forward<T>(val));
115             }
116             catch (const InternalFailure& ife)
117             {
118                 // Property will not be used unless a property changed
119                 // signal message is received for this property.
120                 log<level::INFO>(
121                     "Property not used, unless PropertyChanged signal received",
122                     entry("PATH=%s", _path),
123                     entry("INTERFACE=%s", _iface),
124                     entry("PROPERTY=%s", _property));
125             }
126         }
127     }
128 
129 private:
130     const char* _path;
131     const char* _iface;
132     const char* _property;
133     U _handler;
134 };
135 
136 /**
137  * @brief Used to process a Dbus property changed signal event
138  *
139  * @param[in] path - Object path
140  * @param[in] iface - Object interface
141  * @param[in] property - Object property
142  * @param[in] handler - Handler function to perform
143  *
144  * @tparam T - The type of the property
145  * @tparam U - The type of the handler
146  */
147 template <typename T, typename U>
148 auto propertySignal(const char* path,
149                     const char* iface,
150                     const char* property,
151                     U&& handler)
152 {
153     return PropertyChanged<T, U>(path,
154                                  iface,
155                                  property,
156                                  std::forward<U>(handler));
157 }
158 
159 /**
160  * @struct Interface Added
161  * @brief A match filter functor for Dbus interface added signals
162  *
163  * @tparam T - The type of the property value
164  * @tparam U - The type of the handler
165  */
166 template <typename T, typename U>
167 struct InterfaceAdded
168 {
169     InterfaceAdded() = delete;
170     ~InterfaceAdded() = default;
171     InterfaceAdded(const InterfaceAdded&) = default;
172     InterfaceAdded& operator=(const InterfaceAdded&) = default;
173     InterfaceAdded(InterfaceAdded&&) = default;
174     InterfaceAdded& operator=(InterfaceAdded&&) = default;
175     InterfaceAdded(const char* path,
176                    const char* iface,
177                    const char* property,
178                    U&& handler) :
179         _path(path),
180         _iface(iface),
181         _property(property),
182         _handler(std::forward<U>(handler)) { }
183 
184     /** @brief Run signal handler function
185      *
186      * Extract the property from the InterfacesAdded
187      * message and run the handler function.
188      */
189     void operator()(sdbusplus::bus::bus&,
190                     sdbusplus::message::message& msg,
191                     Zone& zone) const
192     {
193         if (msg)
194         {
195             std::map<std::string,
196                      std::map<std::string,
197                               sdbusplus::message::variant<T>>> intfProp;
198             sdbusplus::message::object_path op;
199 
200             msg.read(op);
201             if (static_cast<const std::string&>(op) != _path)
202             {
203                 // Object path does not match this handler's path
204                 return;
205             }
206 
207             msg.read(intfProp);
208             auto itIntf = intfProp.find(_iface);
209             if (itIntf == intfProp.cend())
210             {
211                 // Interface not found on this handler's path
212                 return;
213             }
214             auto itProp = itIntf->second.find(_property);
215             if (itProp == itIntf->second.cend())
216             {
217                 // Property not found on this handler's path
218                 return;
219             }
220 
221             _handler(zone, std::forward<T>(itProp->second.template get<T>()));
222         }
223     }
224 
225 private:
226     const char* _path;
227     const char* _iface;
228     const char* _property;
229     U _handler;
230 };
231 
232 /**
233  * @brief Used to process a Dbus interface added signal event
234  *
235  * @param[in] path - Object path
236  * @param[in] iface - Object interface
237  * @param[in] property - Object property
238  * @param[in] handler - Handler function to perform
239  *
240  * @tparam T - The type of the property
241  * @tparam U - The type of the handler
242  */
243 template <typename T, typename U>
244 auto objectSignal(const char* path,
245                   const char* iface,
246                   const char* property,
247                   U&& handler)
248 {
249     return InterfaceAdded<T, U>(path,
250                                 iface,
251                                 property,
252                                 std::forward<U>(handler));
253 }
254 
255 /**
256  * @struct Name Owner Changed
257  * @brief A match filter functor for Dbus name owner changed signals
258  *
259  * @tparam U - The type of the handler
260  */
261 template <typename U>
262 struct NameOwnerChanged
263 {
264     NameOwnerChanged() = delete;
265     ~NameOwnerChanged() = default;
266     NameOwnerChanged(const NameOwnerChanged&) = default;
267     NameOwnerChanged& operator=(const NameOwnerChanged&) = default;
268     NameOwnerChanged(NameOwnerChanged&&) = default;
269     NameOwnerChanged& operator=(NameOwnerChanged&&) = default;
270     NameOwnerChanged(const char* path,
271                      const char* iface,
272                      U&& handler) :
273         _path(path),
274         _iface(iface),
275         _handler(std::forward<U>(handler)) { }
276 
277     /** @brief Run signal handler function
278      *
279      * Extract the name owner from the NameOwnerChanged
280      * message (or read the name owner when the message is null)
281      * and run the handler function.
282      */
283     void operator()(sdbusplus::bus::bus& bus,
284                     sdbusplus::message::message& msg,
285                     Zone& zone) const
286     {
287         std::string name;
288         bool hasOwner = false;
289         if (msg)
290         {
291             // Handle NameOwnerChanged signals
292             msg.read(name);
293 
294             std::string oldOwn;
295             msg.read(oldOwn);
296 
297             std::string newOwn;
298             msg.read(newOwn);
299             if (!newOwn.empty())
300             {
301                 hasOwner = true;
302             }
303         }
304         else
305         {
306             try
307             {
308                 // Initialize NameOwnerChanged data store with service name
309                 name = util::SDBusPlus::getService(bus,
310                                                    _path,
311                                                    _iface);
312                 hasOwner = util::SDBusPlus::callMethodAndRead<bool>(
313                         bus,
314                         "org.freedesktop.DBus",
315                         "/org/freedesktop/DBus",
316                         "org.freedesktop.DBus",
317                         "NameHasOwner",
318                         name);
319             }
320             catch (const InternalFailure& ife)
321             {
322                 // Failed to get service name owner state
323                 hasOwner = false;
324             }
325         }
326 
327         _handler(zone, name, hasOwner);
328     }
329 
330 private:
331     const char* _path;
332     const char* _iface;
333     U _handler;
334 };
335 
336 /**
337  * @brief Used to process a Dbus name owner changed signal event
338  *
339  * @param[in] path - Object path
340  * @param[in] iface - Object interface
341  * @param[in] handler - Handler function to perform
342  *
343  * @tparam U - The type of the handler
344  *
345  * @return - The NameOwnerChanged signal struct
346  */
347 template <typename U>
348 auto ownerSignal(const char* path,
349                  const char* iface,
350                  U&& handler)
351 {
352     return NameOwnerChanged<U>(path,
353                                iface,
354                                std::forward<U>(handler));
355 }
356 
357 } // namespace control
358 } // namespace fan
359 } // namespace phosphor
360