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