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