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