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 
10 #include <string>
11 #include <vector>
12 
13 namespace phosphor
14 {
15 namespace dbus
16 {
17 namespace monitoring
18 {
19 
20 using MappedPropertyIndex =
21     RefKeyMap<const std::string,
22               RefKeyMap<const std::string, RefVector<const std::string>>>;
23 
24 MappedPropertyIndex convert(const PropertyIndex& index);
25 
26 template <typename DBusInterfaceType>
start()27 void PropertyWatch<DBusInterfaceType>::start()
28 {
29     if (alreadyRan)
30     {
31         return;
32     }
33 
34     // The index has a flat layout which is not optimal here.  Nest
35     // properties in a map of interface names in a map of object paths.
36     auto mapped = convert(index);
37 
38     for (const auto& m : mapped)
39     {
40         const auto& path = m.first.get();
41         const auto& interfaces = m.second;
42 
43         // Watch for new interfaces on this path.
44         DBusInterfaceType::addMatch(
45             sdbusplus::bus::match::rules::interfacesAdded(path),
46             [this](auto& msg)
47             // *INDENT-OFF*
48             { this->interfacesAdded(msg); });
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         auto getObjectFromMapper = [](const auto& path) {
54             const std::vector<std::string> queryInterfaces; // all interfaces
55             try
56             {
57                 return DBusInterfaceType::template callMethodAndRead<GetObject>(
58                     MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, "GetObject",
59                     path, queryInterfaces);
60             }
61             catch (const sdbusplus::exception_t&)
62             {
63                 // Paths in the configuration may not exist yet.  Prime those
64                 // later, when/if InterfacesAdded occurs.
65                 return GetObject();
66             }
67         };
68         auto mapperResp = getObjectFromMapper(path);
69 
70         for (const auto& i : interfaces)
71         {
72             const auto& interface = i.first.get();
73 
74             // Watch for property changes on this interface.
75             DBusInterfaceType::addMatch(
76                 sdbusplus::bus::match::rules::propertiesChanged(path,
77                                                                 interface),
78                 [this](auto& msg)
79                 // *INDENT-OFF*
80                 {
81                     std::string interface;
82                     msg.read(interface);
83                     auto path = msg.get_path();
84                     this->propertiesChanged(msg, path, interface);
85                 });
86             // *INDENT-ON*
87 
88             // The mapper response is a busname:[interfaces] map.  Look for
89             // each interface in the index and if found, query the service and
90             // populate the cache entries for the interface.
91             for (const auto& mr : mapperResp)
92             {
93                 const auto& busName = mr.first;
94                 const auto& mapperInterfaces = mr.second;
95                 if (mapperInterfaces.end() ==
96                     std::find(mapperInterfaces.begin(), mapperInterfaces.end(),
97                               interface))
98                 {
99                     // This interface isn't being watched.
100                     continue;
101                 }
102 
103                 // Delegate type specific property updates to subclasses.
104                 try
105                 {
106                     updateProperties(busName, path, interface);
107                 }
108                 catch (const sdbusplus::exception_t&)
109                 {
110                     // If for some reason the path has gone away since
111                     // the mapper lookup we'll simply try again if/when
112                     // InterfacesAdded occurs the next time it shows up.
113                 }
114             }
115         }
116     }
117 
118     alreadyRan = true;
119 }
120 
121 template <typename DBusInterfaceType>
callback(Context ctx)122 void PropertyWatch<DBusInterfaceType>::callback(Context ctx)
123 {
124     // Ignore callback if ignoreStartCallback is true and it's the START
125     // callback
126     if (ctx == Context::START && ignoreStartCallback)
127     {
128         return;
129     }
130     // Invoke callback if present.
131     if (this->alreadyRan && this->cb)
132     {
133         (*this->cb)(ctx);
134     }
135 }
136 
137 template <typename T, typename DBusInterfaceType>
updateProperties(const std::string & busName,const std::string & path,const std::string & interface)138 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties(
139     const std::string& busName, const std::string& path,
140     const std::string& interface)
141 {
142     auto properties =
143         DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>(
144             busName.c_str(), path.c_str(), "org.freedesktop.DBus.Properties",
145             "GetAll", interface);
146     propertiesChanged(path, interface, properties);
147 }
148 
149 template <typename T, typename DBusInterfaceType>
propertiesChanged(const std::string & path,const std::string & interface,const PropertiesChanged<T> & properties)150 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
151     const std::string& path, const std::string& interface,
152     const PropertiesChanged<T>& properties)
153 {
154     // Update the cache for any watched properties.
155     for (const auto& p : properties)
156     {
157         auto key = std::make_tuple(path, interface, p.first);
158         auto item = this->index.find(key);
159         if (item == this->index.end())
160         {
161             // This property isn't being watched.
162             continue;
163         }
164 
165         // Run property value thru filter operations
166         auto isFiltered = false;
167         const auto& storage = std::get<storageIndex>(item->second);
168         auto value = std::get<T>(p.second);
169         if (filterOps)
170         {
171             std::any anyValue = value;
172             if ((*filterOps)(anyValue))
173             {
174                 // Property value filtered, clear it from storage so
175                 // callback functions do not use it
176                 isFiltered = true;
177                 std::get<valueIndex>(storage.get()).reset();
178             }
179         }
180         if (!isFiltered)
181         {
182             // Property value not filtered out, update
183             std::get<valueIndex>(storage.get()) = value;
184             // Invoke callback if present.
185             this->callback(Context::SIGNAL);
186         }
187     }
188 }
189 
190 template <typename T, typename DBusInterfaceType>
propertiesChanged(sdbusplus::message_t & msg,const std::string & path,const std::string & interface)191 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
192     sdbusplus::message_t& msg, const std::string& path,
193     const std::string& interface)
194 {
195     PropertiesChanged<T> properties;
196     msg.read(properties);
197     propertiesChanged(path, interface, properties);
198 }
199 
200 template <typename T, typename DBusInterfaceType>
interfacesAdded(const std::string & path,const InterfacesAdded<T> & interfaces)201 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
202     const std::string& path, const InterfacesAdded<T>& interfaces)
203 {
204     for (const auto& i : interfaces)
205     {
206         propertiesChanged(path, i.first, i.second);
207     }
208 }
209 
210 template <typename T, typename DBusInterfaceType>
interfacesAdded(sdbusplus::message_t & msg)211 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
212     sdbusplus::message_t& msg)
213 {
214     sdbusplus::message::object_path path;
215     InterfacesAdded<T> interfaces;
216     msg.read(path, interfaces);
217     interfacesAdded(path, interfaces);
218 }
219 
220 } // namespace monitoring
221 } // namespace dbus
222 } // namespace phosphor
223