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