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