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