| #pragma once |
| |
| #include <sdbusplus/message.hpp> |
| #include <sdbusplus/bus/match.hpp> |
| #include <vector> |
| #include "callback.hpp" |
| #include "data_types.hpp" |
| #include "propertywatch.hpp" |
| |
| namespace phosphor |
| { |
| namespace dbus |
| { |
| namespace monitoring |
| { |
| |
| using MappedPropertyIndex = |
| RefKeyMap<const std::string, |
| RefKeyMap<const std::string, |
| RefVector<const std::string>>>; |
| |
| MappedPropertyIndex convert(const PropertyIndex& index); |
| |
| template <typename DBusInterfaceType> |
| void PropertyWatch<DBusInterfaceType>::start() |
| { |
| if (alreadyRan) |
| { |
| return; |
| } |
| |
| // The index has a flat layout which is not optimal here. Nest |
| // properties in a map of interface names in a map of object paths. |
| auto mapped = convert(index); |
| |
| for (const auto& m : mapped) |
| { |
| const auto& path = m.first.get(); |
| const auto& interfaces = m.second; |
| |
| // Watch for new interfaces on this path. |
| DBusInterfaceType::addMatch( |
| sdbusplus::bus::match::rules::interfacesAdded(path), |
| [this](auto & msg) |
| // *INDENT-OFF* |
| { |
| this->interfacesAdded(msg); |
| }); |
| // *INDENT-ON* |
| |
| // Do a query to populate the cache. Start with a mapper query. |
| // The specific services are queried below. |
| const std::vector<std::string> queryInterfaces; // all interfaces |
| auto mapperResp = |
| DBusInterfaceType::template callMethodAndRead<GetObject>( |
| MAPPER_BUSNAME, |
| MAPPER_PATH, |
| MAPPER_INTERFACE, |
| "GetObject", |
| path, |
| queryInterfaces); |
| |
| for (const auto& i : interfaces) |
| { |
| const auto& interface = i.first.get(); |
| |
| // Watch for property changes on this interface. |
| DBusInterfaceType::addMatch( |
| sdbusplus::bus::match::rules::propertiesChanged( |
| path, interface), |
| [this](auto & msg) |
| // *INDENT-OFF* |
| { |
| std::string interface; |
| msg.read(interface); |
| auto path = msg.get_path(); |
| this->propertiesChanged(msg, path, interface); |
| }); |
| // *INDENT-ON* |
| |
| // The mapper response is a busname:[interfaces] map. Look for |
| // each interface in the index and if found, query the service and |
| // populate the cache entries for the interface. |
| for (const auto& mr : mapperResp) |
| { |
| const auto& busName = mr.first; |
| const auto& mapperInterfaces = mr.second; |
| if (mapperInterfaces.end() == std::find( |
| mapperInterfaces.begin(), |
| mapperInterfaces.end(), |
| interface)) |
| { |
| // This interface isn't being watched. |
| continue; |
| } |
| |
| // Delegate type specific property updates to subclasses. |
| updateProperties(busName, path, interface); |
| } |
| } |
| } |
| |
| alreadyRan = true; |
| } |
| |
| template <typename DBusInterfaceType> |
| void PropertyWatch<DBusInterfaceType>::callback(Context ctx) |
| { |
| // Invoke callback if present. |
| if (this->alreadyRan && this->cb) |
| { |
| (*this->cb)(ctx); |
| } |
| } |
| |
| template <typename T, typename DBusInterfaceType> |
| void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties( |
| const std::string& busName, |
| const std::string& path, |
| const std::string& interface) |
| { |
| auto properties = |
| DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>( |
| busName.c_str(), |
| path.c_str(), |
| "org.freedesktop.DBus.Properties", |
| "GetAll", |
| interface); |
| propertiesChanged(path, interface, properties); |
| } |
| |
| template <typename T, typename DBusInterfaceType> |
| void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( |
| const std::string& path, |
| const std::string& interface, |
| const PropertiesChanged<T>& properties) |
| { |
| // Update the cache for any watched properties. |
| for (const auto& p : properties) |
| { |
| auto key = std::make_tuple(path, interface, p.first); |
| auto item = this->index.find(key); |
| if (item == this->index.end()) |
| { |
| // This property isn't being watched. |
| continue; |
| } |
| |
| std::get<valueIndex>(std::get<storageIndex>(item->second).get()) = |
| p.second.template get<T>(); |
| |
| // Invoke callback if present. |
| this->callback(Context::SIGNAL); |
| } |
| } |
| |
| template <typename T, typename DBusInterfaceType> |
| void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged( |
| sdbusplus::message::message& msg, |
| const std::string& path, |
| const std::string& interface) |
| { |
| PropertiesChanged<T> properties; |
| msg.read(properties); |
| propertiesChanged(path, interface, properties); |
| } |
| |
| template <typename T, typename DBusInterfaceType> |
| void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( |
| const std::string& path, |
| const InterfacesAdded<T>& interfaces) |
| { |
| for (const auto& i : interfaces) |
| { |
| propertiesChanged(path, i.first, i.second); |
| } |
| } |
| |
| template <typename T, typename DBusInterfaceType> |
| void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded( |
| sdbusplus::message::message& msg) |
| { |
| sdbusplus::message::object_path path; |
| InterfacesAdded<T> interfaces; |
| msg.read(path, interfaces); |
| interfacesAdded(path, interfaces); |
| } |
| |
| } // namespace monitoring |
| } // namespace dbus |
| } // namespace phosphor |