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 T, typename DBusInterfaceType> 112 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties( 113 const std::string& busName, 114 const std::string& path, 115 const std::string& interface) 116 { 117 auto properties = 118 DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>( 119 busName.c_str(), 120 path.c_str(), 121 "org.freedesktop.DBus.Properties", 122 "GetAll", 123 interface); 124 propertiesChanged(path, interface, properties); 125 } 126 127 template <typename T, typename DBusInterfaceType> 128 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 129 const std::string& path, 130 const std::string& interface, 131 const PropertiesChanged<T>& properties) 132 { 133 // Update the cache for any watched properties. 134 for (const auto& p : properties) 135 { 136 auto key = std::make_tuple(path, interface, p.first); 137 auto item = this->index.find(key); 138 if (item == this->index.end()) 139 { 140 // This property isn't being watched. 141 continue; 142 } 143 144 std::get<2>(item->second).get() = p.second.template get<T>(); 145 146 // Invoke callback if present. 147 if (this->alreadyRan && this->callback) 148 { 149 (*this->callback)(); 150 } 151 } 152 } 153 154 template <typename T, typename DBusInterfaceType> 155 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 156 sdbusplus::message::message& msg, 157 const std::string& path, 158 const std::string& interface) 159 { 160 PropertiesChanged<T> properties; 161 msg.read(properties); 162 propertiesChanged(path, interface, properties); 163 } 164 165 template <typename T, typename DBusInterfaceType> 166 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 167 const std::string& path, 168 const InterfacesAdded<T>& interfaces) 169 { 170 for (const auto& i : interfaces) 171 { 172 propertiesChanged(path, i.first, i.second); 173 } 174 } 175 176 template <typename T, typename DBusInterfaceType> 177 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 178 sdbusplus::message::message& msg) 179 { 180 sdbusplus::message::object_path path; 181 InterfacesAdded<T> interfaces; 182 msg.read(path, interfaces); 183 interfacesAdded(path, interfaces); 184 } 185 186 } // namespace monitoring 187 } // namespace dbus 188 } // namespace phosphor 189