xref: /openbmc/phosphor-dbus-monitor/src/propertywatchimpl.hpp (revision fccdc39fdf8f97ef4bf3de3b8ddcae28fef4bc8e)
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 T, typename DBusInterfaceType>
112 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties(
113     const std::string& busName,
114     const std::string& path,
115     const std::string& interface)
116 {
117     auto properties =
118         DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>(
119             busName.c_str(),
120             path.c_str(),
121             "org.freedesktop.DBus.Properties",
122             "GetAll",
123             interface);
124     propertiesChanged(path, interface, properties);
125 }
126 
127 template <typename T, typename DBusInterfaceType>
128 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
129     const std::string& path,
130     const std::string& interface,
131     const PropertiesChanged<T>& properties)
132 {
133     // Update the cache for any watched properties.
134     for (const auto& p : properties)
135     {
136         auto key = std::make_tuple(path, interface, p.first);
137         auto item = this->index.find(key);
138         if (item == this->index.end())
139         {
140             // This property isn't being watched.
141             continue;
142         }
143 
144         std::get<2>(item->second).get() = p.second.template get<T>();
145 
146         // Invoke callback if present.
147         if (this->alreadyRan && this->callback)
148         {
149             (*this->callback)();
150         }
151     }
152 }
153 
154 template <typename T, typename DBusInterfaceType>
155 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
156     sdbusplus::message::message& msg,
157     const std::string& path,
158     const std::string& interface)
159 {
160     PropertiesChanged<T> properties;
161     msg.read(properties);
162     propertiesChanged(path, interface, properties);
163 }
164 
165 template <typename T, typename DBusInterfaceType>
166 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
167     const std::string& path,
168     const InterfacesAdded<T>& interfaces)
169 {
170     for (const auto& i : interfaces)
171     {
172         propertiesChanged(path, i.first, i.second);
173     }
174 }
175 
176 template <typename T, typename DBusInterfaceType>
177 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
178     sdbusplus::message::message& msg)
179 {
180     sdbusplus::message::object_path path;
181     InterfacesAdded<T> interfaces;
182     msg.read(path, interfaces);
183     interfacesAdded(path, interfaces);
184 }
185 
186 } // namespace monitoring
187 } // namespace dbus
188 } // namespace phosphor
189