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     cereal::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     cereal::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     cereal::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 }
131 
132 /** @struct MakeInterface
133  *  @brief Adapt an sdbusplus interface proxy.
134  *
135  *  Template instances are builder functions that create
136  *  adapted sdbusplus interface proxy interface objects.
137  *
138  *  @tparam T - The type of the interface being adapted.
139  */
140 
141 template <typename T>
142 struct MakeInterface
143 {
144     static std::any make(sdbusplus::bus::bus& bus, const char* path,
145                          const Interface& props)
146     {
147         return propMake<T>(bus, path, props);
148     }
149 
150     static void assign(const Interface& props, std::any& holder)
151     {
152         propAssign<T>(props, holder);
153     }
154 
155     static void serialize(const std::string& path, const std::string& iface,
156                           const std::any& holder)
157     {
158         propSerialize<T>(path, iface, holder);
159     }
160 
161     static void deserialize(const std::string& path, const std::string& iface,
162                             std::any& holder)
163     {
164         propDeSerialize<T>(path, iface, holder);
165     }
166 };
167 
168 /** @class Manager
169  *  @brief OpenBMC inventory manager implementation.
170  *
171  *  A concrete implementation for the xyz.openbmc_project.Inventory.Manager
172  *  DBus API.
173  */
174 class Manager final : public ServerObject<ManagerIface>
175 {
176   public:
177     Manager() = delete;
178     Manager(const Manager&) = delete;
179     Manager& operator=(const Manager&) = delete;
180     Manager(Manager&&) = default;
181     Manager& operator=(Manager&&) = default;
182     ~Manager() = default;
183 
184     /** @brief Construct an inventory manager.
185      *
186      *  @param[in] bus - An sdbusplus bus connection.
187      *  @param[in] busname - The DBus busname to own.
188      *  @param[in] root - The DBus path on which to implement
189      *      an inventory manager.
190      *  @param[in] iface - The DBus inventory interface to implement.
191      */
192     Manager(sdbusplus::bus::bus&&, const char*, const char*, const char*);
193 
194     using EventInfo =
195         std::tuple<std::vector<EventBasePtr>, std::vector<Action>>;
196 
197     /** @brief Start processing DBus messages. */
198     void run() noexcept;
199 
200     /** @brief Provided for testing only. */
201     void shutdown() noexcept;
202 
203     /** @brief sd_bus Notify method implementation callback. */
204     void
205         notify(std::map<sdbusplus::message::object_path, Object> objs) override;
206 
207     /** @brief Event processing entry point. */
208     void handleEvent(sdbusplus::message::message&, const Event& event,
209                      const EventInfo& info);
210 
211     /** @brief Drop one or more objects from DBus. */
212     void destroyObjects(const std::vector<const char*>& paths);
213 
214     /** @brief Add objects to DBus. */
215     void createObjects(
216         const std::map<sdbusplus::message::object_path, Object>& objs);
217 
218     /** @brief Add or update objects on DBus. */
219     void updateObjects(
220         const std::map<sdbusplus::message::object_path, Object>& objs,
221         bool restoreFromCache = false);
222 
223     /** @brief Restore persistent inventory items */
224     void restore();
225 
226     /** @brief Invoke an sdbusplus server binding method.
227      *
228      *  Invoke the requested method with a reference to the requested
229      *  sdbusplus server binding interface as a parameter.
230      *
231      *  @tparam T - The sdbusplus server binding interface type.
232      *  @tparam U - The type of the sdbusplus server binding member.
233      *  @tparam Args - Argument types of the binding member.
234      *
235      *  @param[in] path - The DBus path on which the method should
236      *      be invoked.
237      *  @param[in] interface - The DBus interface hosting the method.
238      *  @param[in] member - Pointer to sdbusplus server binding member.
239      *  @param[in] args - Arguments to forward to the binding member.
240      *
241      *  @returns - The return/value type of the binding method being
242      *      called.
243      */
244     template <typename T, typename U, typename... Args>
245     decltype(auto) invokeMethod(const char* path, const char* interface,
246                                 U&& member, Args&&... args)
247     {
248         auto& iface = getInterface<T>(path, interface);
249         return (iface.*member)(std::forward<Args>(args)...);
250     }
251 
252     using SigArgs = std::vector<std::unique_ptr<
253         std::tuple<Manager*, const DbusSignal*, const EventInfo*>>>;
254     using SigArg = SigArgs::value_type::element_type;
255 
256   private:
257     using InterfaceComposite = std::map<std::string, std::any>;
258     using ObjectReferences = std::map<std::string, InterfaceComposite>;
259     using Events = std::vector<EventInfo>;
260 
261     // The int instantiations are safe since the signature of these
262     // functions don't change from one instantiation to the next.
263     using MakerType = std::add_pointer_t<decltype(MakeInterface<int>::make)>;
264     using AssignerType =
265         std::add_pointer_t<decltype(MakeInterface<int>::assign)>;
266     using SerializerType =
267         std::add_pointer_t<decltype(MakeInterface<int>::serialize)>;
268     using DeserializerType =
269         std::add_pointer_t<decltype(MakeInterface<int>::deserialize)>;
270     using Makers =
271         std::map<std::string, std::tuple<MakerType, AssignerType,
272                                          SerializerType, DeserializerType>>;
273 
274     /** @brief Provides weak references to interface holders.
275      *
276      *  Common code for all types for the templated getInterface
277      *  methods.
278      *
279      *  @param[in] path - The DBus path for which the interface
280      *      holder instance should be provided.
281      *  @param[in] interface - The DBus interface for which the
282      *      holder instance should be provided.
283      *
284      *  @returns A weak reference to the holder instance.
285      */
286     const std::any& getInterfaceHolder(const char*, const char*) const;
287     std::any& getInterfaceHolder(const char*, const char*);
288 
289     /** @brief Provides weak references to interface holders.
290      *
291      *  @tparam T - The sdbusplus server binding interface type.
292      *
293      *  @param[in] path - The DBus path for which the interface
294      *      should be provided.
295      *  @param[in] interface - The DBus interface to obtain.
296      *
297      *  @returns A weak reference to the interface holder.
298      */
299     template <typename T>
300     auto& getInterface(const char* path, const char* interface)
301     {
302         auto& holder = getInterfaceHolder(path, interface);
303         return *std::any_cast<std::shared_ptr<T>&>(holder);
304     }
305     template <typename T>
306     auto& getInterface(const char* path, const char* interface) const
307     {
308         auto& holder = getInterfaceHolder(path, interface);
309         return *std::any_cast<T>(holder);
310     }
311 
312     /** @brief Add or update interfaces on DBus. */
313     void updateInterfaces(const sdbusplus::message::object_path& path,
314                           const Object& interfaces,
315                           ObjectReferences::iterator pos,
316                           bool emitSignals = true,
317                           bool restoreFromCache = false);
318 
319     /** @brief Provided for testing only. */
320     volatile bool _shutdown;
321 
322     /** @brief Path prefix applied to any relative paths. */
323     const char* _root;
324 
325     /** @brief A container of sdbusplus server interface references. */
326     ObjectReferences _refs;
327 
328     /** @brief A container contexts for signal callbacks. */
329     SigArgs _sigargs;
330 
331     /** @brief A container of sdbusplus signal matches.  */
332     std::vector<sdbusplus::bus::match_t> _matches;
333 
334     /** @brief Persistent sdbusplus DBus bus connection. */
335     sdbusplus::bus::bus _bus;
336 
337     /** @brief sdbusplus org.freedesktop.DBus.ObjectManager reference. */
338     sdbusplus::server::manager::manager _manager;
339 
340     /** @brief A container of pimgen generated events and responses.  */
341     static const Events _events;
342 
343     /** @brief A container of pimgen generated factory methods.  */
344     static const Makers _makers;
345 };
346 
347 } // namespace manager
348 } // namespace inventory
349 } // namespace phosphor
350 
351 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
352