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, sdbusplus::message::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. */ 227 static auto getSubTree( 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 auto mapperResp = 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 if (mapperResp.empty()) 253 { 254 phosphor::logging::log<phosphor::logging::level::ERR>( 255 "Empty response from mapper GetSubTree", 256 phosphor::logging::entry("SUBTREE=%s", path.c_str()), 257 phosphor::logging::entry( 258 "INTERFACE=%s", interface.c_str()), 259 phosphor::logging::entry("DEPTH=%u", depth)); 260 phosphor::logging::elog<detail::errors::InternalFailure>(); 261 } 262 return mapperResp; 263 } 264 265 /** @brief Get service from the mapper. */ 266 static auto getService( 267 sdbusplus::bus::bus& bus, 268 const std::string& path, 269 const std::string& interface) 270 { 271 using namespace std::literals::string_literals; 272 using GetObject = std::map<std::string, std::vector<std::string>>; 273 274 try 275 { 276 auto mapperResp = callMethodAndRead<GetObject>( 277 bus, 278 "xyz.openbmc_project.ObjectMapper"s, 279 "/xyz/openbmc_project/object_mapper"s, 280 "xyz.openbmc_project.ObjectMapper"s, 281 "GetObject"s, 282 path, 283 GetObject::mapped_type{interface}); 284 285 if (mapperResp.empty()) 286 { 287 //Should never happen. A missing object would fail 288 //in callMethodAndRead() 289 phosphor::logging::log<phosphor::logging::level::ERR>( 290 "Empty mapper response on service lookup"); 291 throw DBusServiceError{path, interface}; 292 } 293 return mapperResp.begin()->first; 294 } 295 catch (DBusMethodError& e) 296 { 297 throw DBusServiceError{path, interface}; 298 } 299 } 300 301 /** @brief Get service from the mapper. */ 302 static auto getService( 303 const std::string& path, 304 const std::string& interface) 305 { 306 return getService( 307 getBus(), 308 path, 309 interface); 310 } 311 312 /** @brief Get a property with mapper lookup. */ 313 template <typename Property> 314 static auto getProperty( 315 sdbusplus::bus::bus& bus, 316 const std::string& path, 317 const std::string& interface, 318 const std::string& property) 319 { 320 using namespace std::literals::string_literals; 321 322 auto service = getService(bus, path, interface); 323 auto msg = callMethod( 324 bus, 325 service, 326 path, 327 "org.freedesktop.DBus.Properties"s, 328 "Get"s, 329 interface, 330 property); 331 if (msg.is_method_error()) 332 { 333 throw DBusPropertyError{ 334 "DBus get property failed", 335 service, 336 path, 337 interface, 338 property}; 339 } 340 sdbusplus::message::variant<Property> value; 341 msg.read(value); 342 return value.template get<Property>(); 343 } 344 345 /** @brief Get a property with mapper lookup. */ 346 template <typename Property> 347 static auto getProperty( 348 const std::string& path, 349 const std::string& interface, 350 const std::string& property) 351 { 352 return getProperty<Property>( 353 getBus(), 354 path, 355 interface, 356 property); 357 } 358 359 /** @brief Get a property variant with mapper lookup. */ 360 template <typename Variant> 361 static auto getPropertyVariant( 362 sdbusplus::bus::bus& bus, 363 const std::string& path, 364 const std::string& interface, 365 const std::string& property) 366 { 367 using namespace std::literals::string_literals; 368 369 auto service = getService(bus, path, interface); 370 auto msg = callMethod( 371 bus, 372 service, 373 path, 374 "org.freedesktop.DBus.Properties"s, 375 "Get"s, 376 interface, 377 property); 378 if (msg.is_method_error()) 379 { 380 throw DBusPropertyError{ 381 "DBus get property variant failed", 382 service, 383 path, 384 interface, 385 property}; 386 } 387 Variant value; 388 msg.read(value); 389 return value; 390 } 391 392 /** @brief Get a property variant with mapper lookup. */ 393 template <typename Variant> 394 static auto getPropertyVariant( 395 const std::string& path, 396 const std::string& interface, 397 const std::string& property) 398 { 399 return getPropertyVariant<Variant>( 400 getBus(), 401 path, 402 interface, 403 property); 404 } 405 406 /** @brief Get a property without mapper lookup. */ 407 template <typename Property> 408 static auto getProperty( 409 sdbusplus::bus::bus& bus, 410 const std::string& service, 411 const std::string& path, 412 const std::string& interface, 413 const std::string& property) 414 { 415 using namespace std::literals::string_literals; 416 417 auto msg = callMethodAndReturn( 418 bus, 419 service, 420 path, 421 "org.freedesktop.DBus.Properties"s, 422 "Get"s, 423 interface, 424 property); 425 if (msg.is_method_error()) 426 { 427 throw DBusPropertyError{ 428 "DBus get property failed", 429 service, 430 path, 431 interface, 432 property}; 433 } 434 sdbusplus::message::variant<Property> value; 435 msg.read(value); 436 return value.template get<Property>(); 437 } 438 439 /** @brief Get a property without mapper lookup. */ 440 template <typename Property> 441 static auto getProperty( 442 const std::string& service, 443 const std::string& path, 444 const std::string& interface, 445 const std::string& property) 446 { 447 return getProperty<Property>( 448 getBus(), 449 service, 450 path, 451 interface, 452 property); 453 } 454 455 /** @brief Get a property variant without mapper lookup. */ 456 template <typename Variant> 457 static auto getPropertyVariant( 458 sdbusplus::bus::bus& bus, 459 const std::string& service, 460 const std::string& path, 461 const std::string& interface, 462 const std::string& property) 463 { 464 using namespace std::literals::string_literals; 465 466 auto msg = callMethodAndReturn( 467 bus, 468 service, 469 path, 470 "org.freedesktop.DBus.Properties"s, 471 "Get"s, 472 interface, 473 property); 474 if (msg.is_method_error()) 475 { 476 throw DBusPropertyError{ 477 "DBus get property variant failed", 478 service, 479 path, 480 interface, 481 property}; 482 } 483 Variant value; 484 msg.read(value); 485 return value; 486 } 487 488 /** @brief Get a property variant without mapper lookup. */ 489 template <typename Variant> 490 static auto getPropertyVariant( 491 const std::string& service, 492 const std::string& path, 493 const std::string& interface, 494 const std::string& property) 495 { 496 return getPropertyVariant<Variant>( 497 getBus(), 498 service, 499 path, 500 interface, 501 property); 502 } 503 504 /** @brief Set a property with mapper lookup. */ 505 template <typename Property> 506 static void setProperty( 507 sdbusplus::bus::bus& bus, 508 const std::string& path, 509 const std::string& interface, 510 const std::string& property, 511 Property&& value) 512 { 513 using namespace std::literals::string_literals; 514 515 sdbusplus::message::variant<Property> varValue( 516 std::forward<Property>(value)); 517 518 auto service = getService(bus, path, interface); 519 auto msg = callMethodAndReturn( 520 bus, 521 service, 522 path, 523 "org.freedesktop.DBus.Properties"s, 524 "Set"s, 525 interface, 526 property, 527 varValue); 528 if (msg.is_method_error()) 529 { 530 throw DBusPropertyError{ 531 "DBus set property failed", 532 service, 533 path, 534 interface, 535 property}; 536 } 537 } 538 539 /** @brief Set a property with mapper lookup. */ 540 template <typename Property> 541 static void setProperty( 542 const std::string& path, 543 const std::string& interface, 544 const std::string& property, 545 Property&& value) 546 { 547 return setProperty( 548 getBus(), 549 path, 550 interface, 551 property, 552 std::forward<Property>(value)); 553 } 554 555 /** @brief Set a property without mapper lookup. */ 556 template <typename Property> 557 static void setProperty( 558 sdbusplus::bus::bus& bus, 559 const std::string& service, 560 const std::string& path, 561 const std::string& interface, 562 const std::string& property, 563 Property&& value) 564 { 565 using namespace std::literals::string_literals; 566 567 sdbusplus::message::variant<Property> varValue( 568 std::forward<Property>(value)); 569 570 auto msg = callMethodAndReturn( 571 bus, 572 service, 573 path, 574 "org.freedesktop.DBus.Properties"s, 575 "Set"s, 576 interface, 577 property, 578 varValue); 579 if (msg.is_method_error()) 580 { 581 throw DBusPropertyError{ 582 "DBus set property failed", 583 service, 584 path, 585 interface, 586 property}; 587 } 588 } 589 590 /** @brief Set a property without mapper lookup. */ 591 template <typename Property> 592 static void setProperty( 593 const std::string& service, 594 const std::string& path, 595 const std::string& interface, 596 const std::string& property, 597 Property&& value) 598 { 599 return setProperty( 600 getBus(), 601 service, 602 path, 603 interface, 604 property, 605 std::forward<Property>(value)); 606 } 607 608 /** @brief Invoke method with mapper lookup. */ 609 template <typename ...Args> 610 static auto lookupAndCallMethod( 611 sdbusplus::bus::bus& bus, 612 const std::string& path, 613 const std::string& interface, 614 const std::string& method, 615 Args&& ... args) 616 { 617 return callMethod( 618 bus, 619 getService(bus, path, interface), 620 path, 621 interface, 622 method, 623 std::forward<Args>(args)...); 624 } 625 626 /** @brief Invoke method with mapper lookup. */ 627 template <typename ...Args> 628 static auto lookupAndCallMethod( 629 const std::string& path, 630 const std::string& interface, 631 const std::string& method, 632 Args&& ... args) 633 { 634 return lookupAndCallMethod( 635 getBus(), 636 path, 637 interface, 638 method, 639 std::forward<Args>(args)...); 640 } 641 642 /** @brief Invoke method and read with mapper lookup. */ 643 template <typename Ret, typename ...Args> 644 static auto lookupCallMethodAndRead( 645 sdbusplus::bus::bus& bus, 646 const std::string& path, 647 const std::string& interface, 648 const std::string& method, 649 Args&& ... args) 650 { 651 return callMethodAndRead( 652 bus, 653 getService(bus, path, interface), 654 path, 655 interface, 656 method, 657 std::forward<Args>(args)...); 658 } 659 660 /** @brief Invoke method and read with mapper lookup. */ 661 template <typename Ret, typename ...Args> 662 static auto lookupCallMethodAndRead( 663 const std::string& path, 664 const std::string& interface, 665 const std::string& method, 666 Args&& ... args) 667 { 668 return lookupCallMethodAndRead<Ret>( 669 getBus(), 670 path, 671 interface, 672 method, 673 std::forward<Args>(args)...); 674 } 675 676 /** @brief Invoke a method and return without checking for error. */ 677 template <typename ...Args> 678 static auto callMethodAndReturn( 679 sdbusplus::bus::bus& bus, 680 const std::string& busName, 681 const std::string& path, 682 const std::string& interface, 683 const std::string& method, 684 Args&& ... args) 685 { 686 auto reqMsg = bus.new_method_call( 687 busName.c_str(), 688 path.c_str(), 689 interface.c_str(), 690 method.c_str()); 691 reqMsg.append(std::forward<Args>(args)...); 692 auto respMsg = bus.call(reqMsg); 693 694 return respMsg; 695 } 696 }; 697 698 } // namespace util 699 } // namespace fan 700 } // namespace phosphor 701