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