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