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