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 * @class DBusMethodError 42 * 43 * Thrown on a DBus Method call failure 44 */ 45 class DBusMethodError : public DBusError 46 { 47 public: 48 DBusMethodError(const std::string& busName, const std::string& path, 49 const std::string& interface, const std::string& method) : 50 DBusError(fmt::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: 70 DBusServiceError(const std::string& path, const std::string& interface) : 71 DBusError( 72 fmt::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: 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 + fmt::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. */ 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> 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> 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 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> 170 static auto callMethodAndRead(const std::string& busName, 171 const std::string& path, 172 const std::string& interface, 173 const std::string& method, Args&&... args) 174 { 175 return callMethodAndRead<Ret>(getBus(), busName, path, interface, 176 method, std::forward<Args>(args)...); 177 } 178 179 /** @brief Get subtree from the mapper without checking response. */ 180 static auto getSubTreeRaw(sdbusplus::bus_t& bus, const std::string& path, 181 const std::string& interface, int32_t depth) 182 { 183 using namespace std::literals::string_literals; 184 185 using Path = std::string; 186 using Intf = std::string; 187 using Serv = std::string; 188 using Intfs = std::vector<Intf>; 189 using Objects = std::map<Path, std::map<Serv, Intfs>>; 190 Intfs intfs = {interface}; 191 192 return callMethodAndRead<Objects>(bus, 193 "xyz.openbmc_project.ObjectMapper"s, 194 "/xyz/openbmc_project/object_mapper"s, 195 "xyz.openbmc_project.ObjectMapper"s, 196 "GetSubTree"s, path, depth, intfs); 197 } 198 199 /** @brief Get subtree from the mapper without checking response, 200 * (multiple interfaces version). */ 201 static auto getSubTreeRaw(sdbusplus::bus_t& bus, const std::string& path, 202 const std::vector<std::string>& intfs, 203 int32_t depth) 204 { 205 using namespace std::literals::string_literals; 206 207 using Path = std::string; 208 using Intf = std::string; 209 using Serv = std::string; 210 using Intfs = std::vector<Intf>; 211 using Objects = std::map<Path, std::map<Serv, Intfs>>; 212 213 return callMethodAndRead<Objects>(bus, 214 "xyz.openbmc_project.ObjectMapper"s, 215 "/xyz/openbmc_project/object_mapper"s, 216 "xyz.openbmc_project.ObjectMapper"s, 217 "GetSubTree"s, path, depth, intfs); 218 } 219 220 /** @brief Get subtree from the mapper. */ 221 static auto getSubTree(sdbusplus::bus_t& bus, const std::string& path, 222 const std::string& interface, int32_t depth) 223 { 224 auto mapperResp = getSubTreeRaw(bus, path, interface, depth); 225 if (mapperResp.empty()) 226 { 227 phosphor::logging::log<phosphor::logging::level::ERR>( 228 "Empty response from mapper GetSubTree", 229 phosphor::logging::entry("SUBTREE=%s", path.c_str()), 230 phosphor::logging::entry("INTERFACE=%s", interface.c_str()), 231 phosphor::logging::entry("DEPTH=%u", depth)); 232 phosphor::logging::elog<detail::errors::InternalFailure>(); 233 } 234 return mapperResp; 235 } 236 237 /** @brief Get subtree paths from the mapper without checking response. */ 238 static auto getSubTreePathsRaw(sdbusplus::bus_t& bus, 239 const std::string& path, 240 const std::string& interface, int32_t depth) 241 { 242 using namespace std::literals::string_literals; 243 244 using Path = std::string; 245 using Intf = std::string; 246 using Intfs = std::vector<Intf>; 247 using ObjectPaths = std::vector<Path>; 248 Intfs intfs = {interface}; 249 250 return callMethodAndRead<ObjectPaths>( 251 bus, "xyz.openbmc_project.ObjectMapper"s, 252 "/xyz/openbmc_project/object_mapper"s, 253 "xyz.openbmc_project.ObjectMapper"s, "GetSubTreePaths"s, path, 254 depth, intfs); 255 } 256 257 /** @brief Get subtree paths from the mapper. */ 258 static auto getSubTreePaths(sdbusplus::bus_t& bus, const std::string& path, 259 const std::string& interface, int32_t depth) 260 { 261 auto mapperResp = getSubTreePathsRaw(bus, path, interface, depth); 262 if (mapperResp.empty()) 263 { 264 phosphor::logging::log<phosphor::logging::level::ERR>( 265 "Empty response from mapper GetSubTreePaths", 266 phosphor::logging::entry("SUBTREE=%s", path.c_str()), 267 phosphor::logging::entry("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 without checking response. */ 275 static auto getServiceRaw(sdbusplus::bus_t& bus, const std::string& path, 276 const std::string& interface) 277 { 278 using namespace std::literals::string_literals; 279 using GetObject = std::map<std::string, std::vector<std::string>>; 280 281 return callMethodAndRead<GetObject>( 282 bus, "xyz.openbmc_project.ObjectMapper"s, 283 "/xyz/openbmc_project/object_mapper"s, 284 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path, 285 GetObject::mapped_type{interface}); 286 } 287 288 /** @brief Get service from the mapper. */ 289 static auto getService(sdbusplus::bus_t& bus, const std::string& path, 290 const std::string& interface) 291 { 292 try 293 { 294 auto mapperResp = getServiceRaw(bus, path, interface); 295 296 if (mapperResp.empty()) 297 { 298 // Should never happen. A missing object would fail 299 // in callMethodAndRead() 300 phosphor::logging::log<phosphor::logging::level::ERR>( 301 "Empty mapper response on service lookup"); 302 throw DBusServiceError{path, interface}; 303 } 304 return mapperResp.begin()->first; 305 } 306 catch (const DBusMethodError& e) 307 { 308 throw DBusServiceError{path, interface}; 309 } 310 } 311 312 /** @brief Get service from the mapper. */ 313 static auto getService(const std::string& path, 314 const std::string& interface) 315 { 316 return getService(getBus(), path, interface); 317 } 318 319 /** @brief Get managed objects. */ 320 template <typename Variant> 321 static auto getManagedObjects(sdbusplus::bus_t& bus, 322 const std::string& service, 323 const std::string& path) 324 { 325 using namespace std::literals::string_literals; 326 327 using Path = sdbusplus::message::object_path; 328 using Intf = std::string; 329 using Prop = std::string; 330 using GetManagedObjects = 331 std::map<Path, std::map<Intf, std::map<Prop, Variant>>>; 332 333 return callMethodAndRead<GetManagedObjects>( 334 bus, service, path, "org.freedesktop.DBus.ObjectManager"s, 335 "GetManagedObjects"s); 336 } 337 338 /** @brief Get a property with mapper lookup. */ 339 template <typename Property> 340 static auto getProperty(sdbusplus::bus_t& bus, const std::string& path, 341 const std::string& interface, 342 const std::string& property) 343 { 344 using namespace std::literals::string_literals; 345 346 auto service = getService(bus, path, interface); 347 auto msg = callMethod(bus, service, path, 348 "org.freedesktop.DBus.Properties"s, "Get"s, 349 interface, property); 350 if (msg.is_method_error()) 351 { 352 throw DBusPropertyError{"DBus get property failed", service, path, 353 interface, property}; 354 } 355 std::variant<Property> value; 356 msg.read(value); 357 return std::get<Property>(value); 358 } 359 360 /** @brief Get a property with mapper lookup. */ 361 template <typename Property> 362 static auto getProperty(const std::string& path, 363 const std::string& interface, 364 const std::string& property) 365 { 366 return getProperty<Property>(getBus(), path, interface, property); 367 } 368 369 /** @brief Get a property variant with mapper lookup. */ 370 template <typename Variant> 371 static auto getPropertyVariant(sdbusplus::bus_t& 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(bus, service, path, 380 "org.freedesktop.DBus.Properties"s, "Get"s, 381 interface, property); 382 if (msg.is_method_error()) 383 { 384 throw DBusPropertyError{"DBus get property variant failed", service, 385 path, interface, 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(const std::string& path, 395 const std::string& interface, 396 const std::string& property) 397 { 398 return getPropertyVariant<Variant>(getBus(), path, interface, property); 399 } 400 401 /** @brief Invoke a method and return without checking for error. */ 402 template <typename... Args> 403 static auto callMethodAndReturn(sdbusplus::bus_t& bus, 404 const std::string& busName, 405 const std::string& path, 406 const std::string& interface, 407 const std::string& method, Args&&... args) 408 { 409 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(), 410 interface.c_str(), method.c_str()); 411 reqMsg.append(std::forward<Args>(args)...); 412 auto respMsg = bus.call(reqMsg); 413 414 return respMsg; 415 } 416 417 /** @brief Get a property without mapper lookup. */ 418 template <typename Property> 419 static auto getProperty(sdbusplus::bus_t& bus, 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(bus, service, path, 427 "org.freedesktop.DBus.Properties"s, 428 "Get"s, interface, property); 429 if (msg.is_method_error()) 430 { 431 throw DBusPropertyError{"DBus get property failed", service, path, 432 interface, property}; 433 } 434 std::variant<Property> value; 435 msg.read(value); 436 return std::get<Property>(value); 437 } 438 439 /** @brief Get a property without mapper lookup. */ 440 template <typename Property> 441 static auto getProperty(const std::string& service, const std::string& path, 442 const std::string& interface, 443 const std::string& property) 444 { 445 return getProperty<Property>(getBus(), service, path, interface, 446 property); 447 } 448 449 /** @brief Get a property variant without mapper lookup. */ 450 template <typename Variant> 451 static auto getPropertyVariant(sdbusplus::bus_t& bus, 452 const std::string& service, 453 const std::string& path, 454 const std::string& interface, 455 const std::string& property) 456 { 457 using namespace std::literals::string_literals; 458 459 auto msg = callMethodAndReturn(bus, service, path, 460 "org.freedesktop.DBus.Properties"s, 461 "Get"s, interface, property); 462 if (msg.is_method_error()) 463 { 464 throw DBusPropertyError{"DBus get property variant failed", service, 465 path, interface, property}; 466 } 467 Variant value; 468 msg.read(value); 469 return value; 470 } 471 472 /** @brief Get a property variant without mapper lookup. */ 473 template <typename Variant> 474 static auto getPropertyVariant(const std::string& service, 475 const std::string& path, 476 const std::string& interface, 477 const std::string& property) 478 { 479 return getPropertyVariant<Variant>(getBus(), service, path, interface, 480 property); 481 } 482 483 /** @brief Set a property with mapper lookup. */ 484 template <typename Property> 485 static void setProperty(sdbusplus::bus_t& bus, const std::string& path, 486 const std::string& interface, 487 const std::string& property, Property&& value) 488 { 489 using namespace std::literals::string_literals; 490 491 std::variant<Property> varValue(std::forward<Property>(value)); 492 493 auto service = getService(bus, path, interface); 494 auto msg = callMethodAndReturn(bus, service, path, 495 "org.freedesktop.DBus.Properties"s, 496 "Set"s, interface, property, varValue); 497 if (msg.is_method_error()) 498 { 499 throw DBusPropertyError{"DBus set property failed", service, path, 500 interface, property}; 501 } 502 } 503 504 /** @brief Set a property with mapper lookup. */ 505 template <typename Property> 506 static void setProperty(const std::string& path, 507 const std::string& interface, 508 const std::string& property, Property&& value) 509 { 510 return setProperty(getBus(), path, interface, property, 511 std::forward<Property>(value)); 512 } 513 514 /** @brief Set a property without mapper lookup. */ 515 template <typename Property> 516 static void setProperty(sdbusplus::bus_t& bus, const std::string& service, 517 const std::string& path, 518 const std::string& interface, 519 const std::string& property, Property&& value) 520 { 521 using namespace std::literals::string_literals; 522 523 std::variant<Property> varValue(std::forward<Property>(value)); 524 525 auto msg = callMethodAndReturn(bus, service, path, 526 "org.freedesktop.DBus.Properties"s, 527 "Set"s, interface, property, varValue); 528 if (msg.is_method_error()) 529 { 530 throw DBusPropertyError{"DBus set property failed", service, path, 531 interface, property}; 532 } 533 } 534 535 /** @brief Set a property without mapper lookup. */ 536 template <typename Property> 537 static void setProperty(const std::string& service, const std::string& path, 538 const std::string& interface, 539 const std::string& property, Property&& value) 540 { 541 return setProperty(getBus(), service, path, interface, property, 542 std::forward<Property>(value)); 543 } 544 545 /** @brief Invoke method with mapper lookup. */ 546 template <typename... Args> 547 static auto lookupAndCallMethod(sdbusplus::bus_t& bus, 548 const std::string& path, 549 const std::string& interface, 550 const std::string& method, Args&&... args) 551 { 552 return callMethod(bus, getService(bus, path, interface), path, 553 interface, method, std::forward<Args>(args)...); 554 } 555 556 /** @brief Invoke method with mapper lookup. */ 557 template <typename... Args> 558 static auto lookupAndCallMethod(const std::string& path, 559 const std::string& interface, 560 const std::string& method, Args&&... args) 561 { 562 return lookupAndCallMethod(getBus(), path, interface, method, 563 std::forward<Args>(args)...); 564 } 565 566 /** @brief Invoke method and read with mapper lookup. */ 567 template <typename Ret, typename... Args> 568 static auto 569 lookupCallMethodAndRead(sdbusplus::bus_t& bus, const std::string& path, 570 const std::string& interface, 571 const std::string& method, Args&&... args) 572 { 573 return callMethodAndRead(bus, getService(bus, path, interface), path, 574 interface, method, 575 std::forward<Args>(args)...); 576 } 577 578 /** @brief Invoke method and read with mapper lookup. */ 579 template <typename Ret, typename... Args> 580 static auto lookupCallMethodAndRead(const std::string& path, 581 const std::string& interface, 582 const std::string& method, 583 Args&&... args) 584 { 585 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method, 586 std::forward<Args>(args)...); 587 } 588 }; 589 590 } // namespace util 591 } // namespace fan 592 } // namespace phosphor 593