blob: 1f2839f79757007106e20c2ee56deedffba9fb73 [file] [log] [blame]
Brad Bishop6e9cfdb2017-06-08 11:59:58 -04001#pragma once
2
Matt Spinler23f87572021-01-20 14:20:30 -06003#include <fmt/format.h>
4
Brad Bishop6e9cfdb2017-06-08 11:59:58 -04005#include <phosphor-logging/elog-errors.hpp>
Matthew Barth9e80c872020-05-26 10:50:29 -05006#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>
Brad Bishop6e9cfdb2017-06-08 11:59:58 -040011#include <xyz/openbmc_project/Common/error.hpp>
12
13namespace phosphor
14{
15namespace fan
16{
17namespace util
18{
19namespace detail
20{
21namespace errors = sdbusplus::xyz::openbmc_project::Common::Error;
22} // namespace detail
23
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050024/**
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 */
34class DBusError : public std::runtime_error
35{
Matthew Barth9e80c872020-05-26 10:50:29 -050036 public:
Matt Spinler23f87572021-01-20 14:20:30 -060037 explicit DBusError(const std::string& msg) : std::runtime_error(msg)
Matthew Barth9e80c872020-05-26 10:50:29 -050038 {}
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050039};
40
41/**
42 * @class DBusMethodError
43 *
44 * Thrown on a DBus Method call failure
45 */
46class DBusMethodError : public DBusError
47{
Matthew Barth9e80c872020-05-26 10:50:29 -050048 public:
49 DBusMethodError(const std::string& busName, const std::string& path,
50 const std::string& interface, const std::string& method) :
Matt Spinler23f87572021-01-20 14:20:30 -060051 DBusError(fmt::format("DBus method failed: {} {} {} {}", busName, path,
52 interface, method)),
Matthew Barth9e80c872020-05-26 10:50:29 -050053 busName(busName), path(path), interface(interface), method(method)
54 {}
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050055
Matthew Barth9e80c872020-05-26 10:50:29 -050056 const std::string busName;
57 const std::string path;
58 const std::string interface;
59 const std::string method;
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050060};
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 */
68class DBusServiceError : public DBusError
69{
Matthew Barth9e80c872020-05-26 10:50:29 -050070 public:
71 DBusServiceError(const std::string& path, const std::string& interface) :
Matt Spinler23f87572021-01-20 14:20:30 -060072 DBusError(
73 fmt::format("DBus service lookup failed: {} {}", path, interface)),
74 path(path), interface(interface)
Matthew Barth9e80c872020-05-26 10:50:29 -050075 {}
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050076
Matthew Barth9e80c872020-05-26 10:50:29 -050077 const std::string path;
78 const std::string interface;
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050079};
80
Matthew Barth88923a02018-05-11 10:14:44 -050081/**
82 * @class DBusPropertyError
83 *
84 * Thrown when a set/get property fails.
85 */
86class DBusPropertyError : public DBusError
87{
Matthew Barth9e80c872020-05-26 10:50:29 -050088 public:
Matt Spinler23f87572021-01-20 14:20:30 -060089 DBusPropertyError(const std::string& msg, const std::string& busName,
Matthew Barth9e80c872020-05-26 10:50:29 -050090 const std::string& path, const std::string& interface,
91 const std::string& property) :
Matt Spinler23f87572021-01-20 14:20:30 -060092 DBusError(msg + fmt::format(": {} {} {} {}", busName, path, interface,
93 property)),
Matthew Barth9e80c872020-05-26 10:50:29 -050094 busName(busName), path(path), interface(interface), property(property)
95 {}
Matthew Barth88923a02018-05-11 10:14:44 -050096
Matthew Barth9e80c872020-05-26 10:50:29 -050097 const std::string busName;
98 const std::string path;
99 const std::string interface;
100 const std::string property;
Matthew Barth88923a02018-05-11 10:14:44 -0500101};
102
Brad Bishop1e4922a2017-07-12 22:56:49 -0400103/** @brief Alias for PropertiesChanged signal callbacks. */
Matthew Barth9e80c872020-05-26 10:50:29 -0500104template <typename... T>
Patrick Williamsc21d0b32020-05-13 17:55:14 -0500105using Properties = std::map<std::string, std::variant<T...>>;
Brad Bishop1e4922a2017-07-12 22:56:49 -0400106
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400107/** @class SDBusPlus
108 * @brief DBus access delegate implementation for sdbusplus.
109 */
110class SDBusPlus
111{
112
Matthew Barth9e80c872020-05-26 10:50:29 -0500113 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 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400120
Matthew Barth9e80c872020-05-26 10:50:29 -0500121 /** @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
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400132 {
Matthew Barth9e80c872020-05-26 10:50:29 -0500133 auto respMsg = bus.call(reqMsg);
134 if (respMsg.is_method_error())
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400135 {
Matt Spinlerba7b5fe2018-04-25 15:26:10 -0500136 throw DBusMethodError{busName, path, interface, method};
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400137 }
Matthew Barth7c48d102018-05-09 14:52:36 -0500138 return respMsg;
139 }
Matthew Barth9e80c872020-05-26 10:50:29 -0500140 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
Matthew Barthfcbdc0e2020-10-28 14:11:07 -0500218 /** @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
Matthew Barth9e80c872020-05-26 10:50:29 -0500256 /** @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
Matthew Bartha3553632021-05-03 14:40:59 -0500294 /** @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
Matthew Barth9e80c872020-05-26 10:50:29 -0500313 /** @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 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400564};
565
566} // namespace util
567} // namespace fan
568} // namespace phosphor