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 
36     // The index has a flat layout which is not optimal here.  Nest
37     // properties in a map of interface names in a map of object paths.
38     auto mapped = convert(index);
39 
40     for (const auto& m : mapped)
41     {
42         const auto& path = m.first.get();
43         const auto& interfaces = m.second;
44 
45         // Watch for new interfaces on this path.
46         DBusInterfaceType::addMatch(
47             sdbusplus::bus::match::rules::interfacesAdded(path),
48             [this](auto & msg)
49         // *INDENT-OFF*
50             {
51                 this->interfacesAdded(msg);
52             });
53         // *INDENT-ON*
54 
55         // Do a query to populate the cache.  Start with a mapper query.
56         // The specific services are queried below.
57         const std::vector<std::string> queryInterfaces; // all interfaces
58         auto mapperResp =
59             DBusInterfaceType::template callMethodAndRead<GetObject>(
60                 MAPPER_BUSNAME,
61                 MAPPER_PATH,
62                 MAPPER_INTERFACE,
63                 "GetObject",
64                 path,
65                 queryInterfaces);
66 
67         for (const auto& i : interfaces)
68         {
69             const auto& interface = i.first.get();
70 
71             // Watch for property changes on this interface.
72             DBusInterfaceType::addMatch(
73                 sdbusplus::bus::match::rules::propertiesChanged(
74                         path, interface),
75                 [this](auto & msg)
76                 // *INDENT-OFF*
77                 {
78                     std::string interface;
79                     msg.read(interface);
80                     auto path = msg.get_path();
81                     this->propertiesChanged(msg, path, interface);
82                 });
83                 // *INDENT-ON*
84 
85             // The mapper response is a busname:[interfaces] map.  Look for
86             // each interface in the index and if found, query the service and
87             // populate the cache entries for the interface.
88             for (const auto& mr : mapperResp)
89             {
90                 const auto& busName = mr.first;
91                 const auto& mapperInterfaces = mr.second;
92                 if (mapperInterfaces.end() == std::find(
93                         mapperInterfaces.begin(),
94                         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                 updateProperties(busName, path, interface);
103             }
104         }
105     }
106 
107     alreadyRan = true;
108 }
109 
110 template <typename T, typename DBusInterfaceType>
111 void PropertyWatchOfType<T, DBusInterfaceType>::updateProperties(
112     const std::string& busName,
113     const std::string& path,
114     const std::string& interface)
115 {
116     auto properties =
117         DBusInterfaceType::template callMethodAndRead<PropertiesChanged<T>>(
118             busName.c_str(),
119             path.c_str(),
120             "org.freedesktop.DBus.Properties",
121             "GetAll",
122             interface);
123     propertiesChanged(path, interface, properties);
124 }
125 
126 template <typename T, typename DBusInterfaceType>
127 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
128     const std::string& path,
129     const std::string& interface,
130     const PropertiesChanged<T>& properties)
131 {
132     // Update the cache for any watched properties.
133     for (const auto& p : properties)
134     {
135         auto key = std::make_tuple(path, interface, p.first);
136         auto item = this->index.find(key);
137         if (item == this->index.end())
138         {
139             // This property isn't being watched.
140             continue;
141         }
142 
143         std::get<2>(item->second).get() = p.second.template get<T>();
144     }
145 }
146 
147 template <typename T, typename DBusInterfaceType>
148 void PropertyWatchOfType<T, DBusInterfaceType>::propertiesChanged(
149     sdbusplus::message::message& msg,
150     const std::string& path,
151     const std::string& interface)
152 {
153     PropertiesChanged<T> properties;
154     msg.read(properties);
155     propertiesChanged(path, interface, properties);
156 }
157 
158 template <typename T, typename DBusInterfaceType>
159 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
160     const std::string& path,
161     const InterfacesAdded<T>& interfaces)
162 {
163     for (const auto& i : interfaces)
164     {
165         propertiesChanged(path, i.first, i.second);
166     }
167 }
168 
169 template <typename T, typename DBusInterfaceType>
170 void PropertyWatchOfType<T, DBusInterfaceType>::interfacesAdded(
171     sdbusplus::message::message& msg)
172 {
173     sdbusplus::message::object_path path;
174     InterfacesAdded<T> interfaces;
175     msg.read(path, interfaces);
176     interfacesAdded(path, interfaces);
177 }
178 
179 } // namespace monitoring
180 } // namespace dbus
181 } // namespace phosphor
182