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