1 #pragma once
2 
3 #include "events.hpp"
4 #include "functor.hpp"
5 #include "serialize.hpp"
6 #include "types.hpp"
7 
8 #include <any>
9 #include <map>
10 #include <memory>
11 #include <sdbusplus/server.hpp>
12 #include <string>
13 #include <vector>
14 #include <xyz/openbmc_project/Inventory/Manager/server.hpp>
15 
16 namespace phosphor
17 {
18 namespace inventory
19 {
20 namespace manager
21 {
22 
23 template <typename T>
24 using ServerObject = T;
25 
26 using ManagerIface =
27     sdbusplus::xyz::openbmc_project::Inventory::server::Manager;
28 
29 /** @struct PropertiesVariant
30  *  @brief Wrapper for sdbusplus PropertiesVariant.
31  *
32  *  A wrapper is useful since MakeInterface is instantiated with 'int'
33  *  to deduce the return type of its methods, which does not depend
34  *  on T.
35  *
36  *  @tparam T - The sdbusplus server binding type.
37  */
38 template <typename T, typename Enable = void>
39 struct PropertiesVariant
40 {
41 };
42 
43 template <typename T>
44 struct PropertiesVariant<
45     T, typename std::enable_if<std::is_object<T>::value>::type>
46 {
47     using Type = typename T::PropertiesVariant;
48 };
49 
50 template <typename T>
51 using PropertiesVariantType = typename PropertiesVariant<T>::Type;
52 
53 template <typename T, typename U = int>
54 struct HasProperties : std::false_type
55 {
56 };
57 
58 template <typename T>
59 struct HasProperties<
60     T, decltype((void)std::declval<typename T::PropertiesVariant>(), 0)>
61     : std::true_type
62 {
63 };
64 
65 template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
66 std::any propMake(sdbusplus::bus::bus& bus, const char* path,
67                   const Interface& props)
68 {
69     using InterfaceVariant = std::map<std::string, PropertiesVariantType<T>>;
70 
71     InterfaceVariant v;
72     for (const auto& p : props)
73     {
74         v.emplace(p.first, convertVariant<PropertiesVariantType<T>>(p.second));
75     }
76 
77     return std::any(std::make_shared<T>(bus, path, v));
78 }
79 
80 template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
81 std::any propMake(sdbusplus::bus::bus& bus, const char* path,
82                   const Interface& props)
83 {
84     return std::any(std::make_shared<T>(bus, path));
85 }
86 
87 template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
88 void propAssign(const Interface& props, std::any& holder)
89 {
90     auto& iface = *std::any_cast<std::shared_ptr<T>&>(holder);
91     for (const auto& p : props)
92     {
93         iface.setPropertyByName(
94             p.first, convertVariant<PropertiesVariantType<T>>(p.second));
95     }
96 }
97 
98 template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
99 void propAssign(const Interface& props, std::any& holder)
100 {
101 }
102 
103 template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
104 void propSerialize(const std::string& path, const std::string& iface,
105                    const std::any& holder)
106 {
107     const auto& object = *std::any_cast<const std::shared_ptr<T>&>(holder);
108     SerialOps::serialize(path, iface, object);
109 }
110 
111 template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
112 void propSerialize(const std::string& path, const std::string& iface,
113                    const std::any& holder)
114 {
115     SerialOps::serialize(path, iface);
116 }
117 
118 template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
119 void propDeSerialize(const std::string& path, const std::string& iface,
120                      std::any& holder)
121 {
122     auto& object = *std::any_cast<std::shared_ptr<T>&>(holder);
123     SerialOps::deserialize(path, iface, object);
124 }
125 
126 template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
127 void propDeSerialize(const std::string& path, const std::string& iface,
128                      std::any& holder)
129 {
130     SerialOps::deserialize(path, iface);
131 }
132 
133 /** @struct MakeInterface
134  *  @brief Adapt an sdbusplus interface proxy.
135  *
136  *  Template instances are builder functions that create
137  *  adapted sdbusplus interface proxy interface objects.
138  *
139  *  @tparam T - The type of the interface being adapted.
140  */
141 
142 template <typename T>
143 struct MakeInterface
144 {
145     static std::any make(sdbusplus::bus::bus& bus, const char* path,
146                          const Interface& props)
147     {
148         return propMake<T>(bus, path, props);
149     }
150 
151     static void assign(const Interface& props, std::any& holder)
152     {
153         propAssign<T>(props, holder);
154     }
155 
156     static void serialize(const std::string& path, const std::string& iface,
157                           const std::any& holder)
158     {
159         propSerialize<T>(path, iface, holder);
160     }
161 
162     static void deserialize(const std::string& path, const std::string& iface,
163                             std::any& holder)
164     {
165         propDeSerialize<T>(path, iface, holder);
166     }
167 };
168 
169 /** @class Manager
170  *  @brief OpenBMC inventory manager implementation.
171  *
172  *  A concrete implementation for the xyz.openbmc_project.Inventory.Manager
173  *  DBus API.
174  */
175 class Manager final : public ServerObject<ManagerIface>
176 {
177   public:
178     Manager() = delete;
179     Manager(const Manager&) = delete;
180     Manager& operator=(const Manager&) = delete;
181     Manager(Manager&&) = default;
182     Manager& operator=(Manager&&) = default;
183     ~Manager() = default;
184 
185     /** @brief Construct an inventory manager.
186      *
187      *  @param[in] bus - An sdbusplus bus connection.
188      *  @param[in] busname - The DBus busname to own.
189      *  @param[in] root - The DBus path on which to implement
190      *      an inventory manager.
191      *  @param[in] iface - The DBus inventory interface to implement.
192      */
193     Manager(sdbusplus::bus::bus&&, const char*, const char*, const char*);
194 
195     using EventInfo =
196         std::tuple<std::vector<EventBasePtr>, std::vector<Action>>;
197 
198     /** @brief Start processing DBus messages. */
199     void run() noexcept;
200 
201     /** @brief Provided for testing only. */
202     void shutdown() noexcept;
203 
204     /** @brief sd_bus Notify method implementation callback. */
205     void
206         notify(std::map<sdbusplus::message::object_path, Object> objs) override;
207 
208     /** @brief Event processing entry point. */
209     void handleEvent(sdbusplus::message::message&, const Event& event,
210                      const EventInfo& info);
211 
212     /** @brief Drop one or more objects from DBus. */
213     void destroyObjects(const std::vector<const char*>& paths);
214 
215     /** @brief Add objects to DBus. */
216     void createObjects(
217         const std::map<sdbusplus::message::object_path, Object>& objs);
218 
219     /** @brief Add or update objects on DBus. */
220     void updateObjects(
221         const std::map<sdbusplus::message::object_path, Object>& objs,
222         bool restoreFromCache = false);
223 
224     /** @brief Restore persistent inventory items */
225     void restore();
226 
227     /** @brief Invoke an sdbusplus server binding method.
228      *
229      *  Invoke the requested method with a reference to the requested
230      *  sdbusplus server binding interface as a parameter.
231      *
232      *  @tparam T - The sdbusplus server binding interface type.
233      *  @tparam U - The type of the sdbusplus server binding member.
234      *  @tparam Args - Argument types of the binding member.
235      *
236      *  @param[in] path - The DBus path on which the method should
237      *      be invoked.
238      *  @param[in] interface - The DBus interface hosting the method.
239      *  @param[in] member - Pointer to sdbusplus server binding member.
240      *  @param[in] args - Arguments to forward to the binding member.
241      *
242      *  @returns - The return/value type of the binding method being
243      *      called.
244      */
245     template <typename T, typename U, typename... Args>
246     decltype(auto) invokeMethod(const char* path, const char* interface,
247                                 U&& member, Args&&... args)
248     {
249         auto& iface = getInterface<T>(path, interface);
250         return (iface.*member)(std::forward<Args>(args)...);
251     }
252 
253     using SigArgs = std::vector<std::unique_ptr<
254         std::tuple<Manager*, const DbusSignal*, const EventInfo*>>>;
255     using SigArg = SigArgs::value_type::element_type;
256 
257   private:
258     using InterfaceComposite = std::map<std::string, std::any>;
259     using ObjectReferences = std::map<std::string, InterfaceComposite>;
260     using Events = std::vector<EventInfo>;
261 
262     // The int instantiations are safe since the signature of these
263     // functions don't change from one instantiation to the next.
264     using MakerType = std::add_pointer_t<decltype(MakeInterface<int>::make)>;
265     using AssignerType =
266         std::add_pointer_t<decltype(MakeInterface<int>::assign)>;
267     using SerializerType =
268         std::add_pointer_t<decltype(MakeInterface<int>::serialize)>;
269     using DeserializerType =
270         std::add_pointer_t<decltype(MakeInterface<int>::deserialize)>;
271     using Makers =
272         std::map<std::string, std::tuple<MakerType, AssignerType,
273                                          SerializerType, DeserializerType>>;
274 
275     /** @brief Provides weak references to interface holders.
276      *
277      *  Common code for all types for the templated getInterface
278      *  methods.
279      *
280      *  @param[in] path - The DBus path for which the interface
281      *      holder instance should be provided.
282      *  @param[in] interface - The DBus interface for which the
283      *      holder instance should be provided.
284      *
285      *  @returns A weak reference to the holder instance.
286      */
287     const std::any& getInterfaceHolder(const char*, const char*) const;
288     std::any& getInterfaceHolder(const char*, const char*);
289 
290     /** @brief Provides weak references to interface holders.
291      *
292      *  @tparam T - The sdbusplus server binding interface type.
293      *
294      *  @param[in] path - The DBus path for which the interface
295      *      should be provided.
296      *  @param[in] interface - The DBus interface to obtain.
297      *
298      *  @returns A weak reference to the interface holder.
299      */
300     template <typename T>
301     auto& getInterface(const char* path, const char* interface)
302     {
303         auto& holder = getInterfaceHolder(path, interface);
304         return *std::any_cast<std::shared_ptr<T>&>(holder);
305     }
306     template <typename T>
307     auto& getInterface(const char* path, const char* interface) const
308     {
309         auto& holder = getInterfaceHolder(path, interface);
310         return *std::any_cast<T>(holder);
311     }
312 
313     /** @brief Add or update interfaces on DBus. */
314     void updateInterfaces(const sdbusplus::message::object_path& path,
315                           const Object& interfaces,
316                           ObjectReferences::iterator pos,
317                           bool emitSignals = true,
318                           bool restoreFromCache = false);
319 
320     /** @brief Provided for testing only. */
321     volatile bool _shutdown;
322 
323     /** @brief Path prefix applied to any relative paths. */
324     const char* _root;
325 
326     /** @brief A container of sdbusplus server interface references. */
327     ObjectReferences _refs;
328 
329     /** @brief A container contexts for signal callbacks. */
330     SigArgs _sigargs;
331 
332     /** @brief A container of sdbusplus signal matches.  */
333     std::vector<sdbusplus::bus::match_t> _matches;
334 
335     /** @brief Persistent sdbusplus DBus bus connection. */
336     sdbusplus::bus::bus _bus;
337 
338     /** @brief sdbusplus org.freedesktop.DBus.ObjectManager reference. */
339     sdbusplus::server::manager::manager _manager;
340 
341     /** @brief A container of pimgen generated events and responses.  */
342     static const Events _events;
343 
344     /** @brief A container of pimgen generated factory methods.  */
345     static const Makers _makers;
346 };
347 
348 } // namespace manager
349 } // namespace inventory
350 } // namespace phosphor
351 
352 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
353