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