1 #pragma once 2 3 #include "dbus_types.hpp" 4 5 #include <sdbusplus/bus/match.hpp> 6 7 namespace openpower::pels 8 { 9 10 namespace match_rules = sdbusplus::bus::match::rules; 11 12 /** 13 * @class DBusWatcher 14 * 15 * The base class for the PropertyWatcher and InterfaceWatcher classes. 16 */ 17 class 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 */ 72 template <typename DataIface> 73 class 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 95 * @param[in] service - The D-Bus service to use for the property read. 96 * Can be empty to look it up instead. 97 * @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, 102 const std::string& propertyName, const std::string& service, 103 const DataIface& dataIface, PropertySetFunc func) : 104 DBusWatcher(path, interface), 105 _name(propertyName), _setFunc(func) 106 { 107 _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)); 117 118 try 119 { 120 read(dataIface, service); 121 } 122 catch (const sdbusplus::exception::exception& e) 123 { 124 // Path doesn't exist now 125 } 126 } 127 128 /** 129 * @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 /** 154 * @brief Reads the property on D-Bus, and calls 155 * the user defined function with the value. 156 * 157 * If the passed in service is empty, look up the service to use. 158 * 159 * @param[in] dataIface - The DataInterface object 160 * @param[in] service - The D-Bus service to make the getProperty 161 * call with, if not empty 162 */ 163 void read(const DataIface& dataIface, std::string service) 164 { 165 if (service.empty()) 166 { 167 service = dataIface.getService(_path, _interface); 168 } 169 170 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 */ 255 template <typename DataIface> 256 class 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 { 287 _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)); 297 298 try 299 { 300 read(dataIface); 301 } 302 catch (const sdbusplus::exception::exception& e) 303 { 304 // Path doesn't exist now 305 } 306 } 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 374