1 #pragma once
2 
3 #include <boost/system/error_code.hpp>
4 #include <chrono>
5 #include <ipmid/api-types.hpp>
6 #include <ipmid/message.hpp>
7 #include <ipmid/types.hpp>
8 #include <optional>
9 #include <sdbusplus/server.hpp>
10 
11 namespace ipmi
12 {
13 
14 using namespace std::literals::chrono_literals;
15 
16 constexpr auto MAPPER_BUS_NAME = "xyz.openbmc_project.ObjectMapper";
17 constexpr auto MAPPER_OBJ = "/xyz/openbmc_project/object_mapper";
18 constexpr auto MAPPER_INTF = "xyz.openbmc_project.ObjectMapper";
19 
20 constexpr auto ROOT = "/";
21 constexpr auto HOST_MATCH = "host0";
22 
23 constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties";
24 constexpr auto DELETE_INTERFACE = "xyz.openbmc_project.Object.Delete";
25 
26 constexpr auto METHOD_GET = "Get";
27 constexpr auto METHOD_GET_ALL = "GetAll";
28 constexpr auto METHOD_SET = "Set";
29 
30 /* Use a value of 5s which aligns with BT/KCS bridged timeouts, rather
31  * than the default 25s D-Bus timeout. */
32 constexpr std::chrono::microseconds IPMI_DBUS_TIMEOUT = 5s;
33 
34 /** @class ServiceCache
35  *  @brief Caches lookups of service names from the object mapper.
36  *  @details Most ipmi commands need to talk to other dbus daemons to perform
37  *           their intended actions on the BMC. This usually means they will
38  *           first look up the service name providing the interface they
39  *           require. This class reduces the number of such calls by caching
40  *           the lookup for a specific service.
41  */
42 class ServiceCache
43 {
44   public:
45     /** @brief Creates a new service cache for the given interface
46      *         and path.
47      *
48      *  @param[in] intf - The interface used for each lookup
49      *  @param[in] path - The path used for each lookup
50      */
51     ServiceCache(const std::string& intf, const std::string& path);
52     ServiceCache(std::string&& intf, std::string&& path);
53 
54     /** @brief Gets the service name from the cache or does in a
55      *         lookup when invalid.
56      *
57      *  @param[in] bus - The bus associated with and used for looking
58      *                   up the service.
59      */
60     const std::string& getService(sdbusplus::bus_t& bus);
61 
62     /** @brief Invalidates the current service name */
63     void invalidate();
64 
65     /** @brief A wrapper around sdbusplus bus.new_method_call
66      *
67      *  @param[in] bus - The bus used for calling the method
68      *  @param[in] intf - The interface containing the method
69      *  @param[in] method - The method name
70      *  @return The message containing the method call.
71      */
72     sdbusplus::message_t newMethodCall(sdbusplus::bus_t& bus, const char* intf,
73                                        const char* method);
74 
75     /** @brief Check to see if the current cache is valid
76      *
77      * @param[in] bus - The bus used for the service lookup
78      * @return True if the cache is valid false otherwise.
79      */
80     bool isValid(sdbusplus::bus_t& bus) const;
81 
82   private:
83     /** @brief DBUS interface provided by the service */
84     const std::string intf;
85     /** @brief DBUS path provided by the service */
86     const std::string path;
87     /** @brief The name of the service if valid */
88     std::optional<std::string> cachedService;
89     /** @brief The name of the bus used in the service lookup */
90     std::optional<std::string> cachedBusName;
91 };
92 
93 /**
94  * @brief Get the DBUS Service name for the input dbus path
95  *
96  * @param[in] bus - DBUS Bus Object
97  * @param[in] intf - DBUS Interface
98  * @param[in] path - DBUS Object Path
99  *
100  */
101 std::string getService(sdbusplus::bus_t& bus, const std::string& intf,
102                        const std::string& path);
103 
104 /** @brief Gets the dbus object info implementing the given interface
105  *         from the given subtree.
106  *  @param[in] bus - DBUS Bus Object.
107  *  @param[in] interface - Dbus interface.
108  *  @param[in] subtreePath - subtree from where the search should start.
109  *  @param[in] match - identifier for object.
110  *  @return On success returns the object having objectpath and servicename.
111  */
112 DbusObjectInfo getDbusObject(sdbusplus::bus_t& bus,
113                              const std::string& interface,
114                              const std::string& subtreePath = ROOT,
115                              const std::string& match = {});
116 
117 /** @brief Gets the value associated with the given object
118  *         and the interface.
119  *  @param[in] bus - DBUS Bus Object.
120  *  @param[in] service - Dbus service name.
121  *  @param[in] objPath - Dbus object path.
122  *  @param[in] interface - Dbus interface.
123  *  @param[in] property - name of the property.
124  *  @return On success returns the value of the property.
125  */
126 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
127                       const std::string& objPath, const std::string& interface,
128                       const std::string& property,
129                       std::chrono::microseconds timeout = IPMI_DBUS_TIMEOUT);
130 
131 /** @brief Gets all the properties associated with the given object
132  *         and the interface.
133  *  @param[in] bus - DBUS Bus Object.
134  *  @param[in] service - Dbus service name.
135  *  @param[in] objPath - Dbus object path.
136  *  @param[in] interface - Dbus interface.
137  *  @return On success returns the map of name value pair.
138  */
139 PropertyMap
140     getAllDbusProperties(sdbusplus::bus_t& bus, const std::string& service,
141                          const std::string& objPath,
142                          const std::string& interface,
143                          std::chrono::microseconds timeout = IPMI_DBUS_TIMEOUT);
144 
145 /** @brief Gets all managed objects associated with the given object
146  *         path and service.
147  *  @param[in] bus - D-Bus Bus Object.
148  *  @param[in] service - D-Bus service name.
149  *  @param[in] objPath - D-Bus object path.
150  *  @return On success returns the map of name value pair.
151  */
152 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus,
153                                   const std::string& service,
154                                   const std::string& objPath);
155 
156 /** @brief Sets the property value of the given object.
157  *  @param[in] bus - DBUS Bus Object.
158  *  @param[in] service - Dbus service name.
159  *  @param[in] objPath - Dbus object path.
160  *  @param[in] interface - Dbus interface.
161  *  @param[in] property - name of the property.
162  *  @param[in] value - value which needs to be set.
163  */
164 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
165                      const std::string& objPath, const std::string& interface,
166                      const std::string& property, const Value& value,
167                      std::chrono::microseconds timeout = IPMI_DBUS_TIMEOUT);
168 
169 /** @brief  Gets all the dbus objects from the given service root
170  *          which matches the object identifier.
171  *  @param[in] bus - DBUS Bus Object.
172  *  @param[in] serviceRoot - Service root path.
173  *  @param[in] interface - Dbus interface.
174  *  @param[in] match - Identifier for a path.
175  *  @returns map of object path and service info.
176  */
177 ObjectTree getAllDbusObjects(sdbusplus::bus_t& bus,
178                              const std::string& serviceRoot,
179                              const std::string& interface,
180                              const std::string& match = {});
181 
182 /** @brief Deletes all the dbus objects from the given service root
183            which matches the object identifier.
184  *  @param[in] bus - DBUS Bus Object.
185  *  @param[in] serviceRoot - Service root path.
186  *  @param[in] interface - Dbus interface.
187  *  @param[in] match - Identifier for object.
188  */
189 void deleteAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot,
190                           const std::string& interface,
191                           const std::string& match = {});
192 
193 /** @brief Gets the ancestor objects of the given object
194            which implements the given interface.
195  *  @param[in] bus - Dbus bus object.
196  *  @param[in] path - Child Dbus object path.
197  *  @param[in] interfaces - Dbus interface list.
198  *  @return map of object path and service info.
199  */
200 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path,
201                            InterfaceList&& interfaces);
202 
203 /********* Begin co-routine yielding alternatives ***************/
204 
205 /** @brief Get the D-Bus Service name for the input D-Bus path
206  *
207  *  @param[in] ctx - ipmi::Context::ptr
208  *  @param[in] intf - D-Bus Interface
209  *  @param[in] path - D-Bus Object Path
210  *  @param[out] service - requested service name
211  *  @return boost error code
212  *
213  */
214 boost::system::error_code getService(Context::ptr ctx, const std::string& intf,
215                                      const std::string& path,
216                                      std::string& service);
217 
218 /** @brief Gets the D-Bus object info implementing the given interface
219  *         from the given subtree.
220  *  @param[in] ctx - ipmi::Context::ptr
221  *  @param[in] interface - D-Bus interface.
222  *  @param[in][optional] subtreePath - subtree from where the search starts.
223  *  @param[in][optional] match - identifier for object.
224  *  @param[out] D-Bus object with path and service name
225  *  @return - boost error code object
226  */
227 boost::system::error_code getDbusObject(Context::ptr ctx,
228                                         const std::string& interface,
229                                         const std::string& subtreePath,
230                                         const std::string& match,
231                                         DbusObjectInfo& dbusObject);
232 
233 // default for ROOT for subtreePath and std::string{} for match
234 static inline boost::system::error_code
235     getDbusObject(Context::ptr ctx, const std::string& interface,
236                   DbusObjectInfo& dbusObject)
237 {
238     return getDbusObject(ctx, interface, ROOT, {}, dbusObject);
239 }
240 
241 // default std::string{} for match
242 static inline boost::system::error_code
243     getDbusObject(Context::ptr ctx, const std::string& interface,
244                   const std::string& subtreePath, DbusObjectInfo& dbusObject)
245 {
246     return getDbusObject(ctx, interface, subtreePath, {}, dbusObject);
247 }
248 
249 /** @brief Gets the value associated with the given object
250  *         and the interface.
251  *  @param[in] ctx - ipmi::Context::ptr
252  *  @param[in] service - D-Bus service name.
253  *  @param[in] objPath - D-Bus object path.
254  *  @param[in] interface - D-Bus interface.
255  *  @param[in] property - name of the property.
256  *  @param[out] propertyValue - value of the D-Bus property.
257  *  @return - boost error code object
258  */
259 template <typename Type>
260 boost::system::error_code
261     getDbusProperty(Context::ptr ctx, const std::string& service,
262                     const std::string& objPath, const std::string& interface,
263                     const std::string& property, Type& propertyValue)
264 {
265     boost::system::error_code ec;
266     auto variant = ctx->bus->yield_method_call<std::variant<Type>>(
267         ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, METHOD_GET,
268         interface, property);
269     if (!ec)
270     {
271         Type* tmp = std::get_if<Type>(&variant);
272         if (tmp)
273         {
274             propertyValue = *tmp;
275             return ec;
276         }
277         // user requested incorrect type; make an error code for them
278         ec = boost::system::errc::make_error_code(
279             boost::system::errc::invalid_argument);
280     }
281     return ec;
282 }
283 
284 /** @brief Gets all the properties associated with the given object
285  *         and the interface.
286  *  @param[in] ctx - ipmi::Context::ptr
287  *  @param[in] service - D-Bus service name.
288  *  @param[in] objPath - D-Bus object path.
289  *  @param[in] interface - D-Bus interface.
290  *  @param[out] properties - map of name value pair.
291  *  @return - boost error code object
292  */
293 boost::system::error_code getAllDbusProperties(Context::ptr ctx,
294                                                const std::string& service,
295                                                const std::string& objPath,
296                                                const std::string& interface,
297                                                PropertyMap& properties);
298 
299 /** @brief Sets the property value of the given object.
300  *  @param[in] ctx - ipmi::Context::ptr
301  *  @param[in] service - D-Bus service name.
302  *  @param[in] objPath - D-Bus object path.
303  *  @param[in] interface - D-Bus interface.
304  *  @param[in] property - name of the property.
305  *  @param[in] value - value which needs to be set.
306  *  @return - boost error code object
307  */
308 boost::system::error_code
309     setDbusProperty(Context::ptr ctx, const std::string& service,
310                     const std::string& objPath, const std::string& interface,
311                     const std::string& property, const Value& value);
312 
313 /** @brief  Gets all the D-Bus objects from the given service root
314  *          which matches the object identifier.
315  *  @param[in] ctx - ipmi::Context::ptr
316  *  @param[in] serviceRoot - Service root path.
317  *  @param[in] interface - D-Bus interface.
318  *  @param[in][optional] match - Identifier for a path.
319  *  @param[out] objectree - map of object path and service info.
320  *  @return - boost error code object
321  */
322 boost::system::error_code getAllDbusObjects(Context::ptr ctx,
323                                             const std::string& serviceRoot,
324                                             const std::string& interface,
325                                             const std::string& match,
326                                             ObjectTree& objectTree);
327 
328 // default std::string{} for match
329 static inline boost::system::error_code
330     getAllDbusObjects(Context::ptr ctx, const std::string& serviceRoot,
331                       const std::string& interface, ObjectTree& objectTree)
332 {
333     return getAllDbusObjects(ctx, serviceRoot, interface, {}, objectTree);
334 }
335 
336 /** @brief Deletes all the D-Bus objects from the given service root
337            which matches the object identifier.
338  *  @param[in] ctx - ipmi::Context::ptr
339  *  @param[out] ec - boost error code object
340  *  @param[in] serviceRoot - Service root path.
341  *  @param[in] interface - D-Bus interface.
342  *  @param[in] match - Identifier for object.
343  */
344 boost::system::error_code deleteAllDbusObjects(Context::ptr ctx,
345                                                const std::string& serviceRoot,
346                                                const std::string& interface,
347                                                const std::string& match = {});
348 
349 /** @brief Gets all managed objects associated with the given object
350  *         path and service.
351  *  @param[in] ctx - ipmi::Context::ptr
352  *  @param[in] service - D-Bus service name.
353  *  @param[in] objPath - D-Bus object path.
354  *  @param[out] objects - map of name value pair.
355  *  @return - boost error code object
356  */
357 boost::system::error_code getManagedObjects(Context::ptr ctx,
358                                             const std::string& service,
359                                             const std::string& objPath,
360                                             ObjectValueTree& objects);
361 
362 /** @brief Gets the ancestor objects of the given object
363            which implements the given interface.
364  *  @param[in] ctx - ipmi::Context::ptr
365  *  @param[in] path - Child D-Bus object path.
366  *  @param[in] interfaces - D-Bus interface list.
367  *  @param[out] ObjectTree - map of object path and service info.
368  *  @return - boost error code object
369  */
370 boost::system::error_code getAllAncestors(Context::ptr ctx,
371                                           const std::string& path,
372                                           const InterfaceList& interfaces,
373                                           ObjectTree& objectTree);
374 
375 /** @brief Gets the value associated with the given object
376  *         and the interface.
377  *  @param[in] ctx - ipmi::Context::ptr
378  *  @param[in] service - D-Bus service name.
379  *  @param[in] objPath - D-Bus object path.
380  *  @param[in] interface - D-Bus interface.
381  *  @param[in] method - name of the method.
382  *  @return - boost error code object
383  */
384 
385 boost::system::error_code callDbusMethod(Context::ptr ctx,
386                                          const std::string& service,
387                                          const std::string& objPath,
388                                          const std::string& interface,
389                                          const std::string& method);
390 
391 /********* End co-routine yielding alternatives ***************/
392 
393 /** @brief Retrieve the value from map of variants,
394  *         returning a default if the key does not exist or the
395  *         type of the value does not match the expected type
396  *
397  *  @tparam T - type of expected value to return
398  *  @param[in] props - D-Bus propery map (Map of variants)
399  *  @param[in] name - key name of property to fetch
400  *  @param[in] defaultValue - default value to return on error
401  *  @return - value from propery map at name, or defaultValue
402  */
403 template <typename T>
404 T mappedVariant(const ipmi::PropertyMap& props, const std::string& name,
405                 const T& defaultValue)
406 {
407     auto item = props.find(name);
408     if (item == props.end())
409     {
410         return defaultValue;
411     }
412     const T* prop = std::get_if<T>(&item->second);
413     if (!prop)
414     {
415         return defaultValue;
416     }
417     return *prop;
418 }
419 
420 /** @struct VariantToDoubleVisitor
421  *  @brief Visitor to convert variants to doubles
422  *  @details Performs a static cast on the underlying type
423  */
424 struct VariantToDoubleVisitor
425 {
426     template <typename T>
427     std::enable_if_t<std::is_arithmetic<T>::value, double>
428         operator()(const T& t) const
429     {
430         return static_cast<double>(t);
431     }
432 
433     template <typename T>
434     std::enable_if_t<!std::is_arithmetic<T>::value, double>
435         operator()(const T&) const
436     {
437         throw std::invalid_argument("Cannot translate type to double");
438     }
439 };
440 
441 namespace method_no_args
442 {
443 
444 /** @brief Calls the Dbus method which waits for response.
445  *  @param[in] bus - DBUS Bus Object.
446  *  @param[in] service - Dbus service name.
447  *  @param[in] objPath - Dbus object path.
448  *  @param[in] interface - Dbus interface.
449  *  @param[in] method - Dbus method.
450  */
451 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service,
452                     const std::string& objPath, const std::string& interface,
453                     const std::string& method);
454 
455 } // namespace method_no_args
456 
457 /** @brief Perform the low-level i2c bus write-read.
458  *  @param[in] i2cBus - i2c bus device node name, such as /dev/i2c-2.
459  *  @param[in] slaveAddr - i2c device slave address.
460  *  @param[in] writeData - The data written to i2c device.
461  *  @param[out] readBuf - Data read from the i2c device.
462  */
463 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr,
464                       std::vector<uint8_t> writeData,
465                       std::vector<uint8_t>& readBuf);
466 } // namespace ipmi
467