1 #pragma once
2 
3 #include <sdbusplus/message.hpp>
4 #include <sdbusplus/bus/match.hpp>
5 #include <vector>
6 #include "callback.hpp"
7 #include "data_types.hpp"
8 #include "propertywatch.hpp"
9 
10 namespace phosphor
11 {
12 namespace dbus
13 {
14 namespace monitoring
15 {
16 
17 using MappedPropertyIndex =
18     RefKeyMap<const std::string,
19     RefKeyMap<const std::string,
20     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             {
47                 this->interfacesAdded(msg);
48             });
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         const std::vector<std::string> queryInterfaces; // all interfaces
54         auto mapperResp =
55             DBusInterfaceType::template callMethodAndRead<GetObject>(
56                 MAPPER_BUSNAME,
57                 MAPPER_PATH,
58                 MAPPER_INTERFACE,
59                 "GetObject",
60                 path,
61                 queryInterfaces);
62 
63         for (const auto& i : interfaces)
64         {
65             const auto& interface = i.first.get();
66 
67             // Watch for property changes on this interface.
68             DBusInterfaceType::addMatch(
69                 sdbusplus::bus::match::rules::propertiesChanged(
70                         path, interface),
71                 [this](auto & msg)
72                 // *INDENT-OFF*
73                 {
74                     std::string interface;
75                     msg.read(interface);
76                     auto path = msg.get_path();
77                     this->propertiesChanged(msg, path, interface);
78                 });
79                 // *INDENT-ON*
80 
81             // The mapper response is a busname:[interfaces] map.  Look for
82             // each interface in the index and if found, query the service and
83             // populate the cache entries for the interface.
84             for (const auto& mr : mapperResp)
85             {
86                 const auto& busName = mr.first;
87                 const auto& mapperInterfaces = mr.second;
88                 if (mapperInterfaces.end() == std::find(
89                         mapperInterfaces.begin(),
90                         mapperInterfaces.end(),
91                         interface))
92                 {
93                     // This interface isn't being watched.
94                     continue;
95                 }
96 
97                 // Delegate type specific property updates to subclasses.
98                 updateProperties(busName, path, interface);
99             }
100         }
101     }
102 
103     alreadyRan = true;
104 }
105 
106 template <typename DBusInterfaceType>
107 void PropertyWatch<DBusInterfaceType>::callback(Context ctx)
108 {
109     // Invoke callback if present.
110     if (this->alreadyRan && this->cb)
111     {
112         (*this->cb)(ctx);
113     }
114 }
115 
116 template <typename T, typename DBusInterfaceType>
117 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties(
118     const std::string& busName,
119     const std::string& path,
120     const std::string& interface)
121 {
122     auto properties =
123         DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>(
124             busName.c_str(),
125             path.c_str(),
126             "org.freedesktop.DBus.Properties",
127             "GetAll",
128             interface);
129     propertiesChanged(path, interface, properties);
130 }
131 
132 template <typename T, typename DBusInterfaceType>
133 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
134     const std::string& path,
135     const std::string& interface,
136     const PropertiesChanged<T>& properties)
137 {
138     // Update the cache for any watched properties.
139     for (const auto& p : properties)
140     {
141         auto key = std::make_tuple(path, interface, p.first);
142         auto item = this->index.find(key);
143         if (item == this->index.end())
144         {
145             // This property isn't being watched.
146             continue;
147         }
148 
149         std::get<valueIndex>(std::get<storageIndex>(item->second).get()) =
150                 p.second.template get<T>();
151 
152         // Invoke callback if present.
153         this->callback(Context::SIGNAL);
154     }
155 }
156 
157 template <typename T, typename DBusInterfaceType>
158 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
159     sdbusplus::message::message& msg,
160     const std::string& path,
161     const std::string& interface)
162 {
163     PropertiesChanged<T> properties;
164     msg.read(properties);
165     propertiesChanged(path, interface, properties);
166 }
167 
168 template <typename T, typename DBusInterfaceType>
169 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
170     const std::string& path,
171     const InterfacesAdded<T>& interfaces)
172 {
173     for (const auto& i : interfaces)
174     {
175         propertiesChanged(path, i.first, i.second);
176     }
177 }
178 
179 template <typename T, typename DBusInterfaceType>
180 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
181     sdbusplus::message::message& msg)
182 {
183     sdbusplus::message::object_path path;
184     InterfacesAdded<T> interfaces;
185     msg.read(path, interfaces);
186     interfacesAdded(path, interfaces);
187 }
188 
189 } // namespace monitoring
190 } // namespace dbus
191 } // namespace phosphor
192