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