1 #pragma once 2 3 #include <sdbusplus/message.hpp> 4 #include <sdbusplus/bus/match.hpp> 5 #include <vector> 6 #include "callback.hpp" 7 #include "data_types.hpp" 8 #include "propertywatch.hpp" 9 10 namespace phosphor 11 { 12 namespace dbus 13 { 14 namespace monitoring 15 { 16 17 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 18 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 19 static constexpr auto MAPPER_INTERFACE = 20 "xyz.openbmc_project.ObjectMapper"; 21 22 using MappedPropertyIndex = 23 RefKeyMap<const std::string, 24 RefKeyMap<const std::string, 25 RefVector<const std::string>>>; 26 27 MappedPropertyIndex convert(const PropertyIndex& index); 28 29 template <typename DBusInterfaceType> 30 void PropertyWatch<DBusInterfaceType>::start() 31 { 32 if (alreadyRan) 33 { 34 return; 35 } 36 37 // The index has a flat layout which is not optimal here. Nest 38 // properties in a map of interface names in a map of object paths. 39 auto mapped = convert(index); 40 41 for (const auto& m : mapped) 42 { 43 const auto& path = m.first.get(); 44 const auto& interfaces = m.second; 45 46 // Watch for new interfaces on this path. 47 DBusInterfaceType::addMatch( 48 sdbusplus::bus::match::rules::interfacesAdded(path), 49 [this](auto & msg) 50 // *INDENT-OFF* 51 { 52 this->interfacesAdded(msg); 53 }); 54 // *INDENT-ON* 55 56 // Do a query to populate the cache. Start with a mapper query. 57 // The specific services are queried below. 58 const std::vector<std::string> queryInterfaces; // all interfaces 59 auto mapperResp = 60 DBusInterfaceType::template callMethodAndRead<GetObject>( 61 MAPPER_BUSNAME, 62 MAPPER_PATH, 63 MAPPER_INTERFACE, 64 "GetObject", 65 path, 66 queryInterfaces); 67 68 for (const auto& i : interfaces) 69 { 70 const auto& interface = i.first.get(); 71 72 // Watch for property changes on this interface. 73 DBusInterfaceType::addMatch( 74 sdbusplus::bus::match::rules::propertiesChanged( 75 path, interface), 76 [this](auto & msg) 77 // *INDENT-OFF* 78 { 79 std::string interface; 80 msg.read(interface); 81 auto path = msg.get_path(); 82 this->propertiesChanged(msg, path, interface); 83 }); 84 // *INDENT-ON* 85 86 // The mapper response is a busname:[interfaces] map. Look for 87 // each interface in the index and if found, query the service and 88 // populate the cache entries for the interface. 89 for (const auto& mr : mapperResp) 90 { 91 const auto& busName = mr.first; 92 const auto& mapperInterfaces = mr.second; 93 if (mapperInterfaces.end() == std::find( 94 mapperInterfaces.begin(), 95 mapperInterfaces.end(), 96 interface)) 97 { 98 // This interface isn't being watched. 99 continue; 100 } 101 102 // Delegate type specific property updates to subclasses. 103 updateProperties(busName, path, interface); 104 } 105 } 106 } 107 108 alreadyRan = true; 109 } 110 111 template <typename DBusInterfaceType> 112 void PropertyWatch<DBusInterfaceType>::callback() 113 { 114 // Invoke callback if present. 115 if (this->alreadyRan && this->cb) 116 { 117 (*this->cb)(); 118 } 119 } 120 121 template <typename T, typename DBusInterfaceType> 122 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties( 123 const std::string& busName, 124 const std::string& path, 125 const std::string& interface) 126 { 127 auto properties = 128 DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>( 129 busName.c_str(), 130 path.c_str(), 131 "org.freedesktop.DBus.Properties", 132 "GetAll", 133 interface); 134 propertiesChanged(path, interface, properties); 135 } 136 137 template <typename T, typename DBusInterfaceType> 138 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 139 const std::string& path, 140 const std::string& interface, 141 const PropertiesChanged<T>& properties) 142 { 143 // Update the cache for any watched properties. 144 for (const auto& p : properties) 145 { 146 auto key = std::make_tuple(path, interface, p.first); 147 auto item = this->index.find(key); 148 if (item == this->index.end()) 149 { 150 // This property isn't being watched. 151 continue; 152 } 153 154 std::get<2>(item->second).get() = p.second.template get<T>(); 155 156 // Invoke callback if present. 157 this->callback(); 158 } 159 } 160 161 template <typename T, typename DBusInterfaceType> 162 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 163 sdbusplus::message::message& msg, 164 const std::string& path, 165 const std::string& interface) 166 { 167 PropertiesChanged<T> properties; 168 msg.read(properties); 169 propertiesChanged(path, interface, properties); 170 } 171 172 template <typename T, typename DBusInterfaceType> 173 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 174 const std::string& path, 175 const InterfacesAdded<T>& interfaces) 176 { 177 for (const auto& i : interfaces) 178 { 179 propertiesChanged(path, i.first, i.second); 180 } 181 } 182 183 template <typename T, typename DBusInterfaceType> 184 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 185 sdbusplus::message::message& msg) 186 { 187 sdbusplus::message::object_path path; 188 InterfacesAdded<T> interfaces; 189 msg.read(path, interfaces); 190 interfacesAdded(path, interfaces); 191 } 192 193 } // namespace monitoring 194 } // namespace dbus 195 } // namespace phosphor 196