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