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