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 std::get<valueIndex>(std::get<storageIndex>(item->second).get()) = 158 p.second.template get<T>(); 159 160 // Invoke callback if present. 161 this->callback(Context::SIGNAL); 162 } 163 } 164 165 template <typename T, typename DBusInterfaceType> 166 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( 167 sdbusplus::message::message& msg, const std::string& path, 168 const std::string& interface) 169 { 170 PropertiesChanged<T> properties; 171 msg.read(properties); 172 propertiesChanged(path, interface, properties); 173 } 174 175 template <typename T, typename DBusInterfaceType> 176 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 177 const std::string& path, const InterfacesAdded<T>& interfaces) 178 { 179 for (const auto& i : interfaces) 180 { 181 propertiesChanged(path, i.first, i.second); 182 } 183 } 184 185 template <typename T, typename DBusInterfaceType> 186 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( 187 sdbusplus::message::message& msg) 188 { 189 sdbusplus::message::object_path path; 190 InterfacesAdded<T> interfaces; 191 msg.read(path, interfaces); 192 interfacesAdded(path, interfaces); 193 } 194 195 } // namespace monitoring 196 } // namespace dbus 197 } // namespace phosphor 198