| #pragma once |
| |
| #include "callback.hpp" |
| #include "data_types.hpp" |
| #include "propertywatch.hpp" |
| |
| #include <sdbusplus/bus/match.hpp> |
| #include <sdbusplus/message.hpp> |
| #include <string> |
| #include <vector> |
| |
| 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. |
| auto getObjectFromMapper = [](const auto& path) { |
| const std::vector<std::string> queryInterfaces; // all interfaces |
| try |
| { |
| return DBusInterfaceType::template callMethodAndRead<GetObject>( |
| MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, "GetObject", |
| path, queryInterfaces); |
| } |
| catch (const sdbusplus::exception::exception&) |
| { |
| // Paths in the configuration may not exist yet. Prime those |
| // later, when/if InterfacesAdded occurs. |
| return GetObject(); |
| } |
| }; |
| auto mapperResp = getObjectFromMapper(path); |
| |
| 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. |
| try |
| { |
| updateProperties(busName, path, interface); |
| } |
| catch (const sdbusplus::exception::exception&) |
| { |
| // If for some reason the path has gone away since |
| // the mapper lookup we'll simply try again if/when |
| // InterfacesAdded occurs the next time it shows up. |
| } |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| // Run property value thru filter operations |
| auto isFiltered = false; |
| const auto& storage = std::get<storageIndex>(item->second); |
| auto value = std::get<T>(p.second); |
| if (filterOps) |
| { |
| any_ns::any anyValue = value; |
| if ((*filterOps)(anyValue)) |
| { |
| // Property value filtered, clear it from storage so |
| // callback functions do not use it |
| isFiltered = true; |
| std::get<valueIndex>(storage.get()).clear(); |
| } |
| } |
| if (!isFiltered) |
| { |
| // Property value not filtered out, update |
| std::get<valueIndex>(storage.get()) = value; |
| // 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 |