blob: 16bd0831e9e599f3367f82db021552633360695c [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
294 /** @brief Get a property with mapper lookup. */
295 template <typename Property>
296 static auto getProperty(sdbusplus::bus::bus& bus, const std::string& path,
297 const std::string& interface,
298 const std::string& property)
299 {
300 using namespace std::literals::string_literals;
301
302 auto service = getService(bus, path, interface);
303 auto msg =
304 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
305 "Get"s, interface, property);
306 if (msg.is_method_error())
307 {
308 throw DBusPropertyError{"DBus get property failed", service, path,
309 interface, property};
310 }
311 std::variant<Property> value;
312 msg.read(value);
313 return std::get<Property>(value);
314 }
315
316 /** @brief Get a property with mapper lookup. */
317 template <typename Property>
318 static auto getProperty(const std::string& path,
319 const std::string& interface,
320 const std::string& property)
321 {
322 return getProperty<Property>(getBus(), path, interface, property);
323 }
324
325 /** @brief Get a property variant with mapper lookup. */
326 template <typename Variant>
327 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
328 const std::string& path,
329 const std::string& interface,
330 const std::string& property)
331 {
332 using namespace std::literals::string_literals;
333
334 auto service = getService(bus, path, interface);
335 auto msg =
336 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
337 "Get"s, interface, property);
338 if (msg.is_method_error())
339 {
340 throw DBusPropertyError{"DBus get property variant failed", service,
341 path, interface, property};
342 }
343 Variant value;
344 msg.read(value);
345 return value;
346 }
347
348 /** @brief Get a property variant with mapper lookup. */
349 template <typename Variant>
350 static auto getPropertyVariant(const std::string& path,
351 const std::string& interface,
352 const std::string& property)
353 {
354 return getPropertyVariant<Variant>(getBus(), path, interface, property);
355 }
356
357 /** @brief Get a property without mapper lookup. */
358 template <typename Property>
359 static auto getProperty(sdbusplus::bus::bus& bus,
360 const std::string& service, const std::string& path,
361 const std::string& interface,
362 const std::string& property)
363 {
364 using namespace std::literals::string_literals;
365
366 auto msg = callMethodAndReturn(bus, service, path,
367 "org.freedesktop.DBus.Properties"s,
368 "Get"s, interface, property);
369 if (msg.is_method_error())
370 {
371 throw DBusPropertyError{"DBus get property failed", service, path,
372 interface, property};
373 }
374 std::variant<Property> value;
375 msg.read(value);
376 return std::get<Property>(value);
377 }
378
379 /** @brief Get a property without mapper lookup. */
380 template <typename Property>
381 static auto getProperty(const std::string& service, const std::string& path,
382 const std::string& interface,
383 const std::string& property)
384 {
385 return getProperty<Property>(getBus(), service, path, interface,
386 property);
387 }
388
389 /** @brief Get a property variant without mapper lookup. */
390 template <typename Variant>
391 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
392 const std::string& service,
393 const std::string& path,
394 const std::string& interface,
395 const std::string& property)
396 {
397 using namespace std::literals::string_literals;
398
399 auto msg = callMethodAndReturn(bus, service, path,
400 "org.freedesktop.DBus.Properties"s,
401 "Get"s, interface, property);
402 if (msg.is_method_error())
403 {
404 throw DBusPropertyError{"DBus get property variant failed", service,
405 path, interface, property};
406 }
407 Variant value;
408 msg.read(value);
409 return value;
410 }
411
412 /** @brief Get a property variant without mapper lookup. */
413 template <typename Variant>
414 static auto getPropertyVariant(const std::string& service,
415 const std::string& path,
416 const std::string& interface,
417 const std::string& property)
418 {
419 return getPropertyVariant<Variant>(getBus(), service, path, interface,
420 property);
421 }
422
423 /** @brief Set a property with mapper lookup. */
424 template <typename Property>
425 static void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
426 const std::string& interface,
427 const std::string& property, Property&& value)
428 {
429 using namespace std::literals::string_literals;
430
431 std::variant<Property> varValue(std::forward<Property>(value));
432
433 auto service = getService(bus, path, interface);
434 auto msg = callMethodAndReturn(bus, service, path,
435 "org.freedesktop.DBus.Properties"s,
436 "Set"s, interface, property, varValue);
437 if (msg.is_method_error())
438 {
439 throw DBusPropertyError{"DBus set property failed", service, path,
440 interface, property};
441 }
442 }
443
444 /** @brief Set a property with mapper lookup. */
445 template <typename Property>
446 static void setProperty(const std::string& path,
447 const std::string& interface,
448 const std::string& property, Property&& value)
449 {
450 return setProperty(getBus(), path, interface, property,
451 std::forward<Property>(value));
452 }
453
454 /** @brief Set a property without mapper lookup. */
455 template <typename Property>
456 static void setProperty(sdbusplus::bus::bus& bus,
457 const std::string& service, const std::string& path,
458 const std::string& interface,
459 const std::string& property, Property&& value)
460 {
461 using namespace std::literals::string_literals;
462
463 std::variant<Property> varValue(std::forward<Property>(value));
464
465 auto msg = callMethodAndReturn(bus, service, path,
466 "org.freedesktop.DBus.Properties"s,
467 "Set"s, interface, property, varValue);
468 if (msg.is_method_error())
469 {
470 throw DBusPropertyError{"DBus set property failed", service, path,
471 interface, property};
472 }
473 }
474
475 /** @brief Set a property without mapper lookup. */
476 template <typename Property>
477 static void setProperty(const std::string& service, const std::string& path,
478 const std::string& interface,
479 const std::string& property, Property&& value)
480 {
481 return setProperty(getBus(), service, path, interface, property,
482 std::forward<Property>(value));
483 }
484
485 /** @brief Invoke method with mapper lookup. */
486 template <typename... Args>
487 static auto lookupAndCallMethod(sdbusplus::bus::bus& bus,
488 const std::string& path,
489 const std::string& interface,
490 const std::string& method, Args&&... args)
491 {
492 return callMethod(bus, getService(bus, path, interface), path,
493 interface, method, std::forward<Args>(args)...);
494 }
495
496 /** @brief Invoke method with mapper lookup. */
497 template <typename... Args>
498 static auto lookupAndCallMethod(const std::string& path,
499 const std::string& interface,
500 const std::string& method, Args&&... args)
501 {
502 return lookupAndCallMethod(getBus(), path, interface, method,
503 std::forward<Args>(args)...);
504 }
505
506 /** @brief Invoke method and read with mapper lookup. */
507 template <typename Ret, typename... Args>
508 static auto lookupCallMethodAndRead(sdbusplus::bus::bus& bus,
509 const std::string& path,
510 const std::string& interface,
511 const std::string& method,
512 Args&&... args)
513 {
514 return callMethodAndRead(bus, getService(bus, path, interface), path,
515 interface, method,
516 std::forward<Args>(args)...);
517 }
518
519 /** @brief Invoke method and read with mapper lookup. */
520 template <typename Ret, typename... Args>
521 static auto lookupCallMethodAndRead(const std::string& path,
522 const std::string& interface,
523 const std::string& method,
524 Args&&... args)
525 {
526 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method,
527 std::forward<Args>(args)...);
528 }
529
530 /** @brief Invoke a method and return without checking for error. */
531 template <typename... Args>
532 static auto callMethodAndReturn(sdbusplus::bus::bus& bus,
533 const std::string& busName,
534 const std::string& path,
535 const std::string& interface,
536 const std::string& method, Args&&... args)
537 {
538 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
539 interface.c_str(), method.c_str());
540 reqMsg.append(std::forward<Args>(args)...);
541 auto respMsg = bus.call(reqMsg);
542
543 return respMsg;
544 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400545};
546
547} // namespace util
548} // namespace fan
549} // namespace phosphor