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