blob: 909fbc39e93d3ac696d011bb8307e64539fbe313 [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 }
Patrick Williamsd9ec33a2021-09-02 09:37:57 -0500140 catch (const sdbusplus::exception::exception&)
Matthew Barth9e80c872020-05-26 10:50:29 -0500141 {
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
Mike Capps7a401a22021-07-12 13:59:31 -0400201 /** @brief Get subtree from the mapper without checking response,
202 * (multiple interfaces version). */
203 static auto getSubTreeRaw(sdbusplus::bus::bus& bus, const std::string& path,
204 const std::vector<std::string>& intfs,
205 int32_t depth)
206 {
207 using namespace std::literals::string_literals;
208
209 using Path = std::string;
210 using Intf = std::string;
211 using Serv = std::string;
212 using Intfs = std::vector<Intf>;
213 using Objects = std::map<Path, std::map<Serv, Intfs>>;
214
215 return callMethodAndRead<Objects>(bus,
216 "xyz.openbmc_project.ObjectMapper"s,
217 "/xyz/openbmc_project/object_mapper"s,
218 "xyz.openbmc_project.ObjectMapper"s,
219 "GetSubTree"s, path, depth, intfs);
220 }
221
Matthew Barth9e80c872020-05-26 10:50:29 -0500222 /** @brief Get subtree from the mapper. */
223 static auto getSubTree(sdbusplus::bus::bus& bus, const std::string& path,
224 const std::string& interface, int32_t depth)
225 {
226 auto mapperResp = getSubTreeRaw(bus, path, interface, depth);
227 if (mapperResp.empty())
228 {
229 phosphor::logging::log<phosphor::logging::level::ERR>(
230 "Empty response from mapper GetSubTree",
231 phosphor::logging::entry("SUBTREE=%s", path.c_str()),
232 phosphor::logging::entry("INTERFACE=%s", interface.c_str()),
233 phosphor::logging::entry("DEPTH=%u", depth));
234 phosphor::logging::elog<detail::errors::InternalFailure>();
235 }
236 return mapperResp;
237 }
238
Matthew Barthfcbdc0e2020-10-28 14:11:07 -0500239 /** @brief Get subtree paths from the mapper without checking response. */
240 static auto getSubTreePathsRaw(sdbusplus::bus::bus& bus,
241 const std::string& path,
242 const std::string& interface, int32_t depth)
243 {
244 using namespace std::literals::string_literals;
245
246 using Path = std::string;
247 using Intf = std::string;
248 using Intfs = std::vector<Intf>;
249 using ObjectPaths = std::vector<Path>;
250 Intfs intfs = {interface};
251
252 return callMethodAndRead<ObjectPaths>(
253 bus, "xyz.openbmc_project.ObjectMapper"s,
254 "/xyz/openbmc_project/object_mapper"s,
255 "xyz.openbmc_project.ObjectMapper"s, "GetSubTreePaths"s, path,
256 depth, intfs);
257 }
258
259 /** @brief Get subtree paths from the mapper. */
260 static auto getSubTreePaths(sdbusplus::bus::bus& bus,
261 const std::string& path,
262 const std::string& interface, int32_t depth)
263 {
264 auto mapperResp = getSubTreePathsRaw(bus, path, interface, depth);
265 if (mapperResp.empty())
266 {
267 phosphor::logging::log<phosphor::logging::level::ERR>(
268 "Empty response from mapper GetSubTreePaths",
269 phosphor::logging::entry("SUBTREE=%s", path.c_str()),
270 phosphor::logging::entry("INTERFACE=%s", interface.c_str()),
271 phosphor::logging::entry("DEPTH=%u", depth));
272 phosphor::logging::elog<detail::errors::InternalFailure>();
273 }
274 return mapperResp;
275 }
276
Mike Cappsce6820a2021-05-26 10:40:19 -0400277 /** @brief Get service from the mapper without checking response. */
278 static auto getServiceRaw(sdbusplus::bus::bus& bus, const std::string& path,
279 const std::string& interface)
Matthew Barth9e80c872020-05-26 10:50:29 -0500280 {
281 using namespace std::literals::string_literals;
282 using GetObject = std::map<std::string, std::vector<std::string>>;
283
Mike Cappsce6820a2021-05-26 10:40:19 -0400284 return callMethodAndRead<GetObject>(
285 bus, "xyz.openbmc_project.ObjectMapper"s,
286 "/xyz/openbmc_project/object_mapper"s,
287 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
288 GetObject::mapped_type{interface});
289 }
290
291 /** @brief Get service from the mapper. */
292 static auto getService(sdbusplus::bus::bus& bus, const std::string& path,
293 const std::string& interface)
294 {
Matthew Barth9e80c872020-05-26 10:50:29 -0500295 try
296 {
Mike Cappsce6820a2021-05-26 10:40:19 -0400297 auto mapperResp = getServiceRaw(bus, path, interface);
Matthew Barth9e80c872020-05-26 10:50:29 -0500298
299 if (mapperResp.empty())
300 {
301 // Should never happen. A missing object would fail
302 // in callMethodAndRead()
303 phosphor::logging::log<phosphor::logging::level::ERR>(
304 "Empty mapper response on service lookup");
305 throw DBusServiceError{path, interface};
306 }
307 return mapperResp.begin()->first;
308 }
Patrick Williamsddb773b2021-10-06 11:24:49 -0500309 catch (const DBusMethodError& e)
Matthew Barth9e80c872020-05-26 10:50:29 -0500310 {
311 throw DBusServiceError{path, interface};
312 }
313 }
314
315 /** @brief Get service from the mapper. */
316 static auto getService(const std::string& path,
317 const std::string& interface)
318 {
319 return getService(getBus(), path, interface);
320 }
321
Matthew Bartha3553632021-05-03 14:40:59 -0500322 /** @brief Get managed objects. */
323 template <typename Variant>
324 static auto getManagedObjects(sdbusplus::bus::bus& bus,
325 const std::string& service,
326 const std::string& path)
327 {
328 using namespace std::literals::string_literals;
329
330 using Path = sdbusplus::message::object_path;
331 using Intf = std::string;
332 using Prop = std::string;
333 using GetManagedObjects =
334 std::map<Path, std::map<Intf, std::map<Prop, Variant>>>;
335
336 return callMethodAndRead<GetManagedObjects>(
337 bus, service, path, "org.freedesktop.DBus.ObjectManager"s,
338 "GetManagedObjects"s);
339 }
340
Matthew Barth9e80c872020-05-26 10:50:29 -0500341 /** @brief Get a property with mapper lookup. */
342 template <typename Property>
343 static auto getProperty(sdbusplus::bus::bus& bus, const std::string& path,
344 const std::string& interface,
345 const std::string& property)
346 {
347 using namespace std::literals::string_literals;
348
349 auto service = getService(bus, path, interface);
350 auto msg =
351 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
352 "Get"s, interface, property);
353 if (msg.is_method_error())
354 {
355 throw DBusPropertyError{"DBus get property failed", service, path,
356 interface, property};
357 }
358 std::variant<Property> value;
359 msg.read(value);
360 return std::get<Property>(value);
361 }
362
363 /** @brief Get a property with mapper lookup. */
364 template <typename Property>
365 static auto getProperty(const std::string& path,
366 const std::string& interface,
367 const std::string& property)
368 {
369 return getProperty<Property>(getBus(), path, interface, property);
370 }
371
372 /** @brief Get a property variant with mapper lookup. */
373 template <typename Variant>
374 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
375 const std::string& path,
376 const std::string& interface,
377 const std::string& property)
378 {
379 using namespace std::literals::string_literals;
380
381 auto service = getService(bus, path, interface);
382 auto msg =
383 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
384 "Get"s, interface, property);
385 if (msg.is_method_error())
386 {
387 throw DBusPropertyError{"DBus get property variant failed", service,
388 path, interface, property};
389 }
390 Variant value;
391 msg.read(value);
392 return value;
393 }
394
395 /** @brief Get a property variant with mapper lookup. */
396 template <typename Variant>
397 static auto getPropertyVariant(const std::string& path,
398 const std::string& interface,
399 const std::string& property)
400 {
401 return getPropertyVariant<Variant>(getBus(), path, interface, property);
402 }
403
404 /** @brief Get a property without mapper lookup. */
405 template <typename Property>
406 static auto getProperty(sdbusplus::bus::bus& bus,
407 const std::string& service, const std::string& path,
408 const std::string& interface,
409 const std::string& property)
410 {
411 using namespace std::literals::string_literals;
412
413 auto msg = callMethodAndReturn(bus, service, path,
414 "org.freedesktop.DBus.Properties"s,
415 "Get"s, interface, property);
416 if (msg.is_method_error())
417 {
418 throw DBusPropertyError{"DBus get property failed", service, path,
419 interface, property};
420 }
421 std::variant<Property> value;
422 msg.read(value);
423 return std::get<Property>(value);
424 }
425
426 /** @brief Get a property without mapper lookup. */
427 template <typename Property>
428 static auto getProperty(const std::string& service, const std::string& path,
429 const std::string& interface,
430 const std::string& property)
431 {
432 return getProperty<Property>(getBus(), service, path, interface,
433 property);
434 }
435
436 /** @brief Get a property variant without mapper lookup. */
437 template <typename Variant>
438 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
439 const std::string& service,
440 const std::string& path,
441 const std::string& interface,
442 const std::string& property)
443 {
444 using namespace std::literals::string_literals;
445
446 auto msg = callMethodAndReturn(bus, service, path,
447 "org.freedesktop.DBus.Properties"s,
448 "Get"s, interface, property);
449 if (msg.is_method_error())
450 {
451 throw DBusPropertyError{"DBus get property variant failed", service,
452 path, interface, property};
453 }
454 Variant value;
455 msg.read(value);
456 return value;
457 }
458
459 /** @brief Get a property variant without mapper lookup. */
460 template <typename Variant>
461 static auto getPropertyVariant(const std::string& service,
462 const std::string& path,
463 const std::string& interface,
464 const std::string& property)
465 {
466 return getPropertyVariant<Variant>(getBus(), service, path, interface,
467 property);
468 }
469
470 /** @brief Set a property with mapper lookup. */
471 template <typename Property>
472 static void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
473 const std::string& interface,
474 const std::string& property, Property&& value)
475 {
476 using namespace std::literals::string_literals;
477
478 std::variant<Property> varValue(std::forward<Property>(value));
479
480 auto service = getService(bus, path, interface);
481 auto msg = callMethodAndReturn(bus, service, path,
482 "org.freedesktop.DBus.Properties"s,
483 "Set"s, interface, property, varValue);
484 if (msg.is_method_error())
485 {
486 throw DBusPropertyError{"DBus set property failed", service, path,
487 interface, property};
488 }
489 }
490
491 /** @brief Set a property with mapper lookup. */
492 template <typename Property>
493 static void setProperty(const std::string& path,
494 const std::string& interface,
495 const std::string& property, Property&& value)
496 {
497 return setProperty(getBus(), path, interface, property,
498 std::forward<Property>(value));
499 }
500
501 /** @brief Set a property without mapper lookup. */
502 template <typename Property>
503 static void setProperty(sdbusplus::bus::bus& bus,
504 const std::string& service, const std::string& path,
505 const std::string& interface,
506 const std::string& property, Property&& value)
507 {
508 using namespace std::literals::string_literals;
509
510 std::variant<Property> varValue(std::forward<Property>(value));
511
512 auto msg = callMethodAndReturn(bus, service, path,
513 "org.freedesktop.DBus.Properties"s,
514 "Set"s, interface, property, varValue);
515 if (msg.is_method_error())
516 {
517 throw DBusPropertyError{"DBus set property failed", service, path,
518 interface, property};
519 }
520 }
521
522 /** @brief Set a property without mapper lookup. */
523 template <typename Property>
524 static void setProperty(const std::string& service, const std::string& path,
525 const std::string& interface,
526 const std::string& property, Property&& value)
527 {
528 return setProperty(getBus(), service, path, interface, property,
529 std::forward<Property>(value));
530 }
531
532 /** @brief Invoke method with mapper lookup. */
533 template <typename... Args>
534 static auto lookupAndCallMethod(sdbusplus::bus::bus& bus,
535 const std::string& path,
536 const std::string& interface,
537 const std::string& method, Args&&... args)
538 {
539 return callMethod(bus, getService(bus, path, interface), path,
540 interface, method, std::forward<Args>(args)...);
541 }
542
543 /** @brief Invoke method with mapper lookup. */
544 template <typename... Args>
545 static auto lookupAndCallMethod(const std::string& path,
546 const std::string& interface,
547 const std::string& method, Args&&... args)
548 {
549 return lookupAndCallMethod(getBus(), path, interface, method,
550 std::forward<Args>(args)...);
551 }
552
553 /** @brief Invoke method and read with mapper lookup. */
554 template <typename Ret, typename... Args>
555 static auto lookupCallMethodAndRead(sdbusplus::bus::bus& bus,
556 const std::string& path,
557 const std::string& interface,
558 const std::string& method,
559 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>
568 static auto lookupCallMethodAndRead(const std::string& path,
569 const std::string& interface,
570 const std::string& method,
571 Args&&... args)
572 {
573 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method,
574 std::forward<Args>(args)...);
575 }
576
577 /** @brief Invoke a method and return without checking for error. */
578 template <typename... Args>
579 static auto callMethodAndReturn(sdbusplus::bus::bus& bus,
580 const std::string& busName,
581 const std::string& path,
582 const std::string& interface,
583 const std::string& method, Args&&... args)
584 {
585 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
586 interface.c_str(), method.c_str());
587 reqMsg.append(std::forward<Args>(args)...);
588 auto respMsg = bus.call(reqMsg);
589
590 return respMsg;
591 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400592};
593
594} // namespace util
595} // namespace fan
596} // namespace phosphor