blob: 3917e19897fa472eba47bbdeb529bab1372e1b0b [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)
Patrick Williams2544b412022-10-04 08:41:06 -050035 {}
Matt Spinler2a28c932020-02-03 14:23:40 -060036
37 protected:
38 /**
39 * @brief The D-Bus path
40 */
41 std::string _path;
42
43 /**
44 * @brief The D-Bus interface
45 */
46 std::string _interface;
47
48 /**
49 * @brief The match objects for the propertiesChanged and
50 * interfacesAdded signals.
51 */
52 std::vector<sdbusplus::bus::match_t> _matches;
53};
54
55/**
56 * @class PropertyWatcher
57 *
58 * This class allows the user to be kept up to data with a D-Bus
59 * property's value. It does this by calling a user specified function
60 * that is passed the variant that contains the property's value when:
61 *
62 * 1) The property is read when the class is constructed, if
63 * the property is on D-Bus at the time.
64 * 2) The property changes (via a property changed signal).
65 * 3) An interfacesAdded signal is received with that property.
66 *
67 * The DataInterface class is used to access D-Bus, and is a template
68 * to avoid any circular include issues as that class is one of the
69 * users of this one.
70 */
71template <typename DataIface>
72class PropertyWatcher : public DBusWatcher
73{
74 public:
75 PropertyWatcher() = delete;
76 ~PropertyWatcher() = default;
77 PropertyWatcher(const PropertyWatcher&) = delete;
78 PropertyWatcher& operator=(const PropertyWatcher&) = delete;
79 PropertyWatcher(PropertyWatcher&&) = delete;
80 PropertyWatcher& operator=(PropertyWatcher&&) = delete;
81
82 using PropertySetFunc = std::function<void(const DBusValue&)>;
83
84 /**
85 * @brief Constructor
86 *
87 * Reads the property if it is on D-Bus, and sets up the match
88 * objects for the propertiesChanged and interfacesAdded signals.
89 *
90 * @param[in] bus - The sdbusplus bus object
91 * @param[in] path - The D-Bus path of the property
92 * @param[in] interface - The D-Bus interface that contains the property
93 * @param[in] propertyName - The property name
Matt Spinlerc1746f62020-02-21 15:03:52 -060094 * @param[in] service - The D-Bus service to use for the property read.
95 * Can be empty to look it up instead.
Matt Spinler2a28c932020-02-03 14:23:40 -060096 * @param[in] dataIface - The DataInterface object
97 * @param[in] func - The callback used any time the property is read
98 */
Patrick Williams45e83522022-07-22 19:26:52 -050099 PropertyWatcher(sdbusplus::bus_t& bus, const std::string& path,
Matt Spinler2a28c932020-02-03 14:23:40 -0600100 const std::string& interface,
Matt Spinlerc1746f62020-02-21 15:03:52 -0600101 const std::string& propertyName, const std::string& service,
102 const DataIface& dataIface, PropertySetFunc func) :
Patrick Williams075c7922024-08-16 15:19:49 -0400103 DBusWatcher(path, interface), _name(propertyName), _setFunc(func)
Matt Spinler2a28c932020-02-03 14:23:40 -0600104 {
Matt Spinler2a28c932020-02-03 14:23:40 -0600105 _matches.emplace_back(
106 bus, match_rules::propertiesChanged(_path, _interface),
107 std::bind(std::mem_fn(&PropertyWatcher::propChanged), this,
108 std::placeholders::_1));
109
110 _matches.emplace_back(
111 bus,
112 match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
113 std::bind(std::mem_fn(&PropertyWatcher::interfaceAdded), this,
114 std::placeholders::_1));
Matt Spinler51e927c2020-02-27 10:35:10 -0600115
116 try
117 {
118 read(dataIface, service);
119 }
Patrick Williams45e83522022-07-22 19:26:52 -0500120 catch (const sdbusplus::exception_t& e)
Matt Spinler51e927c2020-02-27 10:35:10 -0600121 {
122 // Path doesn't exist now
123 }
Matt Spinler2a28c932020-02-03 14:23:40 -0600124 }
125
126 /**
Matt Spinlerc1746f62020-02-21 15:03:52 -0600127 * @brief Constructor
128 *
129 * Reads the property if it is on D-Bus, and sets up the match
130 * objects for the propertiesChanged and interfacesAdded signals.
131 *
132 * Unlike the other constructor, this contructor doesn't take the
133 * service to use for the property read so it will look it up with
134 * an ObjectMapper GetObject call.
135 *
136 * @param[in] bus - The sdbusplus bus object
137 * @param[in] path - The D-Bus path of the property
138 * @param[in] interface - The D-Bus interface that contains the property
139 * @param[in] propertyName - The property name
140 * @param[in] dataIface - The DataInterface object
141 * @param[in] func - The callback used any time the property is read
142 */
Patrick Williams45e83522022-07-22 19:26:52 -0500143 PropertyWatcher(sdbusplus::bus_t& bus, const std::string& path,
Matt Spinlerc1746f62020-02-21 15:03:52 -0600144 const std::string& interface,
145 const std::string& propertyName, const DataIface& dataIface,
146 PropertySetFunc func) :
147 PropertyWatcher(bus, path, interface, propertyName, "", dataIface, func)
Patrick Williams2544b412022-10-04 08:41:06 -0500148 {}
Matt Spinlerc1746f62020-02-21 15:03:52 -0600149
150 /**
Matt Spinler2a28c932020-02-03 14:23:40 -0600151 * @brief Reads the property on D-Bus, and calls
152 * the user defined function with the value.
153 *
Matt Spinlerc1746f62020-02-21 15:03:52 -0600154 * If the passed in service is empty, look up the service to use.
155 *
Matt Spinler2a28c932020-02-03 14:23:40 -0600156 * @param[in] dataIface - The DataInterface object
Matt Spinlerc1746f62020-02-21 15:03:52 -0600157 * @param[in] service - The D-Bus service to make the getProperty
158 * call with, if not empty
Matt Spinler2a28c932020-02-03 14:23:40 -0600159 */
Matt Spinlerc1746f62020-02-21 15:03:52 -0600160 void read(const DataIface& dataIface, std::string service)
Matt Spinler2a28c932020-02-03 14:23:40 -0600161 {
Matt Spinlerc1746f62020-02-21 15:03:52 -0600162 if (service.empty())
163 {
164 service = dataIface.getService(_path, _interface);
165 }
166
Matt Spinler2a28c932020-02-03 14:23:40 -0600167 if (!service.empty())
168 {
169 DBusValue value;
170 dataIface.getProperty(service, _path, _interface, _name, value);
171
172 _setFunc(value);
173 }
174 }
175
176 /**
177 * @brief The propertiesChanged callback
178 *
179 * Calls the user defined function with the property value
180 *
181 * @param[in] msg - The sdbusplus message object
182 */
Patrick Williams45e83522022-07-22 19:26:52 -0500183 void propChanged(sdbusplus::message_t& msg)
Matt Spinler2a28c932020-02-03 14:23:40 -0600184 {
185 DBusInterface interface;
186 DBusPropertyMap properties;
187
188 msg.read(interface, properties);
189
190 auto prop = properties.find(_name);
191 if (prop != properties.end())
192 {
193 _setFunc(prop->second);
194 }
195 }
196
197 /**
198 * @brief The interfacesAdded callback
199 *
200 * Calls the user defined function with the property value
201 *
202 * @param[in] msg - The sdbusplus message object
203 */
Patrick Williams45e83522022-07-22 19:26:52 -0500204 void interfaceAdded(sdbusplus::message_t& msg)
Matt Spinler2a28c932020-02-03 14:23:40 -0600205 {
206 sdbusplus::message::object_path path;
207 DBusInterfaceMap interfaces;
208
209 msg.read(path, interfaces);
210
211 auto iface = interfaces.find(_interface);
212 if (iface != interfaces.end())
213 {
214 auto prop = iface->second.find(_name);
215 if (prop != iface->second.end())
216 {
217 _setFunc(prop->second);
218 }
219 }
220 }
221
222 private:
223 /**
224 * @brief The D-Bus property name
225 */
226 std::string _name;
227
228 /**
229 * @brief The function that will be called any time the
230 * property is read.
231 */
232 PropertySetFunc _setFunc;
233};
234
235/**
236 * @class InterfaceWatcher
237 *
238 * This class allows the user to be kept up to data with a D-Bus
239 * interface's properties.. It does this by calling a user specified
240 * function that is passed a map of the D-Bus property names and values
241 * on that interface when:
242 *
243 * 1) The interface is read when the class is constructed, if
244 * the interface is on D-Bus at the time.
245 * 2) The interface has a property that changes (via a property changed signal).
246 * 3) An interfacesAdded signal is received.
247 *
248 * The DataInterface class is used to access D-Bus, and is a template
249 * to avoid any circular include issues as that class is one of the
250 * users of this one.
251 */
252template <typename DataIface>
253class InterfaceWatcher : public DBusWatcher
254{
255 public:
256 InterfaceWatcher() = delete;
257 ~InterfaceWatcher() = default;
258 InterfaceWatcher(const InterfaceWatcher&) = delete;
259 InterfaceWatcher& operator=(const InterfaceWatcher&) = delete;
260 InterfaceWatcher(InterfaceWatcher&&) = delete;
261 InterfaceWatcher& operator=(InterfaceWatcher&&) = delete;
262
263 using InterfaceSetFunc = std::function<void(const DBusPropertyMap&)>;
264
265 /**
266 * @brief Constructor
267 *
268 * Reads all properties on the interface if it is on D-Bus,
269 * and sets up the match objects for the propertiesChanged
270 * and interfacesAdded signals.
271 *
272 * @param[in] bus - The sdbusplus bus object
273 * @param[in] path - The D-Bus path of the property
274 * @param[in] interface - The D-Bus interface that contains the property
275 * @param[in] dataIface - The DataInterface object
276 * @param[in] func - The callback used any time the property is read
277 */
Patrick Williams45e83522022-07-22 19:26:52 -0500278 InterfaceWatcher(sdbusplus::bus_t& bus, const std::string& path,
Matt Spinler2a28c932020-02-03 14:23:40 -0600279 const std::string& interface, const DataIface& dataIface,
280 InterfaceSetFunc func) :
Patrick Williams075c7922024-08-16 15:19:49 -0400281 DBusWatcher(path, interface), _setFunc(func)
Matt Spinler2a28c932020-02-03 14:23:40 -0600282 {
Matt Spinler2a28c932020-02-03 14:23:40 -0600283 _matches.emplace_back(
284 bus, match_rules::propertiesChanged(_path, _interface),
285 std::bind(std::mem_fn(&InterfaceWatcher::propChanged), this,
286 std::placeholders::_1));
287
288 _matches.emplace_back(
289 bus,
290 match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
291 std::bind(std::mem_fn(&InterfaceWatcher::interfaceAdded), this,
292 std::placeholders::_1));
Matt Spinler51e927c2020-02-27 10:35:10 -0600293
294 try
295 {
296 read(dataIface);
297 }
Patrick Williams45e83522022-07-22 19:26:52 -0500298 catch (const sdbusplus::exception_t& e)
Matt Spinler51e927c2020-02-27 10:35:10 -0600299 {
300 // Path doesn't exist now
301 }
Matt Spinler2a28c932020-02-03 14:23:40 -0600302 }
303
304 /**
305 * @brief Reads the interface's properties on D-Bus, and
306 * calls the the user defined function with the property map.
307 *
308 * @param[in] dataIface - The DataInterface object
309 */
310 void read(const DataIface& dataIface)
311 {
312 auto service = dataIface.getService(_path, _interface);
313 if (!service.empty())
314 {
Patrick Williams075c7922024-08-16 15:19:49 -0400315 auto properties =
316 dataIface.getAllProperties(service, _path, _interface);
Matt Spinler2a28c932020-02-03 14:23:40 -0600317
318 _setFunc(properties);
319 }
320 }
321
322 /**
323 * @brief The propertiesChanged callback
324 *
325 * Calls the user defined function with the property map. Only the
326 * properties that changed will be in the map.
327 *
328 * @param[in] msg - The sdbusplus message object
329 */
Patrick Williams45e83522022-07-22 19:26:52 -0500330 void propChanged(sdbusplus::message_t& msg)
Matt Spinler2a28c932020-02-03 14:23:40 -0600331 {
332 DBusInterface interface;
333 DBusPropertyMap properties;
334
335 msg.read(interface, properties);
336
337 _setFunc(properties);
338 }
339
340 /**
341 * @brief The interfacesAdded callback
342 *
343 * Calls the user defined function with the property map
344 *
345 * @param[in] msg - The sdbusplus message object
346 */
Patrick Williams45e83522022-07-22 19:26:52 -0500347 void interfaceAdded(sdbusplus::message_t& msg)
Matt Spinler2a28c932020-02-03 14:23:40 -0600348 {
349 sdbusplus::message::object_path path;
350 DBusInterfaceMap interfaces;
351
352 msg.read(path, interfaces);
353
354 auto iface = interfaces.find(_interface);
355 if (iface != interfaces.end())
356 {
357 _setFunc(iface->second);
358 }
359 }
360
361 private:
362 /**
363 * @brief The function that will be called any time the
364 * interface is read.
365 */
366 InterfaceSetFunc _setFunc;
367};
368
369} // namespace openpower::pels