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>(
103                          sdbusplus::message::variant_ns::get<T>(it->second)));
104         }
105         else
106         {
107             try
108             {
109                 auto service = zone.getService(_path, _iface);
110                 auto val = util::SDBusPlus::getProperty<T>(bus,
111                                                            service,
112                                                            _path,
113                                                            _iface,
114                                                            _property);
115                 _handler(zone, std::forward<T>(val));
116             }
117             catch (const sdbusplus::exception::SdBusError&)
118             {
119                 // Property will not be used unless a property changed
120                 // signal message is received for this property.
121             }
122             catch (const util::DBusError&)
123             {
124                 // Property will not be used unless a property changed
125                 // signal message is received for this property.
126             }
127         }
128     }
129 
130 private:
131     const char* _path;
132     const char* _iface;
133     const char* _property;
134     U _handler;
135 };
136 
137 /**
138  * @brief Used to process a Dbus property changed signal event
139  *
140  * @param[in] path - Object path
141  * @param[in] iface - Object interface
142  * @param[in] property - Object property
143  * @param[in] handler - Handler function to perform
144  *
145  * @tparam T - The type of the property
146  * @tparam U - The type of the handler
147  */
148 template <typename T, typename U>
149 auto propertySignal(const char* path,
150                     const char* iface,
151                     const char* property,
152                     U&& handler)
153 {
154     return PropertyChanged<T, U>(path,
155                                  iface,
156                                  property,
157                                  std::forward<U>(handler));
158 }
159 
160 /**
161  * @struct Interface Added
162  * @brief A match filter functor for Dbus interface added signals
163  *
164  * @tparam T - The type of the property value
165  * @tparam U - The type of the handler
166  */
167 template <typename T, typename U>
168 struct InterfaceAdded
169 {
170     InterfaceAdded() = delete;
171     ~InterfaceAdded() = default;
172     InterfaceAdded(const InterfaceAdded&) = default;
173     InterfaceAdded& operator=(const InterfaceAdded&) = default;
174     InterfaceAdded(InterfaceAdded&&) = default;
175     InterfaceAdded& operator=(InterfaceAdded&&) = default;
176     InterfaceAdded(const char* path,
177                    const char* iface,
178                    const char* property,
179                    U&& handler) :
180         _path(path),
181         _iface(iface),
182         _property(property),
183         _handler(std::forward<U>(handler)) { }
184 
185     /** @brief Run signal handler function
186      *
187      * Extract the property from the InterfacesAdded
188      * message and run the handler function.
189      */
190     void operator()(sdbusplus::bus::bus&,
191                     sdbusplus::message::message& msg,
192                     Zone& zone) const
193     {
194         if (msg)
195         {
196             std::map<std::string,
197                      std::map<std::string,
198                               sdbusplus::message::variant<T>>> intfProp;
199             sdbusplus::message::object_path op;
200 
201             msg.read(op);
202             if (static_cast<const std::string&>(op) != _path)
203             {
204                 // Object path does not match this handler's path
205                 return;
206             }
207 
208             msg.read(intfProp);
209             auto itIntf = intfProp.find(_iface);
210             if (itIntf == intfProp.cend())
211             {
212                 // Interface not found on this handler's path
213                 return;
214             }
215             auto itProp = itIntf->second.find(_property);
216             if (itProp == itIntf->second.cend())
217             {
218                 // Property not found on this handler's path
219                 return;
220             }
221 
222             _handler(zone, std::forward<T>(
223                          sdbusplus::message::variant_ns::get<T>(itProp->second)));
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 util::DBusMethodError& e)
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