xref: /openbmc/phosphor-dbus-monitor/src/propertywatchimpl.hpp (revision eab4f8c0a047e1aaedf74d6144d83132d1b003de)
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