xref: /openbmc/phosphor-host-ipmid/include/ipmid/utils.hpp (revision 93aa983e36a7140d594e175a00a5b3a0021146b8)
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 #include <string>
13 #include <vector>
14 
15 namespace ipmi
16 {
17 
18 using namespace std::literals::chrono_literals;
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 sub tree implementing the given interface.
105  *  @param[in] bus - DBUS Bus Object.
106  *  @param[in] interfaces - Dbus interface.
107  *  @param[in] subtreePath - subtree from where the search should start.
108  *  @param[in] depth - Search depth
109  *  @return map of object path and service info.
110  */
111 ObjectTree getSubTree(sdbusplus::bus_t& bus, const InterfaceList& interface,
112                       const std::string& subtreePath = ROOT, int32_t depth = 0);
113 
114 /** @brief Gets the dbus object info implementing the given interface
115  *         from the given subtree.
116  *  @param[in] bus - DBUS Bus Object.
117  *  @param[in] interface - Dbus interface.
118  *  @param[in] subtreePath - subtree from where the search should start.
119  *  @param[in] match - identifier for object.
120  *  @return On success returns the object having objectpath and servicename.
121  */
122 DbusObjectInfo getDbusObject(
123     sdbusplus::bus_t& bus, const std::string& interface,
124     const std::string& subtreePath = ROOT, const std::string& match = {});
125 
126 /** @brief Gets the value associated with the given object
127  *         and the interface.
128  *  @param[in] bus - DBUS Bus Object.
129  *  @param[in] service - Dbus service name.
130  *  @param[in] objPath - Dbus object path.
131  *  @param[in] interface - Dbus interface.
132  *  @param[in] property - name of the property.
133  *  @return On success returns the value of the property.
134  */
135 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
136                       const std::string& objPath, const std::string& interface,
137                       const std::string& property,
138                       std::chrono::microseconds timeout = IPMI_DBUS_TIMEOUT);
139 
140 /** @brief Gets all the properties associated with the given object
141  *         and the interface.
142  *  @param[in] bus - DBUS Bus Object.
143  *  @param[in] service - Dbus service name.
144  *  @param[in] objPath - Dbus object path.
145  *  @param[in] interface - Dbus interface.
146  *  @return On success returns the map of name value pair.
147  */
148 PropertyMap getAllDbusProperties(
149     sdbusplus::bus_t& bus, const std::string& service,
150     const std::string& objPath, const std::string& interface,
151     std::chrono::microseconds timeout = IPMI_DBUS_TIMEOUT);
152 
153 /** @brief Gets all managed objects associated with the given object
154  *         path and service.
155  *  @param[in] bus - D-Bus Bus Object.
156  *  @param[in] service - D-Bus service name.
157  *  @param[in] objPath - D-Bus object path.
158  *  @return On success returns the map of name value pair.
159  */
160 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus,
161                                   const std::string& service,
162                                   const std::string& objPath);
163 
164 /** @brief Sets the property value of the given object.
165  *  @param[in] bus - DBUS Bus Object.
166  *  @param[in] service - Dbus service name.
167  *  @param[in] objPath - Dbus object path.
168  *  @param[in] interface - Dbus interface.
169  *  @param[in] property - name of the property.
170  *  @param[in] value - value which needs to be set.
171  */
172 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
173                      const std::string& objPath, const std::string& interface,
174                      const std::string& property, const Value& value,
175                      std::chrono::microseconds timeout = IPMI_DBUS_TIMEOUT);
176 
177 /** @brief  Gets all the dbus objects from the given service root
178  *          which matches the object identifier.
179  *  @param[in] bus - DBUS Bus Object.
180  *  @param[in] serviceRoot - Service root path.
181  *  @param[in] interface - Dbus interface.
182  *  @param[in] match - Identifier for a path.
183  *  @returns map of object path and service info.
184  */
185 ObjectTree getAllDbusObjects(
186     sdbusplus::bus_t& bus, const std::string& serviceRoot,
187     const std::string& interface, const std::string& match = {});
188 
189 /********* Begin co-routine yielding alternatives ***************/
190 
191 /** @brief Get the D-Bus Service name for the input D-Bus path
192  *
193  *  @param[in] ctx - ipmi::Context::ptr
194  *  @param[in] intf - D-Bus Interface
195  *  @param[in] path - D-Bus Object Path
196  *  @param[out] service - requested service name
197  *  @return boost error code
198  *
199  */
200 boost::system::error_code getService(Context::ptr ctx, const std::string& intf,
201                                      const std::string& path,
202                                      std::string& service);
203 
204 /** @brief Gets the dbus sub tree implementing the given interface.
205  *  @param[in] ctx - ipmi::Context::ptr
206  *  @param[in] bus - DBUS Bus Object.
207  *  @param[in] interfaces - Dbus interface.
208  *  @param[in] subtreePath - subtree from where the search should start.
209  *  @param[in] depth - Search depth
210  *  @param[out] objectTree - map of object path and service info.
211  *  @return map of object path and service info.
212  */
213 boost::system::error_code getSubTree(
214     Context::ptr ctx, const InterfaceList& interface,
215     const std::string& subtreePath, int32_t depth, ObjectTree& objectTree);
216 
217 /** @brief Gets the D-Bus object info implementing the given interface
218  *         from the given subtree.
219  *  @param[in] ctx - ipmi::Context::ptr
220  *  @param[in] interface - D-Bus interface.
221  *  @param[in][optional] subtreePath - subtree from where the search starts.
222  *  @param[in][optional] match - identifier for object.
223  *  @param[out] D-Bus object with path and service name
224  *  @return - boost error code object
225  */
226 boost::system::error_code getDbusObject(
227     Context::ptr ctx, const std::string& interface,
228     const std::string& subtreePath, const std::string& match,
229     DbusObjectInfo& dbusObject);
230 
231 // default for ROOT for subtreePath and std::string{} for match
getDbusObject(Context::ptr ctx,const std::string & interface,DbusObjectInfo & dbusObject)232 static inline boost::system::error_code getDbusObject(
233     Context::ptr ctx, const std::string& interface, DbusObjectInfo& dbusObject)
234 {
235     return getDbusObject(ctx, interface, ROOT, {}, dbusObject);
236 }
237 
238 // default std::string{} for match
getDbusObject(Context::ptr ctx,const std::string & interface,const std::string & subtreePath,DbusObjectInfo & dbusObject)239 static inline boost::system::error_code getDbusObject(
240     Context::ptr ctx, const std::string& interface,
241     const std::string& subtreePath, DbusObjectInfo& dbusObject)
242 {
243     return getDbusObject(ctx, interface, subtreePath, {}, dbusObject);
244 }
245 
246 /** @brief Gets the value associated with the given object
247  *         and the interface.
248  *  @param[in] ctx - ipmi::Context::ptr
249  *  @param[in] service - D-Bus service name.
250  *  @param[in] objPath - D-Bus object path.
251  *  @param[in] interface - D-Bus interface.
252  *  @param[in] property - name of the property.
253  *  @param[out] propertyValue - value of the D-Bus property.
254  *  @return - boost error code object
255  */
256 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)257 boost::system::error_code getDbusProperty(
258     Context::ptr ctx, const std::string& service, const std::string& objPath,
259     const std::string& interface, const std::string& property,
260     Type& propertyValue)
261 {
262     boost::system::error_code ec;
263     auto variant = ctx->bus->yield_method_call<std::variant<Type>>(
264         ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, METHOD_GET,
265         interface, property);
266     if (!ec)
267     {
268         Type* tmp = std::get_if<Type>(&variant);
269         if (tmp)
270         {
271             propertyValue = *tmp;
272             return ec;
273         }
274         // user requested incorrect type; make an error code for them
275         ec = boost::system::errc::make_error_code(
276             boost::system::errc::invalid_argument);
277     }
278     return ec;
279 }
280 
281 /** @brief Gets all the properties associated with the given object
282  *         and the interface.
283  *  @param[in] ctx - ipmi::Context::ptr
284  *  @param[in] service - D-Bus service name.
285  *  @param[in] objPath - D-Bus object path.
286  *  @param[in] interface - D-Bus interface.
287  *  @param[out] properties - map of name value pair.
288  *  @return - boost error code object
289  */
290 boost::system::error_code getAllDbusProperties(
291     Context::ptr ctx, const std::string& service, const std::string& objPath,
292     const std::string& interface, PropertyMap& properties);
293 
294 /** @brief Sets the property value of the given object.
295  *  @param[in] ctx - ipmi::Context::ptr
296  *  @param[in] service - D-Bus service name.
297  *  @param[in] objPath - D-Bus object path.
298  *  @param[in] interface - D-Bus interface.
299  *  @param[in] property - name of the property.
300  *  @param[in] value - value which needs to be set.
301  *  @return - boost error code object
302  */
303 boost::system::error_code setDbusProperty(
304     Context::ptr ctx, const std::string& service, const std::string& objPath,
305     const std::string& interface, const std::string& property,
306     const Value& value);
307 
308 /** @brief  Gets all the D-Bus objects from the given service root
309  *          which matches the object identifier.
310  *  @param[in] ctx - ipmi::Context::ptr
311  *  @param[in] serviceRoot - Service root path.
312  *  @param[in] interface - D-Bus interface.
313  *  @param[in][optional] match - Identifier for a path.
314  *  @param[out] objectree - map of object path and service info.
315  *  @return - boost error code object
316  */
317 boost::system::error_code getAllDbusObjects(
318     Context::ptr ctx, const std::string& serviceRoot,
319     const std::string& interface, const std::string& match,
320     ObjectTree& objectTree);
321 
322 // default std::string{} for match
getAllDbusObjects(Context::ptr ctx,const std::string & serviceRoot,const std::string & interface,ObjectTree & objectTree)323 static inline boost::system::error_code getAllDbusObjects(
324     Context::ptr ctx, const std::string& serviceRoot,
325     const std::string& interface, ObjectTree& objectTree)
326 {
327     return getAllDbusObjects(ctx, serviceRoot, interface, {}, objectTree);
328 }
329 
330 /** @brief Gets all managed objects associated with the given object
331  *         path and service.
332  *  @param[in] ctx - ipmi::Context::ptr
333  *  @param[in] service - D-Bus service name.
334  *  @param[in] objPath - D-Bus object path.
335  *  @param[out] objects - map of name value pair.
336  *  @return - boost error code object
337  */
338 boost::system::error_code getManagedObjects(
339     Context::ptr ctx, const std::string& service, const std::string& objPath,
340     ObjectValueTree& objects);
341 
342 /** @brief Gets the value associated with the given object
343  *         and the interface.
344  *  @param[in] ctx - ipmi::Context::ptr
345  *  @param[in] service - D-Bus service name.
346  *  @param[in] objPath - D-Bus object path.
347  *  @param[in] interface - D-Bus interface.
348  *  @param[in] method - name of the method.
349  *  @return - boost error code object
350  */
351 
352 boost::system::error_code callDbusMethod(
353     Context::ptr ctx, const std::string& service, const std::string& objPath,
354     const std::string& interface, const std::string& method);
355 
356 /********* End co-routine yielding alternatives ***************/
357 
358 /** @brief Retrieve the value from map of variants,
359  *         returning a default if the key does not exist or the
360  *         type of the value does not match the expected type
361  *
362  *  @tparam T - type of expected value to return
363  *  @param[in] props - D-Bus propery map (Map of variants)
364  *  @param[in] name - key name of property to fetch
365  *  @param[in] defaultValue - default value to return on error
366  *  @return - value from propery map at name, or defaultValue
367  */
368 template <typename T>
mappedVariant(const ipmi::PropertyMap & props,const std::string & name,const T & defaultValue)369 T mappedVariant(const ipmi::PropertyMap& props, const std::string& name,
370                 const T& defaultValue)
371 {
372     auto item = props.find(name);
373     if (item == props.end())
374     {
375         return defaultValue;
376     }
377     const T* prop = std::get_if<T>(&item->second);
378     if (!prop)
379     {
380         return defaultValue;
381     }
382     return *prop;
383 }
384 
385 /** @struct VariantToDoubleVisitor
386  *  @brief Visitor to convert variants to doubles
387  *  @details Performs a static cast on the underlying type
388  */
389 struct VariantToDoubleVisitor
390 {
391     template <typename T>
operator ()ipmi::VariantToDoubleVisitor392     std::enable_if_t<std::is_arithmetic<T>::value, double> operator()(
393         const T& t) const
394     {
395         return static_cast<double>(t);
396     }
397 
398     template <typename T>
operator ()ipmi::VariantToDoubleVisitor399     std::enable_if_t<!std::is_arithmetic<T>::value, double> operator()(
400         const T&) const
401     {
402         throw std::invalid_argument("Cannot translate type to double");
403     }
404 };
405 
406 namespace method_no_args
407 {
408 
409 /** @brief Calls the Dbus method which waits for response.
410  *  @param[in] bus - DBUS Bus Object.
411  *  @param[in] service - Dbus service name.
412  *  @param[in] objPath - Dbus object path.
413  *  @param[in] interface - Dbus interface.
414  *  @param[in] method - Dbus method.
415  */
416 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service,
417                     const std::string& objPath, const std::string& interface,
418                     const std::string& method);
419 
420 } // namespace method_no_args
421 
422 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)423 boost::system::error_code callDbusMethod(
424     ipmi::Context::ptr ctx, const std::string& service,
425     const std::string& objPath, const std::string& interface,
426     const std::string& method, const InputArgs&... args)
427 {
428     boost::system::error_code ec;
429     ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface,
430                                 method, args...);
431 
432     return ec;
433 }
434 
435 template <typename RetType, typename... InputArgs>
436 RetType callDbusMethod(ipmi::Context::ptr ctx, boost::system::error_code& ec,
437                        const std::string& service, const std::string& objPath,
438                        const std::string& interface, const std::string& method,
439                        const InputArgs&... args)
440 {
441     auto rc = ctx->bus->yield_method_call<RetType>(
442         ctx->yield, ec, service, objPath, interface, method, args...);
443 
444     return rc;
445 }
446 
447 /** @brief Perform the low-level i2c bus write-read.
448  *  @param[in] i2cBus - i2c bus device node name, such as /dev/i2c-2.
449  *  @param[in] targetAddr - i2c device target address.
450  *  @param[in] writeData - The data written to i2c device.
451  *  @param[out] readBuf - Data read from the i2c device.
452  */
453 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t targetAddr,
454                       std::vector<uint8_t> writeData,
455                       std::vector<uint8_t>& readBuf);
456 
457 /** @brief Split a string into a vector of strings
458  *  @param[in] srcStr - The string to split
459  *  @param[in] delim - The delimiter to split the string on
460  *  @return A vector of strings
461  */
462 std::vector<std::string> split(const std::string& srcStr, char delim);
463 
464 } // namespace ipmi
465