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 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
18 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
19 static constexpr auto MAPPER_INTERFACE =
20     "xyz.openbmc_project.ObjectMapper";
21 
22 using MappedPropertyIndex =
23     RefKeyMap<const std::string,
24     RefKeyMap<const std::string,
25     RefVector<const std::string>>>;
26 
27 MappedPropertyIndex convert(const PropertyIndex& index);
28 
29 template <typename DBusInterfaceType>
30 void PropertyWatch<DBusInterfaceType>::start()
31 {
32     if (alreadyRan)
33     {
34         return;
35     }
36 
37     // The index has a flat layout which is not optimal here.  Nest
38     // properties in a map of interface names in a map of object paths.
39     auto mapped = convert(index);
40 
41     for (const auto& m : mapped)
42     {
43         const auto& path = m.first.get();
44         const auto& interfaces = m.second;
45 
46         // Watch for new interfaces on this path.
47         DBusInterfaceType::addMatch(
48             sdbusplus::bus::match::rules::interfacesAdded(path),
49             [this](auto & msg)
50         // *INDENT-OFF*
51             {
52                 this->interfacesAdded(msg);
53             });
54         // *INDENT-ON*
55 
56         // Do a query to populate the cache.  Start with a mapper query.
57         // The specific services are queried below.
58         const std::vector<std::string> queryInterfaces; // all interfaces
59         auto mapperResp =
60             DBusInterfaceType::template callMethodAndRead<GetObject>(
61                 MAPPER_BUSNAME,
62                 MAPPER_PATH,
63                 MAPPER_INTERFACE,
64                 "GetObject",
65                 path,
66                 queryInterfaces);
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(
75                         path, 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() == std::find(
94                         mapperInterfaces.begin(),
95                         mapperInterfaces.end(),
96                         interface))
97                 {
98                     // This interface isn't being watched.
99                     continue;
100                 }
101 
102                 // Delegate type specific property updates to subclasses.
103                 updateProperties(busName, path, interface);
104             }
105         }
106     }
107 
108     alreadyRan = true;
109 }
110 
111 template <typename DBusInterfaceType>
112 void PropertyWatch<DBusInterfaceType>::callback()
113 {
114     // Invoke callback if present.
115     if (this->alreadyRan && this->cb)
116     {
117         (*this->cb)();
118     }
119 }
120 
121 template <typename T, typename DBusInterfaceType>
122 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties(
123     const std::string& busName,
124     const std::string& path,
125     const std::string& interface)
126 {
127     auto properties =
128         DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>(
129             busName.c_str(),
130             path.c_str(),
131             "org.freedesktop.DBus.Properties",
132             "GetAll",
133             interface);
134     propertiesChanged(path, interface, properties);
135 }
136 
137 template <typename T, typename DBusInterfaceType>
138 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
139     const std::string& path,
140     const std::string& interface,
141     const PropertiesChanged<T>& properties)
142 {
143     // Update the cache for any watched properties.
144     for (const auto& p : properties)
145     {
146         auto key = std::make_tuple(path, interface, p.first);
147         auto item = this->index.find(key);
148         if (item == this->index.end())
149         {
150             // This property isn't being watched.
151             continue;
152         }
153 
154         std::get<2>(item->second).get() = p.second.template get<T>();
155 
156         // Invoke callback if present.
157         this->callback();
158     }
159 }
160 
161 template <typename T, typename DBusInterfaceType>
162 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
163     sdbusplus::message::message& msg,
164     const std::string& path,
165     const std::string& interface)
166 {
167     PropertiesChanged<T> properties;
168     msg.read(properties);
169     propertiesChanged(path, interface, properties);
170 }
171 
172 template <typename T, typename DBusInterfaceType>
173 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
174     const std::string& path,
175     const InterfacesAdded<T>& interfaces)
176 {
177     for (const auto& i : interfaces)
178     {
179         propertiesChanged(path, i.first, i.second);
180     }
181 }
182 
183 template <typename T, typename DBusInterfaceType>
184 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
185     sdbusplus::message::message& msg)
186 {
187     sdbusplus::message::object_path path;
188     InterfacesAdded<T> interfaces;
189     msg.read(path, interfaces);
190     interfacesAdded(path, interfaces);
191 }
192 
193 } // namespace monitoring
194 } // namespace dbus
195 } // namespace phosphor
196