xref: /openbmc/phosphor-dbus-monitor/src/propertywatchimpl.hpp (revision 13fd8722e616dd424cd585187fb0ef65f6316023)
1 #pragma once
2 
3 #include <sdbusplus/message.hpp>
4 #include <sdbusplus/bus/match.hpp>
5 #include <vector>
6 #include "data_types.hpp"
7 #include "propertywatch.hpp"
8 
9 namespace phosphor
10 {
11 namespace dbus
12 {
13 namespace monitoring
14 {
15 
16 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
17 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
18 static constexpr auto MAPPER_INTERFACE =
19     "xyz.openbmc_project.ObjectMapper";
20 
21 using MappedPropertyIndex =
22     RefKeyMap<const std::string,
23     RefKeyMap<const std::string,
24     RefVector<const std::string>>>;
25 
26 MappedPropertyIndex convert(const PropertyIndex& index);
27 
28 template <typename DBusInterfaceType>
29 void PropertyWatch<DBusInterfaceType>::start()
30 {
31     if (alreadyRan)
32     {
33         return;
34     }
35     else
36     {
37         alreadyRan = true;
38     }
39 
40     // The index has a flat layout which is not optimal here.  Nest
41     // properties in a map of interface names in a map of object paths.
42     auto mapped = convert(index);
43 
44     for (const auto& m : mapped)
45     {
46         const auto& path = m.first.get();
47         const auto& interfaces = m.second;
48 
49         // Watch for new interfaces on this path.
50         DBusInterfaceType::addMatch(
51             sdbusplus::bus::match::rules::interfacesAdded(path),
52             [this](auto & msg)
53         // *INDENT-OFF*
54             {
55                 this->interfacesAdded(msg);
56             });
57         // *INDENT-ON*
58 
59         // Do a query to populate the cache.  Start with a mapper query.
60         // The specific services are queried below.
61         const std::vector<std::string> queryInterfaces; // all interfaces
62         auto mapperResp =
63             DBusInterfaceType::template callMethodAndRead<GetObject>(
64                 MAPPER_BUSNAME,
65                 MAPPER_PATH,
66                 MAPPER_INTERFACE,
67                 "GetObject",
68                 path,
69                 queryInterfaces);
70 
71         for (const auto& i : interfaces)
72         {
73             const auto& interface = i.first.get();
74 
75             // Watch for property changes on this interface.
76             DBusInterfaceType::addMatch(
77                 sdbusplus::bus::match::rules::propertiesChanged(
78                         path, interface),
79                 [this](auto & msg)
80                 // *INDENT-OFF*
81                 {
82                     std::string interface;
83                     msg.read(interface);
84                     auto path = msg.get_path();
85                     this->propertiesChanged(msg, path, interface);
86                 });
87                 // *INDENT-ON*
88 
89             // The mapper response is a busname:[interfaces] map.  Look for
90             // each interface in the index and if found, query the service and
91             // populate the cache entries for the interface.
92             for (const auto& mr : mapperResp)
93             {
94                 const auto& busName = mr.first;
95                 const auto& mapperInterfaces = mr.second;
96                 if (mapperInterfaces.end() == std::find(
97                         mapperInterfaces.begin(),
98                         mapperInterfaces.end(),
99                         interface))
100                 {
101                     // This interface isn't being watched.
102                     continue;
103                 }
104 
105                 // Delegate type specific property updates to subclasses.
106                 updateProperties(busName, path, interface);
107             }
108         }
109     }
110 }
111 
112 template <typename T, typename DBusInterfaceType>
113 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties(
114     const std::string& busName,
115     const std::string& path,
116     const std::string& interface)
117 {
118     auto properties =
119         DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>(
120             busName.c_str(),
121             path.c_str(),
122             "org.freedesktop.DBus.Properties",
123             "GetAll",
124             interface);
125     propertiesChanged(path, interface, properties);
126 }
127 
128 template <typename T, typename DBusInterfaceType>
129 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
130     const std::string& path,
131     const std::string& interface,
132     const PropertiesChanged<T>& properties)
133 {
134     // Update the cache for any watched properties.
135     for (const auto& p : properties)
136     {
137         auto key = std::make_tuple(path, interface, p.first);
138         auto item = this->index.find(key);
139         if (item == this->index.end())
140         {
141             // This property isn't being watched.
142             continue;
143         }
144 
145         std::get<2>(item->second).get() = p.second.template get<T>();
146     }
147 }
148 
149 template <typename T, typename DBusInterfaceType>
150 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
151     sdbusplus::message::message& msg,
152     const std::string& path,
153     const std::string& interface)
154 {
155     PropertiesChanged<T> properties;
156     msg.read(properties);
157     propertiesChanged(path, interface, properties);
158 }
159 
160 template <typename T, typename DBusInterfaceType>
161 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
162     const std::string& path,
163     const InterfacesAdded<T>& interfaces)
164 {
165     for (const auto& i : interfaces)
166     {
167         propertiesChanged(path, i.first, i.second);
168     }
169 }
170 
171 template <typename T, typename DBusInterfaceType>
172 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
173     sdbusplus::message::message& msg)
174 {
175     sdbusplus::message::object_path path;
176     InterfacesAdded<T> interfaces;
177     msg.read(path, interfaces);
178     interfacesAdded(path, interfaces);
179 }
180 
181 } // namespace monitoring
182 } // namespace dbus
183 } // namespace phosphor
184