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