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