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