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 service from the mapper. */ 214 static auto getService(sdbusplus::bus::bus& bus, const std::string& path, 215 const std::string& interface) 216 { 217 using namespace std::literals::string_literals; 218 using GetObject = std::map<std::string, std::vector<std::string>>; 219 220 try 221 { 222 auto mapperResp = callMethodAndRead<GetObject>( 223 bus, "xyz.openbmc_project.ObjectMapper"s, 224 "/xyz/openbmc_project/object_mapper"s, 225 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path, 226 GetObject::mapped_type{interface}); 227 228 if (mapperResp.empty()) 229 { 230 // Should never happen. A missing object would fail 231 // in callMethodAndRead() 232 phosphor::logging::log<phosphor::logging::level::ERR>( 233 "Empty mapper response on service lookup"); 234 throw DBusServiceError{path, interface}; 235 } 236 return mapperResp.begin()->first; 237 } 238 catch (DBusMethodError& e) 239 { 240 throw DBusServiceError{path, interface}; 241 } 242 } 243 244 /** @brief Get service from the mapper. */ 245 static auto getService(const std::string& path, 246 const std::string& interface) 247 { 248 return getService(getBus(), path, interface); 249 } 250 251 /** @brief Get a property with mapper lookup. */ 252 template <typename Property> 253 static auto getProperty(sdbusplus::bus::bus& bus, const std::string& path, 254 const std::string& interface, 255 const std::string& property) 256 { 257 using namespace std::literals::string_literals; 258 259 auto service = getService(bus, path, interface); 260 auto msg = 261 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s, 262 "Get"s, interface, property); 263 if (msg.is_method_error()) 264 { 265 throw DBusPropertyError{"DBus get property failed", service, path, 266 interface, property}; 267 } 268 std::variant<Property> value; 269 msg.read(value); 270 return std::get<Property>(value); 271 } 272 273 /** @brief Get a property with mapper lookup. */ 274 template <typename Property> 275 static auto getProperty(const std::string& path, 276 const std::string& interface, 277 const std::string& property) 278 { 279 return getProperty<Property>(getBus(), path, interface, property); 280 } 281 282 /** @brief Get a property variant with mapper lookup. */ 283 template <typename Variant> 284 static auto getPropertyVariant(sdbusplus::bus::bus& bus, 285 const std::string& path, 286 const std::string& interface, 287 const std::string& property) 288 { 289 using namespace std::literals::string_literals; 290 291 auto service = getService(bus, path, interface); 292 auto msg = 293 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s, 294 "Get"s, interface, property); 295 if (msg.is_method_error()) 296 { 297 throw DBusPropertyError{"DBus get property variant failed", service, 298 path, interface, property}; 299 } 300 Variant value; 301 msg.read(value); 302 return value; 303 } 304 305 /** @brief Get a property variant with mapper lookup. */ 306 template <typename Variant> 307 static auto getPropertyVariant(const std::string& path, 308 const std::string& interface, 309 const std::string& property) 310 { 311 return getPropertyVariant<Variant>(getBus(), path, interface, property); 312 } 313 314 /** @brief Get a property without mapper lookup. */ 315 template <typename Property> 316 static auto getProperty(sdbusplus::bus::bus& bus, 317 const std::string& service, const std::string& path, 318 const std::string& interface, 319 const std::string& property) 320 { 321 using namespace std::literals::string_literals; 322 323 auto msg = callMethodAndReturn(bus, service, path, 324 "org.freedesktop.DBus.Properties"s, 325 "Get"s, interface, property); 326 if (msg.is_method_error()) 327 { 328 throw DBusPropertyError{"DBus get property failed", service, path, 329 interface, property}; 330 } 331 std::variant<Property> value; 332 msg.read(value); 333 return std::get<Property>(value); 334 } 335 336 /** @brief Get a property without mapper lookup. */ 337 template <typename Property> 338 static auto getProperty(const std::string& service, const std::string& path, 339 const std::string& interface, 340 const std::string& property) 341 { 342 return getProperty<Property>(getBus(), service, path, interface, 343 property); 344 } 345 346 /** @brief Get a property variant without mapper lookup. */ 347 template <typename Variant> 348 static auto getPropertyVariant(sdbusplus::bus::bus& bus, 349 const std::string& service, 350 const std::string& path, 351 const std::string& interface, 352 const std::string& property) 353 { 354 using namespace std::literals::string_literals; 355 356 auto msg = callMethodAndReturn(bus, service, path, 357 "org.freedesktop.DBus.Properties"s, 358 "Get"s, interface, property); 359 if (msg.is_method_error()) 360 { 361 throw DBusPropertyError{"DBus get property variant failed", service, 362 path, interface, property}; 363 } 364 Variant value; 365 msg.read(value); 366 return value; 367 } 368 369 /** @brief Get a property variant without mapper lookup. */ 370 template <typename Variant> 371 static auto getPropertyVariant(const std::string& service, 372 const std::string& path, 373 const std::string& interface, 374 const std::string& property) 375 { 376 return getPropertyVariant<Variant>(getBus(), service, path, interface, 377 property); 378 } 379 380 /** @brief Set a property with mapper lookup. */ 381 template <typename Property> 382 static void setProperty(sdbusplus::bus::bus& bus, const std::string& path, 383 const std::string& interface, 384 const std::string& property, Property&& value) 385 { 386 using namespace std::literals::string_literals; 387 388 std::variant<Property> varValue(std::forward<Property>(value)); 389 390 auto service = getService(bus, path, interface); 391 auto msg = callMethodAndReturn(bus, service, path, 392 "org.freedesktop.DBus.Properties"s, 393 "Set"s, interface, property, varValue); 394 if (msg.is_method_error()) 395 { 396 throw DBusPropertyError{"DBus set property failed", service, path, 397 interface, property}; 398 } 399 } 400 401 /** @brief Set a property with mapper lookup. */ 402 template <typename Property> 403 static void setProperty(const std::string& path, 404 const std::string& interface, 405 const std::string& property, Property&& value) 406 { 407 return setProperty(getBus(), path, interface, property, 408 std::forward<Property>(value)); 409 } 410 411 /** @brief Set a property without mapper lookup. */ 412 template <typename Property> 413 static void setProperty(sdbusplus::bus::bus& bus, 414 const std::string& service, const std::string& path, 415 const std::string& interface, 416 const std::string& property, Property&& value) 417 { 418 using namespace std::literals::string_literals; 419 420 std::variant<Property> varValue(std::forward<Property>(value)); 421 422 auto msg = callMethodAndReturn(bus, service, path, 423 "org.freedesktop.DBus.Properties"s, 424 "Set"s, interface, property, varValue); 425 if (msg.is_method_error()) 426 { 427 throw DBusPropertyError{"DBus set property failed", service, path, 428 interface, property}; 429 } 430 } 431 432 /** @brief Set a property without mapper lookup. */ 433 template <typename Property> 434 static void setProperty(const std::string& service, const std::string& path, 435 const std::string& interface, 436 const std::string& property, Property&& value) 437 { 438 return setProperty(getBus(), service, path, interface, property, 439 std::forward<Property>(value)); 440 } 441 442 /** @brief Invoke method with mapper lookup. */ 443 template <typename... Args> 444 static auto lookupAndCallMethod(sdbusplus::bus::bus& bus, 445 const std::string& path, 446 const std::string& interface, 447 const std::string& method, Args&&... args) 448 { 449 return callMethod(bus, getService(bus, path, interface), path, 450 interface, method, std::forward<Args>(args)...); 451 } 452 453 /** @brief Invoke method with mapper lookup. */ 454 template <typename... Args> 455 static auto lookupAndCallMethod(const std::string& path, 456 const std::string& interface, 457 const std::string& method, Args&&... args) 458 { 459 return lookupAndCallMethod(getBus(), path, interface, method, 460 std::forward<Args>(args)...); 461 } 462 463 /** @brief Invoke method and read with mapper lookup. */ 464 template <typename Ret, typename... Args> 465 static auto lookupCallMethodAndRead(sdbusplus::bus::bus& bus, 466 const std::string& path, 467 const std::string& interface, 468 const std::string& method, 469 Args&&... args) 470 { 471 return callMethodAndRead(bus, getService(bus, path, interface), path, 472 interface, method, 473 std::forward<Args>(args)...); 474 } 475 476 /** @brief Invoke method and read with mapper lookup. */ 477 template <typename Ret, typename... Args> 478 static auto lookupCallMethodAndRead(const std::string& path, 479 const std::string& interface, 480 const std::string& method, 481 Args&&... args) 482 { 483 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method, 484 std::forward<Args>(args)...); 485 } 486 487 /** @brief Invoke a method and return without checking for error. */ 488 template <typename... Args> 489 static auto callMethodAndReturn(sdbusplus::bus::bus& bus, 490 const std::string& busName, 491 const std::string& path, 492 const std::string& interface, 493 const std::string& method, Args&&... args) 494 { 495 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(), 496 interface.c_str(), method.c_str()); 497 reqMsg.append(std::forward<Args>(args)...); 498 auto respMsg = bus.call(reqMsg); 499 500 return respMsg; 501 } 502 }; 503 504 } // namespace util 505 } // namespace fan 506 } // namespace phosphor 507