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