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