1 #pragma once 2 3 #include <phosphor-logging/elog-errors.hpp> 4 #include <phosphor-logging/elog.hpp> 5 #include <phosphor-logging/log.hpp> 6 #include <sdbusplus/bus.hpp> 7 #include <sdbusplus/bus/match.hpp> 8 #include <sdbusplus/message.hpp> 9 #include <xyz/openbmc_project/Common/error.hpp> 10 11 #include <format> 12 13 namespace phosphor 14 { 15 namespace fan 16 { 17 namespace util 18 { 19 namespace detail 20 { 21 namespace errors = sdbusplus::xyz::openbmc_project::Common::Error; 22 } // namespace detail 23 24 /** 25 * @class DBusError 26 * 27 * The base class for the exceptions thrown on fails in the various 28 * SDBusPlus calls. Used so that a single catch statement can catch 29 * any type of these exceptions. 30 * 31 * None of these exceptions will log anything when they are created, 32 * it is up to the handler to do that if desired. 33 */ 34 class DBusError : public std::runtime_error 35 { 36 public: DBusError(const std::string & msg)37 explicit DBusError(const std::string& msg) : std::runtime_error(msg) {} 38 }; 39 40 /** 41 * @class DBusMethodError 42 * 43 * Thrown on a DBus Method call failure 44 */ 45 class DBusMethodError : public DBusError 46 { 47 public: DBusMethodError(const std::string & busName,const std::string & path,const std::string & interface,const std::string & method)48 DBusMethodError(const std::string& busName, const std::string& path, 49 const std::string& interface, const std::string& method) : 50 DBusError(std::format("DBus method failed: {} {} {} {}", busName, path, 51 interface, method)), 52 busName(busName), path(path), interface(interface), method(method) 53 {} 54 55 const std::string busName; 56 const std::string path; 57 const std::string interface; 58 const std::string method; 59 }; 60 61 /** 62 * @class DBusServiceError 63 * 64 * Thrown when a service lookup fails. Usually this points to 65 * the object path not being present in D-Bus. 66 */ 67 class DBusServiceError : public DBusError 68 { 69 public: DBusServiceError(const std::string & path,const std::string & interface)70 DBusServiceError(const std::string& path, const std::string& interface) : 71 DBusError( 72 std::format("DBus service lookup failed: {} {}", path, interface)), 73 path(path), interface(interface) 74 {} 75 76 const std::string path; 77 const std::string interface; 78 }; 79 80 /** 81 * @class DBusPropertyError 82 * 83 * Thrown when a set/get property fails. 84 */ 85 class DBusPropertyError : public DBusError 86 { 87 public: DBusPropertyError(const std::string & msg,const std::string & busName,const std::string & path,const std::string & interface,const std::string & property)88 DBusPropertyError(const std::string& msg, const std::string& busName, 89 const std::string& path, const std::string& interface, 90 const std::string& property) : 91 DBusError(msg + std::format(": {} {} {} {}", busName, path, interface, 92 property)), 93 busName(busName), path(path), interface(interface), property(property) 94 {} 95 96 const std::string busName; 97 const std::string path; 98 const std::string interface; 99 const std::string property; 100 }; 101 102 /** @brief Alias for PropertiesChanged signal callbacks. */ 103 template <typename... T> 104 using Properties = std::map<std::string, std::variant<T...>>; 105 106 /** @class SDBusPlus 107 * @brief DBus access delegate implementation for sdbusplus. 108 */ 109 class SDBusPlus 110 { 111 public: 112 /** @brief Get the bus connection. */ getBus()113 static auto& getBus() __attribute__((pure)) 114 { 115 static auto bus = sdbusplus::bus::new_default(); 116 return bus; 117 } 118 119 /** @brief Invoke a method. */ 120 template <typename... Args> callMethod(sdbusplus::bus_t & bus,const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)121 static auto callMethod(sdbusplus::bus_t& bus, const std::string& busName, 122 const std::string& path, 123 const std::string& interface, 124 const std::string& method, Args&&... args) 125 { 126 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(), 127 interface.c_str(), method.c_str()); 128 reqMsg.append(std::forward<Args>(args)...); 129 try 130 { 131 auto respMsg = bus.call(reqMsg); 132 if (respMsg.is_method_error()) 133 { 134 throw DBusMethodError{busName, path, interface, method}; 135 } 136 return respMsg; 137 } 138 catch (const sdbusplus::exception_t&) 139 { 140 throw DBusMethodError{busName, path, interface, method}; 141 } 142 } 143 144 /** @brief Invoke a method. */ 145 template <typename... Args> callMethod(const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)146 static auto callMethod(const std::string& busName, const std::string& path, 147 const std::string& interface, 148 const std::string& method, Args&&... args) 149 { 150 return callMethod(getBus(), busName, path, interface, method, 151 std::forward<Args>(args)...); 152 } 153 154 /** @brief Invoke a method and read the response. */ 155 template <typename Ret, typename... Args> 156 static auto callMethodAndRead(sdbusplus::bus_t & bus,const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)157 callMethodAndRead(sdbusplus::bus_t& bus, const std::string& busName, 158 const std::string& path, const std::string& interface, 159 const std::string& method, Args&&... args) 160 { 161 sdbusplus::message_t respMsg = callMethod<Args...>( 162 bus, busName, path, interface, method, std::forward<Args>(args)...); 163 Ret resp; 164 respMsg.read(resp); 165 return resp; 166 } 167 168 /** @brief Invoke a method and read the response. */ 169 template <typename Ret, typename... Args> callMethodAndRead(const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)170 static auto callMethodAndRead( 171 const std::string& busName, const std::string& path, 172 const std::string& interface, const std::string& method, Args&&... args) 173 { 174 return callMethodAndRead<Ret>(getBus(), busName, path, interface, 175 method, std::forward<Args>(args)...); 176 } 177 178 /** @brief Get subtree from the mapper without checking response. */ getSubTreeRaw(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,int32_t depth)179 static auto getSubTreeRaw(sdbusplus::bus_t& bus, const std::string& path, 180 const std::string& interface, int32_t depth) 181 { 182 using namespace std::literals::string_literals; 183 184 using Path = std::string; 185 using Intf = std::string; 186 using Serv = std::string; 187 using Intfs = std::vector<Intf>; 188 using Objects = std::map<Path, std::map<Serv, Intfs>>; 189 Intfs intfs = {interface}; 190 191 return callMethodAndRead<Objects>( 192 bus, "xyz.openbmc_project.ObjectMapper"s, 193 "/xyz/openbmc_project/object_mapper"s, 194 "xyz.openbmc_project.ObjectMapper"s, "GetSubTree"s, path, depth, 195 intfs); 196 } 197 198 /** @brief Get subtree from the mapper without checking response, 199 * (multiple interfaces version). */ getSubTreeRaw(sdbusplus::bus_t & bus,const std::string & path,const std::vector<std::string> & intfs,int32_t depth)200 static auto getSubTreeRaw(sdbusplus::bus_t& bus, const std::string& path, 201 const std::vector<std::string>& intfs, 202 int32_t depth) 203 { 204 using namespace std::literals::string_literals; 205 206 using Path = std::string; 207 using Intf = std::string; 208 using Serv = std::string; 209 using Intfs = std::vector<Intf>; 210 using Objects = std::map<Path, std::map<Serv, Intfs>>; 211 212 return callMethodAndRead<Objects>( 213 bus, "xyz.openbmc_project.ObjectMapper"s, 214 "/xyz/openbmc_project/object_mapper"s, 215 "xyz.openbmc_project.ObjectMapper"s, "GetSubTree"s, path, depth, 216 intfs); 217 } 218 219 /** @brief Get subtree from the mapper. */ getSubTree(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,int32_t depth)220 static auto getSubTree(sdbusplus::bus_t& bus, const std::string& path, 221 const std::string& interface, int32_t depth) 222 { 223 auto mapperResp = getSubTreeRaw(bus, path, interface, depth); 224 if (mapperResp.empty()) 225 { 226 phosphor::logging::log<phosphor::logging::level::ERR>( 227 "Empty response from mapper GetSubTree", 228 phosphor::logging::entry("SUBTREE=%s", path.c_str()), 229 phosphor::logging::entry("INTERFACE=%s", interface.c_str()), 230 phosphor::logging::entry("DEPTH=%u", depth)); 231 phosphor::logging::elog<detail::errors::InternalFailure>(); 232 } 233 return mapperResp; 234 } 235 236 /** @brief Get subtree paths from the mapper without checking response. */ getSubTreePathsRaw(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,int32_t depth)237 static auto getSubTreePathsRaw(sdbusplus::bus_t& bus, 238 const std::string& path, 239 const std::string& interface, int32_t depth) 240 { 241 using namespace std::literals::string_literals; 242 243 using Path = std::string; 244 using Intf = std::string; 245 using Intfs = std::vector<Intf>; 246 using ObjectPaths = std::vector<Path>; 247 Intfs intfs = {interface}; 248 249 return callMethodAndRead<ObjectPaths>( 250 bus, "xyz.openbmc_project.ObjectMapper"s, 251 "/xyz/openbmc_project/object_mapper"s, 252 "xyz.openbmc_project.ObjectMapper"s, "GetSubTreePaths"s, path, 253 depth, intfs); 254 } 255 256 /** @brief Get subtree paths from the mapper. */ getSubTreePaths(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,int32_t depth)257 static auto getSubTreePaths(sdbusplus::bus_t& bus, const std::string& path, 258 const std::string& interface, int32_t depth) 259 { 260 auto mapperResp = getSubTreePathsRaw(bus, path, interface, depth); 261 if (mapperResp.empty()) 262 { 263 phosphor::logging::log<phosphor::logging::level::ERR>( 264 "Empty response from mapper GetSubTreePaths", 265 phosphor::logging::entry("SUBTREE=%s", path.c_str()), 266 phosphor::logging::entry("INTERFACE=%s", interface.c_str()), 267 phosphor::logging::entry("DEPTH=%u", depth)); 268 phosphor::logging::elog<detail::errors::InternalFailure>(); 269 } 270 return mapperResp; 271 } 272 273 /** @brief Get service from the mapper without checking response. */ getServiceRaw(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface)274 static auto getServiceRaw(sdbusplus::bus_t& bus, const std::string& path, 275 const std::string& interface) 276 { 277 using namespace std::literals::string_literals; 278 using GetObject = std::map<std::string, std::vector<std::string>>; 279 280 return callMethodAndRead<GetObject>( 281 bus, "xyz.openbmc_project.ObjectMapper"s, 282 "/xyz/openbmc_project/object_mapper"s, 283 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path, 284 GetObject::mapped_type{interface}); 285 } 286 287 /** @brief Get service from the mapper. */ getService(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface)288 static auto getService(sdbusplus::bus_t& bus, const std::string& path, 289 const std::string& interface) 290 { 291 try 292 { 293 auto mapperResp = getServiceRaw(bus, path, interface); 294 295 if (mapperResp.empty()) 296 { 297 // Should never happen. A missing object would fail 298 // in callMethodAndRead() 299 phosphor::logging::log<phosphor::logging::level::ERR>( 300 "Empty mapper response on service lookup"); 301 throw DBusServiceError{path, interface}; 302 } 303 return mapperResp.begin()->first; 304 } 305 catch (const DBusMethodError& e) 306 { 307 throw DBusServiceError{path, interface}; 308 } 309 } 310 311 /** @brief Get service from the mapper. */ getService(const std::string & path,const std::string & interface)312 static auto getService(const std::string& path, 313 const std::string& interface) 314 { 315 return getService(getBus(), path, interface); 316 } 317 318 /** @brief Get managed objects. */ 319 template <typename Variant> getManagedObjects(sdbusplus::bus_t & bus,const std::string & service,const std::string & path)320 static auto getManagedObjects(sdbusplus::bus_t& bus, 321 const std::string& service, 322 const std::string& path) 323 { 324 using namespace std::literals::string_literals; 325 326 using Path = sdbusplus::message::object_path; 327 using Intf = std::string; 328 using Prop = std::string; 329 using GetManagedObjects = 330 std::map<Path, std::map<Intf, std::map<Prop, Variant>>>; 331 332 return callMethodAndRead<GetManagedObjects>( 333 bus, service, path, "org.freedesktop.DBus.ObjectManager"s, 334 "GetManagedObjects"s); 335 } 336 337 /** @brief Get a property with mapper lookup. */ 338 template <typename Property> getProperty(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & property)339 static auto getProperty(sdbusplus::bus_t& bus, const std::string& path, 340 const std::string& interface, 341 const std::string& property) 342 { 343 using namespace std::literals::string_literals; 344 345 auto service = getService(bus, path, interface); 346 auto msg = 347 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s, 348 "Get"s, interface, property); 349 if (msg.is_method_error()) 350 { 351 throw DBusPropertyError{"DBus get property failed", service, path, 352 interface, property}; 353 } 354 std::variant<Property> value; 355 msg.read(value); 356 return std::get<Property>(value); 357 } 358 359 /** @brief Get a property with mapper lookup. */ 360 template <typename Property> getProperty(const std::string & path,const std::string & interface,const std::string & property)361 static auto getProperty(const std::string& path, 362 const std::string& interface, 363 const std::string& property) 364 { 365 return getProperty<Property>(getBus(), path, interface, property); 366 } 367 368 /** @brief Get a property variant with mapper lookup. */ 369 template <typename Variant> getPropertyVariant(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & property)370 static auto getPropertyVariant( 371 sdbusplus::bus_t& bus, const std::string& path, 372 const std::string& interface, const std::string& property) 373 { 374 using namespace std::literals::string_literals; 375 376 auto service = getService(bus, path, interface); 377 auto msg = 378 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s, 379 "Get"s, interface, property); 380 if (msg.is_method_error()) 381 { 382 throw DBusPropertyError{"DBus get property variant failed", service, 383 path, interface, property}; 384 } 385 Variant value; 386 msg.read(value); 387 return value; 388 } 389 390 /** @brief Get a property variant with mapper lookup. */ 391 template <typename Variant> getPropertyVariant(const std::string & path,const std::string & interface,const std::string & property)392 static auto getPropertyVariant(const std::string& path, 393 const std::string& interface, 394 const std::string& property) 395 { 396 return getPropertyVariant<Variant>(getBus(), path, interface, property); 397 } 398 399 /** @brief Invoke a method and return without checking for error. */ 400 template <typename... Args> callMethodAndReturn(sdbusplus::bus_t & bus,const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)401 static auto callMethodAndReturn( 402 sdbusplus::bus_t& bus, const std::string& busName, 403 const std::string& path, const std::string& interface, 404 const std::string& method, Args&&... args) 405 { 406 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(), 407 interface.c_str(), method.c_str()); 408 reqMsg.append(std::forward<Args>(args)...); 409 auto respMsg = bus.call(reqMsg); 410 411 return respMsg; 412 } 413 414 /** @brief Get a property without mapper lookup. */ 415 template <typename Property> getProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & path,const std::string & interface,const std::string & property)416 static auto getProperty(sdbusplus::bus_t& bus, const std::string& service, 417 const std::string& path, 418 const std::string& interface, 419 const std::string& property) 420 { 421 using namespace std::literals::string_literals; 422 423 auto msg = callMethodAndReturn(bus, service, path, 424 "org.freedesktop.DBus.Properties"s, 425 "Get"s, interface, property); 426 if (msg.is_method_error()) 427 { 428 throw DBusPropertyError{"DBus get property failed", service, path, 429 interface, property}; 430 } 431 std::variant<Property> value; 432 msg.read(value); 433 return std::get<Property>(value); 434 } 435 436 /** @brief Get a property without mapper lookup. */ 437 template <typename Property> getProperty(const std::string & service,const std::string & path,const std::string & interface,const std::string & property)438 static auto getProperty(const std::string& service, const std::string& path, 439 const std::string& interface, 440 const std::string& property) 441 { 442 return getProperty<Property>(getBus(), service, path, interface, 443 property); 444 } 445 446 /** @brief Get a property variant without mapper lookup. */ 447 template <typename Variant> getPropertyVariant(sdbusplus::bus_t & bus,const std::string & service,const std::string & path,const std::string & interface,const std::string & property)448 static auto getPropertyVariant( 449 sdbusplus::bus_t& bus, const std::string& service, 450 const std::string& path, const std::string& interface, 451 const std::string& property) 452 { 453 using namespace std::literals::string_literals; 454 455 auto msg = callMethodAndReturn(bus, service, path, 456 "org.freedesktop.DBus.Properties"s, 457 "Get"s, interface, property); 458 if (msg.is_method_error()) 459 { 460 throw DBusPropertyError{"DBus get property variant failed", service, 461 path, interface, property}; 462 } 463 Variant value; 464 msg.read(value); 465 return value; 466 } 467 468 /** @brief Get a property variant without mapper lookup. */ 469 template <typename Variant> getPropertyVariant(const std::string & service,const std::string & path,const std::string & interface,const std::string & property)470 static auto getPropertyVariant( 471 const std::string& service, const std::string& path, 472 const std::string& interface, const std::string& property) 473 { 474 return getPropertyVariant<Variant>(getBus(), service, path, interface, 475 property); 476 } 477 478 /** @brief Set a property with mapper lookup. */ 479 template <typename Property> setProperty(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & property,Property && value)480 static void setProperty(sdbusplus::bus_t& bus, const std::string& path, 481 const std::string& interface, 482 const std::string& property, Property&& value) 483 { 484 using namespace std::literals::string_literals; 485 486 std::variant<Property> varValue(std::forward<Property>(value)); 487 488 auto service = getService(bus, path, interface); 489 auto msg = callMethodAndReturn(bus, service, path, 490 "org.freedesktop.DBus.Properties"s, 491 "Set"s, interface, property, varValue); 492 if (msg.is_method_error()) 493 { 494 throw DBusPropertyError{"DBus set property failed", service, path, 495 interface, property}; 496 } 497 } 498 499 /** @brief Set a property with mapper lookup. */ 500 template <typename Property> setProperty(const std::string & path,const std::string & interface,const std::string & property,Property && value)501 static void setProperty(const std::string& path, 502 const std::string& interface, 503 const std::string& property, Property&& value) 504 { 505 return setProperty(getBus(), path, interface, property, 506 std::forward<Property>(value)); 507 } 508 509 /** @brief Set a property without mapper lookup. */ 510 template <typename Property> setProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & path,const std::string & interface,const std::string & property,Property && value)511 static void setProperty(sdbusplus::bus_t& bus, const std::string& service, 512 const std::string& path, 513 const std::string& interface, 514 const std::string& property, Property&& value) 515 { 516 using namespace std::literals::string_literals; 517 518 std::variant<Property> varValue(std::forward<Property>(value)); 519 520 auto msg = callMethodAndReturn(bus, service, path, 521 "org.freedesktop.DBus.Properties"s, 522 "Set"s, interface, property, varValue); 523 if (msg.is_method_error()) 524 { 525 throw DBusPropertyError{"DBus set property failed", service, path, 526 interface, property}; 527 } 528 } 529 530 /** @brief Set a property without mapper lookup. */ 531 template <typename Property> setProperty(const std::string & service,const std::string & path,const std::string & interface,const std::string & property,Property && value)532 static void setProperty(const std::string& service, const std::string& path, 533 const std::string& interface, 534 const std::string& property, Property&& value) 535 { 536 return setProperty(getBus(), service, path, interface, property, 537 std::forward<Property>(value)); 538 } 539 540 /** @brief Invoke method with mapper lookup. */ 541 template <typename... Args> lookupAndCallMethod(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)542 static auto lookupAndCallMethod( 543 sdbusplus::bus_t& bus, const std::string& path, 544 const std::string& interface, const std::string& method, Args&&... args) 545 { 546 return callMethod(bus, getService(bus, path, interface), path, 547 interface, method, std::forward<Args>(args)...); 548 } 549 550 /** @brief Invoke method with mapper lookup. */ 551 template <typename... Args> lookupAndCallMethod(const std::string & path,const std::string & interface,const std::string & method,Args &&...args)552 static auto lookupAndCallMethod(const std::string& path, 553 const std::string& interface, 554 const std::string& method, Args&&... args) 555 { 556 return lookupAndCallMethod(getBus(), path, interface, method, 557 std::forward<Args>(args)...); 558 } 559 560 /** @brief Invoke method and read with mapper lookup. */ 561 template <typename Ret, typename... Args> lookupCallMethodAndRead(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)562 static auto lookupCallMethodAndRead( 563 sdbusplus::bus_t& bus, const std::string& path, 564 const std::string& interface, const std::string& method, Args&&... args) 565 { 566 return callMethodAndRead(bus, getService(bus, path, interface), path, 567 interface, method, 568 std::forward<Args>(args)...); 569 } 570 571 /** @brief Invoke method and read with mapper lookup. */ 572 template <typename Ret, typename... Args> lookupCallMethodAndRead(const std::string & path,const std::string & interface,const std::string & method,Args &&...args)573 static auto lookupCallMethodAndRead( 574 const std::string& path, const std::string& interface, 575 const std::string& method, Args&&... args) 576 { 577 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method, 578 std::forward<Args>(args)...); 579 } 580 }; 581 582 } // namespace util 583 } // namespace fan 584 } // namespace phosphor 585