blob: 54cf6d4cbc7ee7da66527f3eb89e6ea332a04011 [file] [log] [blame]
Matt Spinler2a28c932020-02-03 14:23:40 -06001#pragma once
2
3#include "dbus_types.hpp"
4
5#include <sdbusplus/bus/match.hpp>
6
7namespace openpower::pels
8{
9
Matt Spinler2a28c932020-02-03 14:23:40 -060010namespace match_rules = sdbusplus::bus::match::rules;
11
12/**
13 * @class DBusWatcher
14 *
15 * The base class for the PropertyWatcher and InterfaceWatcher classes.
16 */
17class DBusWatcher
18{
19 public:
20 DBusWatcher() = delete;
21 virtual ~DBusWatcher() = default;
22 DBusWatcher(const DBusWatcher&) = default;
23 DBusWatcher& operator=(const DBusWatcher&) = default;
24 DBusWatcher(DBusWatcher&&) = default;
25 DBusWatcher& operator=(DBusWatcher&&) = default;
26
27 /**
28 * @brief Constructor
29 *
30 * @param[in] path - The D-Bus path that will be watched
31 * @param[in] interface - The D-Bus interface that will be watched
32 */
33 DBusWatcher(const std::string& path, const std::string& interface) :
34 _path(path), _interface(interface)
35 {
36 }
37
38 protected:
39 /**
40 * @brief The D-Bus path
41 */
42 std::string _path;
43
44 /**
45 * @brief The D-Bus interface
46 */
47 std::string _interface;
48
49 /**
50 * @brief The match objects for the propertiesChanged and
51 * interfacesAdded signals.
52 */
53 std::vector<sdbusplus::bus::match_t> _matches;
54};
55
56/**
57 * @class PropertyWatcher
58 *
59 * This class allows the user to be kept up to data with a D-Bus
60 * property's value. It does this by calling a user specified function
61 * that is passed the variant that contains the property's value when:
62 *
63 * 1) The property is read when the class is constructed, if
64 * the property is on D-Bus at the time.
65 * 2) The property changes (via a property changed signal).
66 * 3) An interfacesAdded signal is received with that property.
67 *
68 * The DataInterface class is used to access D-Bus, and is a template
69 * to avoid any circular include issues as that class is one of the
70 * users of this one.
71 */
72template <typename DataIface>
73class PropertyWatcher : public DBusWatcher
74{
75 public:
76 PropertyWatcher() = delete;
77 ~PropertyWatcher() = default;
78 PropertyWatcher(const PropertyWatcher&) = delete;
79 PropertyWatcher& operator=(const PropertyWatcher&) = delete;
80 PropertyWatcher(PropertyWatcher&&) = delete;
81 PropertyWatcher& operator=(PropertyWatcher&&) = delete;
82
83 using PropertySetFunc = std::function<void(const DBusValue&)>;
84
85 /**
86 * @brief Constructor
87 *
88 * Reads the property if it is on D-Bus, and sets up the match
89 * objects for the propertiesChanged and interfacesAdded signals.
90 *
91 * @param[in] bus - The sdbusplus bus object
92 * @param[in] path - The D-Bus path of the property
93 * @param[in] interface - The D-Bus interface that contains the property
94 * @param[in] propertyName - The property name
Matt Spinlerc1746f62020-02-21 15:03:52 -060095 * @param[in] service - The D-Bus service to use for the property read.
96 * Can be empty to look it up instead.
Matt Spinler2a28c932020-02-03 14:23:40 -060097 * @param[in] dataIface - The DataInterface object
98 * @param[in] func - The callback used any time the property is read
99 */
100 PropertyWatcher(sdbusplus::bus::bus& bus, const std::string& path,
101 const std::string& interface,
Matt Spinlerc1746f62020-02-21 15:03:52 -0600102 const std::string& propertyName, const std::string& service,
103 const DataIface& dataIface, PropertySetFunc func) :
Matt Spinler2a28c932020-02-03 14:23:40 -0600104 DBusWatcher(path, interface),
105 _name(propertyName), _setFunc(func)
106 {
Matt Spinler2a28c932020-02-03 14:23:40 -0600107 _matches.emplace_back(
108 bus, match_rules::propertiesChanged(_path, _interface),
109 std::bind(std::mem_fn(&PropertyWatcher::propChanged), this,
110 std::placeholders::_1));
111
112 _matches.emplace_back(
113 bus,
114 match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
115 std::bind(std::mem_fn(&PropertyWatcher::interfaceAdded), this,
116 std::placeholders::_1));
Matt Spinler51e927c2020-02-27 10:35:10 -0600117
118 try
119 {
120 read(dataIface, service);
121 }
Patrick Williamse99a4fd2021-09-02 09:44:53 -0500122 catch (const sdbusplus::exception::exception& e)
Matt Spinler51e927c2020-02-27 10:35:10 -0600123 {
124 // Path doesn't exist now
125 }
Matt Spinler2a28c932020-02-03 14:23:40 -0600126 }
127
128 /**
Matt Spinlerc1746f62020-02-21 15:03:52 -0600129 * @brief Constructor
130 *
131 * Reads the property if it is on D-Bus, and sets up the match
132 * objects for the propertiesChanged and interfacesAdded signals.
133 *
134 * Unlike the other constructor, this contructor doesn't take the
135 * service to use for the property read so it will look it up with
136 * an ObjectMapper GetObject call.
137 *
138 * @param[in] bus - The sdbusplus bus object
139 * @param[in] path - The D-Bus path of the property
140 * @param[in] interface - The D-Bus interface that contains the property
141 * @param[in] propertyName - The property name
142 * @param[in] dataIface - The DataInterface object
143 * @param[in] func - The callback used any time the property is read
144 */
145 PropertyWatcher(sdbusplus::bus::bus& bus, const std::string& path,
146 const std::string& interface,
147 const std::string& propertyName, const DataIface& dataIface,
148 PropertySetFunc func) :
149 PropertyWatcher(bus, path, interface, propertyName, "", dataIface, func)
150 {
151 }
152
153 /**
Matt Spinler2a28c932020-02-03 14:23:40 -0600154 * @brief Reads the property on D-Bus, and calls
155 * the user defined function with the value.
156 *
Matt Spinlerc1746f62020-02-21 15:03:52 -0600157 * If the passed in service is empty, look up the service to use.
158 *
Matt Spinler2a28c932020-02-03 14:23:40 -0600159 * @param[in] dataIface - The DataInterface object
Matt Spinlerc1746f62020-02-21 15:03:52 -0600160 * @param[in] service - The D-Bus service to make the getProperty
161 * call with, if not empty
Matt Spinler2a28c932020-02-03 14:23:40 -0600162 */
Matt Spinlerc1746f62020-02-21 15:03:52 -0600163 void read(const DataIface& dataIface, std::string service)
Matt Spinler2a28c932020-02-03 14:23:40 -0600164 {
Matt Spinlerc1746f62020-02-21 15:03:52 -0600165 if (service.empty())
166 {
167 service = dataIface.getService(_path, _interface);
168 }
169
Matt Spinler2a28c932020-02-03 14:23:40 -0600170 if (!service.empty())
171 {
172 DBusValue value;
173 dataIface.getProperty(service, _path, _interface, _name, value);
174
175 _setFunc(value);
176 }
177 }
178
179 /**
180 * @brief The propertiesChanged callback
181 *
182 * Calls the user defined function with the property value
183 *
184 * @param[in] msg - The sdbusplus message object
185 */
186 void propChanged(sdbusplus::message::message& msg)
187 {
188 DBusInterface interface;
189 DBusPropertyMap properties;
190
191 msg.read(interface, properties);
192
193 auto prop = properties.find(_name);
194 if (prop != properties.end())
195 {
196 _setFunc(prop->second);
197 }
198 }
199
200 /**
201 * @brief The interfacesAdded callback
202 *
203 * Calls the user defined function with the property value
204 *
205 * @param[in] msg - The sdbusplus message object
206 */
207 void interfaceAdded(sdbusplus::message::message& msg)
208 {
209 sdbusplus::message::object_path path;
210 DBusInterfaceMap interfaces;
211
212 msg.read(path, interfaces);
213
214 auto iface = interfaces.find(_interface);
215 if (iface != interfaces.end())
216 {
217 auto prop = iface->second.find(_name);
218 if (prop != iface->second.end())
219 {
220 _setFunc(prop->second);
221 }
222 }
223 }
224
225 private:
226 /**
227 * @brief The D-Bus property name
228 */
229 std::string _name;
230
231 /**
232 * @brief The function that will be called any time the
233 * property is read.
234 */
235 PropertySetFunc _setFunc;
236};
237
238/**
239 * @class InterfaceWatcher
240 *
241 * This class allows the user to be kept up to data with a D-Bus
242 * interface's properties.. It does this by calling a user specified
243 * function that is passed a map of the D-Bus property names and values
244 * on that interface when:
245 *
246 * 1) The interface is read when the class is constructed, if
247 * the interface is on D-Bus at the time.
248 * 2) The interface has a property that changes (via a property changed signal).
249 * 3) An interfacesAdded signal is received.
250 *
251 * The DataInterface class is used to access D-Bus, and is a template
252 * to avoid any circular include issues as that class is one of the
253 * users of this one.
254 */
255template <typename DataIface>
256class InterfaceWatcher : public DBusWatcher
257{
258 public:
259 InterfaceWatcher() = delete;
260 ~InterfaceWatcher() = default;
261 InterfaceWatcher(const InterfaceWatcher&) = delete;
262 InterfaceWatcher& operator=(const InterfaceWatcher&) = delete;
263 InterfaceWatcher(InterfaceWatcher&&) = delete;
264 InterfaceWatcher& operator=(InterfaceWatcher&&) = delete;
265
266 using InterfaceSetFunc = std::function<void(const DBusPropertyMap&)>;
267
268 /**
269 * @brief Constructor
270 *
271 * Reads all properties on the interface if it is on D-Bus,
272 * and sets up the match objects for the propertiesChanged
273 * and interfacesAdded signals.
274 *
275 * @param[in] bus - The sdbusplus bus object
276 * @param[in] path - The D-Bus path of the property
277 * @param[in] interface - The D-Bus interface that contains the property
278 * @param[in] dataIface - The DataInterface object
279 * @param[in] func - The callback used any time the property is read
280 */
281 InterfaceWatcher(sdbusplus::bus::bus& bus, const std::string& path,
282 const std::string& interface, const DataIface& dataIface,
283 InterfaceSetFunc func) :
284 DBusWatcher(path, interface),
285 _setFunc(func)
286 {
Matt Spinler2a28c932020-02-03 14:23:40 -0600287 _matches.emplace_back(
288 bus, match_rules::propertiesChanged(_path, _interface),
289 std::bind(std::mem_fn(&InterfaceWatcher::propChanged), this,
290 std::placeholders::_1));
291
292 _matches.emplace_back(
293 bus,
294 match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
295 std::bind(std::mem_fn(&InterfaceWatcher::interfaceAdded), this,
296 std::placeholders::_1));
Matt Spinler51e927c2020-02-27 10:35:10 -0600297
298 try
299 {
300 read(dataIface);
301 }
Patrick Williamse99a4fd2021-09-02 09:44:53 -0500302 catch (const sdbusplus::exception::exception& e)
Matt Spinler51e927c2020-02-27 10:35:10 -0600303 {
304 // Path doesn't exist now
305 }
Matt Spinler2a28c932020-02-03 14:23:40 -0600306 }
307
308 /**
309 * @brief Reads the interface's properties on D-Bus, and
310 * calls the the user defined function with the property map.
311 *
312 * @param[in] dataIface - The DataInterface object
313 */
314 void read(const DataIface& dataIface)
315 {
316 auto service = dataIface.getService(_path, _interface);
317 if (!service.empty())
318 {
319 auto properties =
320 dataIface.getAllProperties(service, _path, _interface);
321
322 _setFunc(properties);
323 }
324 }
325
326 /**
327 * @brief The propertiesChanged callback
328 *
329 * Calls the user defined function with the property map. Only the
330 * properties that changed will be in the map.
331 *
332 * @param[in] msg - The sdbusplus message object
333 */
334 void propChanged(sdbusplus::message::message& msg)
335 {
336 DBusInterface interface;
337 DBusPropertyMap properties;
338
339 msg.read(interface, properties);
340
341 _setFunc(properties);
342 }
343
344 /**
345 * @brief The interfacesAdded callback
346 *
347 * Calls the user defined function with the property map
348 *
349 * @param[in] msg - The sdbusplus message object
350 */
351 void interfaceAdded(sdbusplus::message::message& msg)
352 {
353 sdbusplus::message::object_path path;
354 DBusInterfaceMap interfaces;
355
356 msg.read(path, interfaces);
357
358 auto iface = interfaces.find(_interface);
359 if (iface != interfaces.end())
360 {
361 _setFunc(iface->second);
362 }
363 }
364
365 private:
366 /**
367 * @brief The function that will be called any time the
368 * interface is read.
369 */
370 InterfaceSetFunc _setFunc;
371};
372
373} // namespace openpower::pels