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