1 #pragma once 2 3 #include "callback.hpp" 4 #include "data_types.hpp" 5 #include "propertywatch.hpp" 6 7 #include <sdbusplus/bus/match.hpp> 8 #include <sdbusplus/message.hpp> 9 #include <vector> 10 11 namespace phosphor 12 { 13 namespace dbus 14 { 15 namespace monitoring 16 { 17 18 using MappedPropertyIndex = 19 RefKeyMap<const std::string, 20 RefKeyMap<const std::string, RefVector<const std::string>>>; 21 22 MappedPropertyIndex convert(const PropertyIndex& index); 23 24 template <typename DBusInterfaceType> 25 void PropertyWatch<DBusInterfaceType>::start() 26 { 27 if (alreadyRan) 28 { 29 return; 30 } 31 32 // The index has a flat layout which is not optimal here. Nest 33 // properties in a map of interface names in a map of object paths. 34 auto mapped = convert(index); 35 36 for (const auto& m : mapped) 37 { 38 const auto& path = m.first.get(); 39 const auto& interfaces = m.second; 40 41 // Watch for new interfaces on this path. 42 DBusInterfaceType::addMatch( 43 sdbusplus::bus::match::rules::interfacesAdded(path), 44 [this](auto& msg) 45 // *INDENT-OFF* 46 { this->interfacesAdded(msg); }); 47 // *INDENT-ON* 48 49 // Do a query to populate the cache. Start with a mapper query. 50 // The specific services are queried below. 51 auto getObjectFromMapper = [](const auto& path) { 52 const std::vector<std::string> queryInterfaces; // all interfaces 53 try 54 { 55 return DBusInterfaceType::template callMethodAndRead<GetObject>( 56 MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, "GetObject", 57 path, queryInterfaces); 58 } 59 catch (const sdbusplus::exception::SdBusError&) 60 { 61 // Paths in the configuration may not exist yet. Prime those 62 // later, when/if InterfacesAdded occurs. 63 return GetObject(); 64 } 65 }; 66 auto mapperResp = getObjectFromMapper(path); 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(path, 75 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() == 94 std::find(mapperInterfaces.begin(), mapperInterfaces.end(), 95 interface)) 96 { 97 // This interface isn't being watched. 98 continue; 99 } 100 101 // Delegate type specific property updates to subclasses. 102 try 103 { 104 updateProperties(busName, path, interface); 105 } 106 catch (const sdbusplus::exception::SdBusError&) 107 { 108 // If for some reason the path has gone away since 109 // the mapper lookup we'll simply try again if/when 110 // InterfacesAdded occurs the next time it shows up. 111 } 112 } 113 } 114 } 115 116 alreadyRan = true; 117 } 118 119 template <typename DBusInterfaceType> 120 void PropertyWatch<DBusInterfaceType>::callback(Context ctx) 121 { 122 // Invoke callback if present. 123 if (this->alreadyRan && this->cb) 124 { 125 (*this->cb)(ctx); 126 } 127 } 128 129 template <typename T, typename DBusInterfaceType> 130 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties( 131 const std::string& busName, const std::string& path, 132 const std::string& interface) 133 { 134 auto properties = 135 DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>( 136 busName.c_str(), path.c_str(), "org.freedesktop.DBus.Properties", 137 "GetAll", interface); 138 propertiesChanged(path, interface, properties); 139 } 140 141 template <typename T, typename DBusInterfaceType> 142 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 143 const std::string& path, const std::string& interface, 144 const PropertiesChanged<T>& properties) 145 { 146 // Update the cache for any watched properties. 147 for (const auto& p : properties) 148 { 149 auto key = std::make_tuple(path, interface, p.first); 150 auto item = this->index.find(key); 151 if (item == this->index.end()) 152 { 153 // This property isn't being watched. 154 continue; 155 } 156 157 // Run property value thru filter operations 158 auto isFiltered = false; 159 const auto& storage = std::get<storageIndex>(item->second); 160 auto value = std::get<T>(p.second); 161 if (filterOps) 162 { 163 any_ns::any anyValue = value; 164 if ((*filterOps)(anyValue)) 165 { 166 // Property value filtered, clear it from storage so 167 // callback functions do not use it 168 isFiltered = true; 169 std::get<valueIndex>(storage.get()).clear(); 170 } 171 } 172 if (!isFiltered) 173 { 174 // Property value not filtered out, update 175 std::get<valueIndex>(storage.get()) = value; 176 // Invoke callback if present. 177 this->callback(Context::SIGNAL); 178 } 179 } 180 } 181 182 template <typename T, typename DBusInterfaceType> 183 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 184 sdbusplus::message::message& msg, const std::string& path, 185 const std::string& interface) 186 { 187 PropertiesChanged<T> properties; 188 msg.read(properties); 189 propertiesChanged(path, interface, properties); 190 } 191 192 template <typename T, typename DBusInterfaceType> 193 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 194 const std::string& path, const InterfacesAdded<T>& interfaces) 195 { 196 for (const auto& i : interfaces) 197 { 198 propertiesChanged(path, i.first, i.second); 199 } 200 } 201 202 template <typename T, typename DBusInterfaceType> 203 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 204 sdbusplus::message::message& msg) 205 { 206 sdbusplus::message::object_path path; 207 InterfacesAdded<T> interfaces; 208 msg.read(path, interfaces); 209 interfacesAdded(path, interfaces); 210 } 211 212 } // namespace monitoring 213 } // namespace dbus 214 } // namespace phosphor 215