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 204 /** @brief Gets the ancestor objects of the given object 205 which implements the given interface. 206 * @param[in] bus - Dbus bus object. 207 * @param[in] path - Child Dbus object path. 208 * @param[in] interfaces - Dbus interface list. 209 * @return map of object path and service info. 210 */ 211 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path, 212 InterfaceList&& interfaces); 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(Context::ptr ctx, 239 const InterfaceList& interface, 240 const std::string& subtreePath, 241 int32_t depth, ObjectTree& objectTree); 242 243 /** @brief Gets the D-Bus object info implementing the given interface 244 * from the given subtree. 245 * @param[in] ctx - ipmi::Context::ptr 246 * @param[in] interface - D-Bus interface. 247 * @param[in][optional] subtreePath - subtree from where the search starts. 248 * @param[in][optional] match - identifier for object. 249 * @param[out] D-Bus object with path and service name 250 * @return - boost error code object 251 */ 252 boost::system::error_code getDbusObject(Context::ptr ctx, 253 const std::string& interface, 254 const std::string& subtreePath, 255 const std::string& match, 256 DbusObjectInfo& dbusObject); 257 258 // default for ROOT for subtreePath and std::string{} for match 259 static inline boost::system::error_code 260 getDbusObject(Context::ptr ctx, const std::string& interface, 261 DbusObjectInfo& dbusObject) 262 { 263 return getDbusObject(ctx, interface, ROOT, {}, dbusObject); 264 } 265 266 // default std::string{} for match 267 static inline boost::system::error_code 268 getDbusObject(Context::ptr ctx, const std::string& interface, 269 const std::string& subtreePath, DbusObjectInfo& dbusObject) 270 { 271 return getDbusObject(ctx, interface, subtreePath, {}, dbusObject); 272 } 273 274 /** @brief Gets the value associated with the given object 275 * and the interface. 276 * @param[in] ctx - ipmi::Context::ptr 277 * @param[in] service - D-Bus service name. 278 * @param[in] objPath - D-Bus object path. 279 * @param[in] interface - D-Bus interface. 280 * @param[in] property - name of the property. 281 * @param[out] propertyValue - value of the D-Bus property. 282 * @return - boost error code object 283 */ 284 template <typename Type> 285 boost::system::error_code 286 getDbusProperty(Context::ptr ctx, const std::string& service, 287 const std::string& objPath, const std::string& interface, 288 const std::string& property, Type& propertyValue) 289 { 290 boost::system::error_code ec; 291 auto variant = ctx->bus->yield_method_call<std::variant<Type>>( 292 ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, METHOD_GET, 293 interface, property); 294 if (!ec) 295 { 296 Type* tmp = std::get_if<Type>(&variant); 297 if (tmp) 298 { 299 propertyValue = *tmp; 300 return ec; 301 } 302 // user requested incorrect type; make an error code for them 303 ec = boost::system::errc::make_error_code( 304 boost::system::errc::invalid_argument); 305 } 306 return ec; 307 } 308 309 /** @brief Gets all the properties associated with the given object 310 * and the interface. 311 * @param[in] ctx - ipmi::Context::ptr 312 * @param[in] service - D-Bus service name. 313 * @param[in] objPath - D-Bus object path. 314 * @param[in] interface - D-Bus interface. 315 * @param[out] properties - map of name value pair. 316 * @return - boost error code object 317 */ 318 boost::system::error_code getAllDbusProperties(Context::ptr ctx, 319 const std::string& service, 320 const std::string& objPath, 321 const std::string& interface, 322 PropertyMap& properties); 323 324 /** @brief Sets the property value of the given object. 325 * @param[in] ctx - ipmi::Context::ptr 326 * @param[in] service - D-Bus service name. 327 * @param[in] objPath - D-Bus object path. 328 * @param[in] interface - D-Bus interface. 329 * @param[in] property - name of the property. 330 * @param[in] value - value which needs to be set. 331 * @return - boost error code object 332 */ 333 boost::system::error_code 334 setDbusProperty(Context::ptr ctx, const std::string& service, 335 const std::string& objPath, const std::string& interface, 336 const std::string& property, const Value& value); 337 338 /** @brief Gets all the D-Bus objects from the given service root 339 * which matches the object identifier. 340 * @param[in] ctx - ipmi::Context::ptr 341 * @param[in] serviceRoot - Service root path. 342 * @param[in] interface - D-Bus interface. 343 * @param[in][optional] match - Identifier for a path. 344 * @param[out] objectree - map of object path and service info. 345 * @return - boost error code object 346 */ 347 boost::system::error_code getAllDbusObjects(Context::ptr ctx, 348 const std::string& serviceRoot, 349 const std::string& interface, 350 const std::string& match, 351 ObjectTree& objectTree); 352 353 // default std::string{} for match 354 static inline boost::system::error_code 355 getAllDbusObjects(Context::ptr ctx, const std::string& serviceRoot, 356 const std::string& interface, ObjectTree& objectTree) 357 { 358 return getAllDbusObjects(ctx, serviceRoot, interface, {}, objectTree); 359 } 360 361 /** @brief Deletes all the D-Bus objects from the given service root 362 which matches the object identifier. 363 * @param[in] ctx - ipmi::Context::ptr 364 * @param[out] ec - boost error code object 365 * @param[in] serviceRoot - Service root path. 366 * @param[in] interface - D-Bus interface. 367 * @param[in] match - Identifier for object. 368 */ 369 boost::system::error_code deleteAllDbusObjects(Context::ptr ctx, 370 const std::string& serviceRoot, 371 const std::string& interface, 372 const std::string& match = {}); 373 374 /** @brief Gets all managed objects associated with the given object 375 * path and service. 376 * @param[in] ctx - ipmi::Context::ptr 377 * @param[in] service - D-Bus service name. 378 * @param[in] objPath - D-Bus object path. 379 * @param[out] objects - map of name value pair. 380 * @return - boost error code object 381 */ 382 boost::system::error_code getManagedObjects(Context::ptr ctx, 383 const std::string& service, 384 const std::string& objPath, 385 ObjectValueTree& objects); 386 387 /** @brief Gets the ancestor objects of the given object 388 which implements the given interface. 389 * @param[in] ctx - ipmi::Context::ptr 390 * @param[in] path - Child D-Bus object path. 391 * @param[in] interfaces - D-Bus interface list. 392 * @param[out] ObjectTree - map of object path and service info. 393 * @return - boost error code object 394 */ 395 boost::system::error_code getAllAncestors(Context::ptr ctx, 396 const std::string& path, 397 const InterfaceList& interfaces, 398 ObjectTree& objectTree); 399 400 /** @brief Gets the value associated with the given object 401 * and the interface. 402 * @param[in] ctx - ipmi::Context::ptr 403 * @param[in] service - D-Bus service name. 404 * @param[in] objPath - D-Bus object path. 405 * @param[in] interface - D-Bus interface. 406 * @param[in] method - name of the method. 407 * @return - boost error code object 408 */ 409 410 boost::system::error_code callDbusMethod(Context::ptr ctx, 411 const std::string& service, 412 const std::string& objPath, 413 const std::string& interface, 414 const std::string& method); 415 416 /********* End co-routine yielding alternatives ***************/ 417 418 /** @brief Retrieve the value from map of variants, 419 * returning a default if the key does not exist or the 420 * type of the value does not match the expected type 421 * 422 * @tparam T - type of expected value to return 423 * @param[in] props - D-Bus propery map (Map of variants) 424 * @param[in] name - key name of property to fetch 425 * @param[in] defaultValue - default value to return on error 426 * @return - value from propery map at name, or defaultValue 427 */ 428 template <typename T> 429 T mappedVariant(const ipmi::PropertyMap& props, const std::string& name, 430 const T& defaultValue) 431 { 432 auto item = props.find(name); 433 if (item == props.end()) 434 { 435 return defaultValue; 436 } 437 const T* prop = std::get_if<T>(&item->second); 438 if (!prop) 439 { 440 return defaultValue; 441 } 442 return *prop; 443 } 444 445 /** @struct VariantToDoubleVisitor 446 * @brief Visitor to convert variants to doubles 447 * @details Performs a static cast on the underlying type 448 */ 449 struct VariantToDoubleVisitor 450 { 451 template <typename T> 452 std::enable_if_t<std::is_arithmetic<T>::value, double> 453 operator()(const T& t) const 454 { 455 return static_cast<double>(t); 456 } 457 458 template <typename T> 459 std::enable_if_t<!std::is_arithmetic<T>::value, double> 460 operator()(const T&) const 461 { 462 throw std::invalid_argument("Cannot translate type to double"); 463 } 464 }; 465 466 namespace method_no_args 467 { 468 469 /** @brief Calls the Dbus method which waits for response. 470 * @param[in] bus - DBUS Bus Object. 471 * @param[in] service - Dbus service name. 472 * @param[in] objPath - Dbus object path. 473 * @param[in] interface - Dbus interface. 474 * @param[in] method - Dbus method. 475 */ 476 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service, 477 const std::string& objPath, const std::string& interface, 478 const std::string& method); 479 480 } // namespace method_no_args 481 482 /** @brief Perform the low-level i2c bus write-read. 483 * @param[in] i2cBus - i2c bus device node name, such as /dev/i2c-2. 484 * @param[in] targetAddr - i2c device target address. 485 * @param[in] writeData - The data written to i2c device. 486 * @param[out] readBuf - Data read from the i2c device. 487 */ 488 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t targetAddr, 489 std::vector<uint8_t> writeData, 490 std::vector<uint8_t>& readBuf); 491 } // namespace ipmi 492