blob: 73af8b0e2fa2740070ee798ed0d3753211bc8b72 [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
Mike Cappsce6820a2021-05-26 10:40:19 -0400256 /** @brief Get service from the mapper without checking response. */
257 static auto getServiceRaw(sdbusplus::bus::bus& bus, const std::string& path,
258 const std::string& interface)
Matthew Barth9e80c872020-05-26 10:50:29 -0500259 {
260 using namespace std::literals::string_literals;
261 using GetObject = std::map<std::string, std::vector<std::string>>;
262
Mike Cappsce6820a2021-05-26 10:40:19 -0400263 return callMethodAndRead<GetObject>(
264 bus, "xyz.openbmc_project.ObjectMapper"s,
265 "/xyz/openbmc_project/object_mapper"s,
266 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
267 GetObject::mapped_type{interface});
268 }
269
270 /** @brief Get service from the mapper. */
271 static auto getService(sdbusplus::bus::bus& bus, const std::string& path,
272 const std::string& interface)
273 {
Matthew Barth9e80c872020-05-26 10:50:29 -0500274 try
275 {
Mike Cappsce6820a2021-05-26 10:40:19 -0400276 auto mapperResp = getServiceRaw(bus, path, interface);
Matthew Barth9e80c872020-05-26 10:50:29 -0500277
278 if (mapperResp.empty())
279 {
280 // Should never happen. A missing object would fail
281 // in callMethodAndRead()
282 phosphor::logging::log<phosphor::logging::level::ERR>(
283 "Empty mapper response on service lookup");
284 throw DBusServiceError{path, interface};
285 }
286 return mapperResp.begin()->first;
287 }
288 catch (DBusMethodError& e)
289 {
290 throw DBusServiceError{path, interface};
291 }
292 }
293
294 /** @brief Get service from the mapper. */
295 static auto getService(const std::string& path,
296 const std::string& interface)
297 {
298 return getService(getBus(), path, interface);
299 }
300
Matthew Bartha3553632021-05-03 14:40:59 -0500301 /** @brief Get managed objects. */
302 template <typename Variant>
303 static auto getManagedObjects(sdbusplus::bus::bus& bus,
304 const std::string& service,
305 const std::string& path)
306 {
307 using namespace std::literals::string_literals;
308
309 using Path = sdbusplus::message::object_path;
310 using Intf = std::string;
311 using Prop = std::string;
312 using GetManagedObjects =
313 std::map<Path, std::map<Intf, std::map<Prop, Variant>>>;
314
315 return callMethodAndRead<GetManagedObjects>(
316 bus, service, path, "org.freedesktop.DBus.ObjectManager"s,
317 "GetManagedObjects"s);
318 }
319
Matthew Barth9e80c872020-05-26 10:50:29 -0500320 /** @brief Get a property with mapper lookup. */
321 template <typename Property>
322 static auto getProperty(sdbusplus::bus::bus& bus, const std::string& path,
323 const std::string& interface,
324 const std::string& property)
325 {
326 using namespace std::literals::string_literals;
327
328 auto service = getService(bus, path, interface);
329 auto msg =
330 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
331 "Get"s, interface, property);
332 if (msg.is_method_error())
333 {
334 throw DBusPropertyError{"DBus get property failed", service, path,
335 interface, property};
336 }
337 std::variant<Property> value;
338 msg.read(value);
339 return std::get<Property>(value);
340 }
341
342 /** @brief Get a property with mapper lookup. */
343 template <typename Property>
344 static auto getProperty(const std::string& path,
345 const std::string& interface,
346 const std::string& property)
347 {
348 return getProperty<Property>(getBus(), path, interface, property);
349 }
350
351 /** @brief Get a property variant with mapper lookup. */
352 template <typename Variant>
353 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
354 const std::string& path,
355 const std::string& interface,
356 const std::string& property)
357 {
358 using namespace std::literals::string_literals;
359
360 auto service = getService(bus, path, interface);
361 auto msg =
362 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
363 "Get"s, interface, property);
364 if (msg.is_method_error())
365 {
366 throw DBusPropertyError{"DBus get property variant failed", service,
367 path, interface, property};
368 }
369 Variant value;
370 msg.read(value);
371 return value;
372 }
373
374 /** @brief Get a property variant with mapper lookup. */
375 template <typename Variant>
376 static auto getPropertyVariant(const std::string& path,
377 const std::string& interface,
378 const std::string& property)
379 {
380 return getPropertyVariant<Variant>(getBus(), path, interface, property);
381 }
382
383 /** @brief Get a property without mapper lookup. */
384 template <typename Property>
385 static auto getProperty(sdbusplus::bus::bus& bus,
386 const std::string& service, const std::string& path,
387 const std::string& interface,
388 const std::string& property)
389 {
390 using namespace std::literals::string_literals;
391
392 auto msg = callMethodAndReturn(bus, service, path,
393 "org.freedesktop.DBus.Properties"s,
394 "Get"s, interface, property);
395 if (msg.is_method_error())
396 {
397 throw DBusPropertyError{"DBus get property failed", service, path,
398 interface, property};
399 }
400 std::variant<Property> value;
401 msg.read(value);
402 return std::get<Property>(value);
403 }
404
405 /** @brief Get a property without mapper lookup. */
406 template <typename Property>
407 static auto getProperty(const std::string& service, const std::string& path,
408 const std::string& interface,
409 const std::string& property)
410 {
411 return getProperty<Property>(getBus(), service, path, interface,
412 property);
413 }
414
415 /** @brief Get a property variant without mapper lookup. */
416 template <typename Variant>
417 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
418 const std::string& service,
419 const std::string& path,
420 const std::string& interface,
421 const std::string& property)
422 {
423 using namespace std::literals::string_literals;
424
425 auto msg = callMethodAndReturn(bus, service, path,
426 "org.freedesktop.DBus.Properties"s,
427 "Get"s, interface, property);
428 if (msg.is_method_error())
429 {
430 throw DBusPropertyError{"DBus get property variant failed", service,
431 path, interface, property};
432 }
433 Variant value;
434 msg.read(value);
435 return value;
436 }
437
438 /** @brief Get a property variant without mapper lookup. */
439 template <typename Variant>
440 static auto getPropertyVariant(const std::string& service,
441 const std::string& path,
442 const std::string& interface,
443 const std::string& property)
444 {
445 return getPropertyVariant<Variant>(getBus(), service, path, interface,
446 property);
447 }
448
449 /** @brief Set a property with mapper lookup. */
450 template <typename Property>
451 static void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
452 const std::string& interface,
453 const std::string& property, Property&& value)
454 {
455 using namespace std::literals::string_literals;
456
457 std::variant<Property> varValue(std::forward<Property>(value));
458
459 auto service = getService(bus, path, interface);
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 with mapper lookup. */
471 template <typename Property>
472 static void setProperty(const std::string& path,
473 const std::string& interface,
474 const std::string& property, Property&& value)
475 {
476 return setProperty(getBus(), path, interface, property,
477 std::forward<Property>(value));
478 }
479
480 /** @brief Set a property without mapper lookup. */
481 template <typename Property>
482 static void setProperty(sdbusplus::bus::bus& bus,
483 const std::string& service, const std::string& path,
484 const std::string& interface,
485 const std::string& property, Property&& value)
486 {
487 using namespace std::literals::string_literals;
488
489 std::variant<Property> varValue(std::forward<Property>(value));
490
491 auto msg = callMethodAndReturn(bus, service, path,
492 "org.freedesktop.DBus.Properties"s,
493 "Set"s, interface, property, varValue);
494 if (msg.is_method_error())
495 {
496 throw DBusPropertyError{"DBus set property failed", service, path,
497 interface, property};
498 }
499 }
500
501 /** @brief Set a property without mapper lookup. */
502 template <typename Property>
503 static void setProperty(const std::string& service, const std::string& path,
504 const std::string& interface,
505 const std::string& property, Property&& value)
506 {
507 return setProperty(getBus(), service, path, interface, property,
508 std::forward<Property>(value));
509 }
510
511 /** @brief Invoke method with mapper lookup. */
512 template <typename... Args>
513 static auto lookupAndCallMethod(sdbusplus::bus::bus& bus,
514 const std::string& path,
515 const std::string& interface,
516 const std::string& method, Args&&... args)
517 {
518 return callMethod(bus, getService(bus, path, interface), path,
519 interface, method, std::forward<Args>(args)...);
520 }
521
522 /** @brief Invoke method with mapper lookup. */
523 template <typename... Args>
524 static auto lookupAndCallMethod(const std::string& path,
525 const std::string& interface,
526 const std::string& method, Args&&... args)
527 {
528 return lookupAndCallMethod(getBus(), path, interface, method,
529 std::forward<Args>(args)...);
530 }
531
532 /** @brief Invoke method and read with mapper lookup. */
533 template <typename Ret, typename... Args>
534 static auto lookupCallMethodAndRead(sdbusplus::bus::bus& bus,
535 const std::string& path,
536 const std::string& interface,
537 const std::string& method,
538 Args&&... args)
539 {
540 return callMethodAndRead(bus, getService(bus, path, interface), path,
541 interface, method,
542 std::forward<Args>(args)...);
543 }
544
545 /** @brief Invoke method and read with mapper lookup. */
546 template <typename Ret, typename... Args>
547 static auto lookupCallMethodAndRead(const std::string& path,
548 const std::string& interface,
549 const std::string& method,
550 Args&&... args)
551 {
552 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method,
553 std::forward<Args>(args)...);
554 }
555
556 /** @brief Invoke a method and return without checking for error. */
557 template <typename... Args>
558 static auto callMethodAndReturn(sdbusplus::bus::bus& bus,
559 const std::string& busName,
560 const std::string& path,
561 const std::string& interface,
562 const std::string& method, Args&&... args)
563 {
564 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
565 interface.c_str(), method.c_str());
566 reqMsg.append(std::forward<Args>(args)...);
567 auto respMsg = bus.call(reqMsg);
568
569 return respMsg;
570 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400571};
572
573} // namespace util
574} // namespace fan
575} // namespace phosphor