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