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 
19 /**
20  * @brief Create a handler function object
21  *
22  * @param[in] handler - The handler being created
23  *
24  * @return - The created handler function object
25  */
26 template <typename T>
27 auto make_handler(T&& handler)
28 {
29     return Handler(std::forward<T>(handler));
30 }
31 
32 /**
33  * @brief Create an action function object
34  *
35  * @param[in] action - The action being created
36  *
37  * @return - The created action function object
38  */
39 template <typename T>
40 auto make_action(T&& action)
41 {
42     return Action(std::forward<T>(action));
43 }
44 
45 /**
46  * @struct Property Changed
47  * @brief A match filter functor for Dbus property value changed signals
48  *
49  * @tparam T - The type of the property value
50  * @tparam U - The type of the handler
51  */
52 template <typename T, typename U>
53 struct PropertyChanged
54 {
55     PropertyChanged() = delete;
56     ~PropertyChanged() = default;
57     PropertyChanged(const PropertyChanged&) = default;
58     PropertyChanged& operator=(const PropertyChanged&) = default;
59     PropertyChanged(PropertyChanged&&) = default;
60     PropertyChanged& operator=(PropertyChanged&&) = default;
61     PropertyChanged(const char* path,
62                     const char* iface,
63                     const char* property,
64                     U&& handler) :
65         _path(path),
66         _iface(iface),
67         _property(property),
68         _handler(std::forward<U>(handler)) { }
69 
70     /** @brief Run signal handler function
71      *
72      * Extract the property from the PropertiesChanged
73      * message (or read the property when the message is null)
74      * and run the handler function.
75      */
76     void operator()(sdbusplus::bus::bus& bus,
77                     sdbusplus::message::message& msg,
78                     Zone& zone) const
79     {
80         if (msg)
81         {
82             std::map<std::string, sdbusplus::message::variant<T>> properties;
83             std::string iface;
84 
85             msg.read(iface);
86             if (iface != _iface)
87             {
88                 return;
89             }
90 
91             msg.read(properties);
92             auto it = properties.find(_property);
93             if (it == properties.cend())
94             {
95                 log<level::ERR>("Unable to find property on interface",
96                                 entry("PROPERTY=%s", _property),
97                                 entry("INTERFACE=%s", _iface),
98                                 entry("PATH=%s", _path));
99                 return;
100             }
101 
102             _handler(zone, std::forward<T>(it->second.template get<T>()));
103         }
104         else
105         {
106             try
107             {
108                 auto service = zone.getService(_path, _iface);
109                 auto val = util::SDBusPlus::getProperty<T>(bus,
110                                                            service,
111                                                            _path,
112                                                            _iface,
113                                                            _property);
114                 _handler(zone, std::forward<T>(val));
115             }
116             catch (const sdbusplus::exception::SdBusError&)
117             {
118                 // Property will not be used unless a property changed
119                 // signal message is received for this property.
120             }
121             catch (const util::DBusError&)
122             {
123                 // Property will not be used unless a property changed
124                 // signal message is received for this 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 Interface Removed
257  * @brief A match filter functor for Dbus interface removed signals
258  *
259  * @tparam U - The type of the handler
260  */
261 template <typename U>
262 struct InterfaceRemoved
263 {
264     InterfaceRemoved() = delete;
265     ~InterfaceRemoved() = default;
266     InterfaceRemoved(const InterfaceRemoved&) = default;
267     InterfaceRemoved& operator=(const InterfaceRemoved&) = default;
268     InterfaceRemoved(InterfaceRemoved&&) = default;
269     InterfaceRemoved& operator=(InterfaceRemoved&&) = default;
270     InterfaceRemoved(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 property from the InterfacesRemoved
280      * message and run the handler function.
281      */
282     void operator()(sdbusplus::bus::bus&,
283                     sdbusplus::message::message& msg,
284                     Zone& zone) const
285     {
286         if (msg)
287         {
288             std::vector<std::string> intfs;
289             sdbusplus::message::object_path op;
290 
291             msg.read(op);
292             if (static_cast<const std::string&>(op) != _path)
293             {
294                 // Object path does not match this handler's path
295                 return;
296             }
297 
298             msg.read(intfs);
299             auto itIntf = std::find(intfs.begin(), intfs.end(), _iface);
300             if (itIntf == intfs.cend())
301             {
302                 // Interface not found on this handler's path
303                 return;
304             }
305 
306             _handler(zone);
307         }
308     }
309 
310 private:
311     const char* _path;
312     const char* _iface;
313     U _handler;
314 };
315 
316 /**
317  * @brief Used to process a Dbus interface removed signal event
318  *
319  * @param[in] path - Object path
320  * @param[in] iface - Object interface
321  * @param[in] handler - Handler function to perform
322  *
323  * @tparam U - The type of the handler
324  */
325 template <typename U>
326 auto objectSignal(const char* path,
327                   const char* iface,
328                   U&& handler)
329 {
330     return InterfaceRemoved<U>(path,
331                                iface,
332                                std::forward<U>(handler));
333 }
334 
335 /**
336  * @struct Name Owner Changed
337  * @brief A match filter functor for Dbus name owner changed signals
338  *
339  * @tparam U - The type of the handler
340  */
341 template <typename U>
342 struct NameOwnerChanged
343 {
344     NameOwnerChanged() = delete;
345     ~NameOwnerChanged() = default;
346     NameOwnerChanged(const NameOwnerChanged&) = default;
347     NameOwnerChanged& operator=(const NameOwnerChanged&) = default;
348     NameOwnerChanged(NameOwnerChanged&&) = default;
349     NameOwnerChanged& operator=(NameOwnerChanged&&) = default;
350     NameOwnerChanged(const char* path,
351                      const char* iface,
352                      U&& handler) :
353         _path(path),
354         _iface(iface),
355         _handler(std::forward<U>(handler)) { }
356 
357     /** @brief Run signal handler function
358      *
359      * Extract the name owner from the NameOwnerChanged
360      * message (or read the name owner when the message is null)
361      * and run the handler function.
362      */
363     void operator()(sdbusplus::bus::bus& bus,
364                     sdbusplus::message::message& msg,
365                     Zone& zone) const
366     {
367         std::string name;
368         bool hasOwner = false;
369         if (msg)
370         {
371             // Handle NameOwnerChanged signals
372             msg.read(name);
373 
374             std::string oldOwn;
375             msg.read(oldOwn);
376 
377             std::string newOwn;
378             msg.read(newOwn);
379             if (!newOwn.empty())
380             {
381                 hasOwner = true;
382             }
383         }
384         else
385         {
386             try
387             {
388                 // Initialize NameOwnerChanged data store with service name
389                 name = zone.getService(_path, _iface);
390                 hasOwner = util::SDBusPlus::callMethodAndRead<bool>(
391                         bus,
392                         "org.freedesktop.DBus",
393                         "/org/freedesktop/DBus",
394                         "org.freedesktop.DBus",
395                         "NameHasOwner",
396                         name);
397             }
398             catch (const util::DBusMethodError& e)
399             {
400                 // Failed to get service name owner state
401                 hasOwner = false;
402             }
403         }
404 
405         _handler(zone, name, hasOwner);
406     }
407 
408 private:
409     const char* _path;
410     const char* _iface;
411     U _handler;
412 };
413 
414 /**
415  * @brief Used to process a Dbus name owner changed signal event
416  *
417  * @param[in] path - Object path
418  * @param[in] iface - Object interface
419  * @param[in] handler - Handler function to perform
420  *
421  * @tparam U - The type of the handler
422  *
423  * @return - The NameOwnerChanged signal struct
424  */
425 template <typename U>
426 auto ownerSignal(const char* path,
427                  const char* iface,
428                  U&& handler)
429 {
430     return NameOwnerChanged<U>(path,
431                                iface,
432                                std::forward<U>(handler));
433 }
434 
435 } // namespace control
436 } // namespace fan
437 } // namespace phosphor
438