blob: bdd3b2c1087c75cdeb1bf8ecf8476e51dafc2440 [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
96 * @param[in] dataIface - The DataInterface object
97 * @param[in] func - The callback used any time the property is read
98 */
99 PropertyWatcher(sdbusplus::bus::bus& bus, const std::string& path,
100 const std::string& interface,
101 const std::string& propertyName, const DataIface& dataIface,
102 PropertySetFunc func) :
103 DBusWatcher(path, interface),
104 _name(propertyName), _setFunc(func)
105 {
106 try
107 {
108 read(dataIface);
109 }
110 catch (const SdBusError& e)
111 {
112 // Path doesn't exist now
113 }
114
115 _matches.emplace_back(
116 bus, match_rules::propertiesChanged(_path, _interface),
117 std::bind(std::mem_fn(&PropertyWatcher::propChanged), this,
118 std::placeholders::_1));
119
120 _matches.emplace_back(
121 bus,
122 match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
123 std::bind(std::mem_fn(&PropertyWatcher::interfaceAdded), this,
124 std::placeholders::_1));
125 }
126
127 /**
128 * @brief Reads the property on D-Bus, and calls
129 * the user defined function with the value.
130 *
131 * @param[in] dataIface - The DataInterface object
132 */
133 void read(const DataIface& dataIface)
134 {
135 auto service = dataIface.getService(_path, _interface);
136 if (!service.empty())
137 {
138 DBusValue value;
139 dataIface.getProperty(service, _path, _interface, _name, value);
140
141 _setFunc(value);
142 }
143 }
144
145 /**
146 * @brief The propertiesChanged callback
147 *
148 * Calls the user defined function with the property value
149 *
150 * @param[in] msg - The sdbusplus message object
151 */
152 void propChanged(sdbusplus::message::message& msg)
153 {
154 DBusInterface interface;
155 DBusPropertyMap properties;
156
157 msg.read(interface, properties);
158
159 auto prop = properties.find(_name);
160 if (prop != properties.end())
161 {
162 _setFunc(prop->second);
163 }
164 }
165
166 /**
167 * @brief The interfacesAdded callback
168 *
169 * Calls the user defined function with the property value
170 *
171 * @param[in] msg - The sdbusplus message object
172 */
173 void interfaceAdded(sdbusplus::message::message& msg)
174 {
175 sdbusplus::message::object_path path;
176 DBusInterfaceMap interfaces;
177
178 msg.read(path, interfaces);
179
180 auto iface = interfaces.find(_interface);
181 if (iface != interfaces.end())
182 {
183 auto prop = iface->second.find(_name);
184 if (prop != iface->second.end())
185 {
186 _setFunc(prop->second);
187 }
188 }
189 }
190
191 private:
192 /**
193 * @brief The D-Bus property name
194 */
195 std::string _name;
196
197 /**
198 * @brief The function that will be called any time the
199 * property is read.
200 */
201 PropertySetFunc _setFunc;
202};
203
204/**
205 * @class InterfaceWatcher
206 *
207 * This class allows the user to be kept up to data with a D-Bus
208 * interface's properties.. It does this by calling a user specified
209 * function that is passed a map of the D-Bus property names and values
210 * on that interface when:
211 *
212 * 1) The interface is read when the class is constructed, if
213 * the interface is on D-Bus at the time.
214 * 2) The interface has a property that changes (via a property changed signal).
215 * 3) An interfacesAdded signal is received.
216 *
217 * The DataInterface class is used to access D-Bus, and is a template
218 * to avoid any circular include issues as that class is one of the
219 * users of this one.
220 */
221template <typename DataIface>
222class InterfaceWatcher : public DBusWatcher
223{
224 public:
225 InterfaceWatcher() = delete;
226 ~InterfaceWatcher() = default;
227 InterfaceWatcher(const InterfaceWatcher&) = delete;
228 InterfaceWatcher& operator=(const InterfaceWatcher&) = delete;
229 InterfaceWatcher(InterfaceWatcher&&) = delete;
230 InterfaceWatcher& operator=(InterfaceWatcher&&) = delete;
231
232 using InterfaceSetFunc = std::function<void(const DBusPropertyMap&)>;
233
234 /**
235 * @brief Constructor
236 *
237 * Reads all properties on the interface if it is on D-Bus,
238 * and sets up the match objects for the propertiesChanged
239 * and interfacesAdded signals.
240 *
241 * @param[in] bus - The sdbusplus bus object
242 * @param[in] path - The D-Bus path of the property
243 * @param[in] interface - The D-Bus interface that contains the property
244 * @param[in] dataIface - The DataInterface object
245 * @param[in] func - The callback used any time the property is read
246 */
247 InterfaceWatcher(sdbusplus::bus::bus& bus, const std::string& path,
248 const std::string& interface, const DataIface& dataIface,
249 InterfaceSetFunc func) :
250 DBusWatcher(path, interface),
251 _setFunc(func)
252 {
253 try
254 {
255 read(dataIface);
256 }
257 catch (const SdBusError& e)
258 {
259 // Path doesn't exist now
260 }
261
262 _matches.emplace_back(
263 bus, match_rules::propertiesChanged(_path, _interface),
264 std::bind(std::mem_fn(&InterfaceWatcher::propChanged), this,
265 std::placeholders::_1));
266
267 _matches.emplace_back(
268 bus,
269 match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
270 std::bind(std::mem_fn(&InterfaceWatcher::interfaceAdded), this,
271 std::placeholders::_1));
272 }
273
274 /**
275 * @brief Reads the interface's properties on D-Bus, and
276 * calls the the user defined function with the property map.
277 *
278 * @param[in] dataIface - The DataInterface object
279 */
280 void read(const DataIface& dataIface)
281 {
282 auto service = dataIface.getService(_path, _interface);
283 if (!service.empty())
284 {
285 auto properties =
286 dataIface.getAllProperties(service, _path, _interface);
287
288 _setFunc(properties);
289 }
290 }
291
292 /**
293 * @brief The propertiesChanged callback
294 *
295 * Calls the user defined function with the property map. Only the
296 * properties that changed will be in the map.
297 *
298 * @param[in] msg - The sdbusplus message object
299 */
300 void propChanged(sdbusplus::message::message& msg)
301 {
302 DBusInterface interface;
303 DBusPropertyMap properties;
304
305 msg.read(interface, properties);
306
307 _setFunc(properties);
308 }
309
310 /**
311 * @brief The interfacesAdded callback
312 *
313 * Calls the user defined function with the property map
314 *
315 * @param[in] msg - The sdbusplus message object
316 */
317 void interfaceAdded(sdbusplus::message::message& msg)
318 {
319 sdbusplus::message::object_path path;
320 DBusInterfaceMap interfaces;
321
322 msg.read(path, interfaces);
323
324 auto iface = interfaces.find(_interface);
325 if (iface != interfaces.end())
326 {
327 _setFunc(iface->second);
328 }
329 }
330
331 private:
332 /**
333 * @brief The function that will be called any time the
334 * interface is read.
335 */
336 InterfaceSetFunc _setFunc;
337};
338
339} // namespace openpower::pels