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