blob: 3da6f937a01c3ff4c347d0305bf6079b2d26bb79 [file] [log] [blame]
Brad Bishop6e9cfdb2017-06-08 11:59:58 -04001#pragma once
2
Brad Bishop6e9cfdb2017-06-08 11:59:58 -04003#include <phosphor-logging/elog-errors.hpp>
Matthew Barth9e80c872020-05-26 10:50:29 -05004#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>
Brad Bishop6e9cfdb2017-06-08 11:59:58 -04009#include <xyz/openbmc_project/Common/error.hpp>
10
11namespace phosphor
12{
13namespace fan
14{
15namespace util
16{
17namespace detail
18{
19namespace errors = sdbusplus::xyz::openbmc_project::Common::Error;
20} // namespace detail
21
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050022/**
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 */
32class DBusError : public std::runtime_error
33{
Matthew Barth9e80c872020-05-26 10:50:29 -050034 public:
Matthew Barth9f93bd32020-05-28 16:57:14 -050035 explicit DBusError(const char* msg) : std::runtime_error(msg)
Matthew Barth9e80c872020-05-26 10:50:29 -050036 {}
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050037};
38
39/**
40 * @class DBusMethodError
41 *
42 * Thrown on a DBus Method call failure
43 */
44class DBusMethodError : public DBusError
45{
Matthew Barth9e80c872020-05-26 10:50:29 -050046 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 {}
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050052
Matthew Barth9e80c872020-05-26 10:50:29 -050053 const std::string busName;
54 const std::string path;
55 const std::string interface;
56 const std::string method;
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050057};
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 */
65class DBusServiceError : public DBusError
66{
Matthew Barth9e80c872020-05-26 10:50:29 -050067 public:
68 DBusServiceError(const std::string& path, const std::string& interface) :
69 DBusError("DBus service lookup failed"), path(path),
70 interface(interface)
71 {}
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050072
Matthew Barth9e80c872020-05-26 10:50:29 -050073 const std::string path;
74 const std::string interface;
Matt Spinlerba7b5fe2018-04-25 15:26:10 -050075};
76
Matthew Barth88923a02018-05-11 10:14:44 -050077/**
78 * @class DBusPropertyError
79 *
80 * Thrown when a set/get property fails.
81 */
82class DBusPropertyError : public DBusError
83{
Matthew Barth9e80c872020-05-26 10:50:29 -050084 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 {}
Matthew Barth88923a02018-05-11 10:14:44 -050091
Matthew Barth9e80c872020-05-26 10:50:29 -050092 const std::string busName;
93 const std::string path;
94 const std::string interface;
95 const std::string property;
Matthew Barth88923a02018-05-11 10:14:44 -050096};
97
Brad Bishop1e4922a2017-07-12 22:56:49 -040098/** @brief Alias for PropertiesChanged signal callbacks. */
Matthew Barth9e80c872020-05-26 10:50:29 -050099template <typename... T>
Patrick Williamsc21d0b32020-05-13 17:55:14 -0500100using Properties = std::map<std::string, std::variant<T...>>;
Brad Bishop1e4922a2017-07-12 22:56:49 -0400101
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400102/** @class SDBusPlus
103 * @brief DBus access delegate implementation for sdbusplus.
104 */
105class SDBusPlus
106{
107
Matthew Barth9e80c872020-05-26 10:50:29 -0500108 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 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400115
Matthew Barth9e80c872020-05-26 10:50:29 -0500116 /** @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
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400127 {
Matthew Barth9e80c872020-05-26 10:50:29 -0500128 auto respMsg = bus.call(reqMsg);
129 if (respMsg.is_method_error())
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400130 {
Matt Spinlerba7b5fe2018-04-25 15:26:10 -0500131 throw DBusMethodError{busName, path, interface, method};
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400132 }
Matthew Barth7c48d102018-05-09 14:52:36 -0500133 return respMsg;
134 }
Matthew Barth9e80c872020-05-26 10:50:29 -0500135 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
Matthew Barthfcbdc0e2020-10-28 14:11:07 -0500213 /** @brief Get subtree paths from the mapper without checking response. */
214 static auto getSubTreePathsRaw(sdbusplus::bus::bus& bus,
215 const std::string& path,
216 const std::string& interface, int32_t depth)
217 {
218 using namespace std::literals::string_literals;
219
220 using Path = std::string;
221 using Intf = std::string;
222 using Intfs = std::vector<Intf>;
223 using ObjectPaths = std::vector<Path>;
224 Intfs intfs = {interface};
225
226 return callMethodAndRead<ObjectPaths>(
227 bus, "xyz.openbmc_project.ObjectMapper"s,
228 "/xyz/openbmc_project/object_mapper"s,
229 "xyz.openbmc_project.ObjectMapper"s, "GetSubTreePaths"s, path,
230 depth, intfs);
231 }
232
233 /** @brief Get subtree paths from the mapper. */
234 static auto getSubTreePaths(sdbusplus::bus::bus& bus,
235 const std::string& path,
236 const std::string& interface, int32_t depth)
237 {
238 auto mapperResp = getSubTreePathsRaw(bus, path, interface, depth);
239 if (mapperResp.empty())
240 {
241 phosphor::logging::log<phosphor::logging::level::ERR>(
242 "Empty response from mapper GetSubTreePaths",
243 phosphor::logging::entry("SUBTREE=%s", path.c_str()),
244 phosphor::logging::entry("INTERFACE=%s", interface.c_str()),
245 phosphor::logging::entry("DEPTH=%u", depth));
246 phosphor::logging::elog<detail::errors::InternalFailure>();
247 }
248 return mapperResp;
249 }
250
Matthew Barth9e80c872020-05-26 10:50:29 -0500251 /** @brief Get service from the mapper. */
252 static auto getService(sdbusplus::bus::bus& bus, const std::string& path,
253 const std::string& interface)
254 {
255 using namespace std::literals::string_literals;
256 using GetObject = std::map<std::string, std::vector<std::string>>;
257
258 try
259 {
260 auto mapperResp = callMethodAndRead<GetObject>(
261 bus, "xyz.openbmc_project.ObjectMapper"s,
262 "/xyz/openbmc_project/object_mapper"s,
263 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
264 GetObject::mapped_type{interface});
265
266 if (mapperResp.empty())
267 {
268 // Should never happen. A missing object would fail
269 // in callMethodAndRead()
270 phosphor::logging::log<phosphor::logging::level::ERR>(
271 "Empty mapper response on service lookup");
272 throw DBusServiceError{path, interface};
273 }
274 return mapperResp.begin()->first;
275 }
276 catch (DBusMethodError& e)
277 {
278 throw DBusServiceError{path, interface};
279 }
280 }
281
282 /** @brief Get service from the mapper. */
283 static auto getService(const std::string& path,
284 const std::string& interface)
285 {
286 return getService(getBus(), path, interface);
287 }
288
289 /** @brief Get a property with mapper lookup. */
290 template <typename Property>
291 static auto getProperty(sdbusplus::bus::bus& bus, const std::string& path,
292 const std::string& interface,
293 const std::string& property)
294 {
295 using namespace std::literals::string_literals;
296
297 auto service = getService(bus, path, interface);
298 auto msg =
299 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
300 "Get"s, interface, property);
301 if (msg.is_method_error())
302 {
303 throw DBusPropertyError{"DBus get property failed", service, path,
304 interface, property};
305 }
306 std::variant<Property> value;
307 msg.read(value);
308 return std::get<Property>(value);
309 }
310
311 /** @brief Get a property with mapper lookup. */
312 template <typename Property>
313 static auto getProperty(const std::string& path,
314 const std::string& interface,
315 const std::string& property)
316 {
317 return getProperty<Property>(getBus(), path, interface, property);
318 }
319
320 /** @brief Get a property variant with mapper lookup. */
321 template <typename Variant>
322 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
323 const std::string& path,
324 const std::string& interface,
325 const std::string& property)
326 {
327 using namespace std::literals::string_literals;
328
329 auto service = getService(bus, path, interface);
330 auto msg =
331 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
332 "Get"s, interface, property);
333 if (msg.is_method_error())
334 {
335 throw DBusPropertyError{"DBus get property variant failed", service,
336 path, interface, property};
337 }
338 Variant value;
339 msg.read(value);
340 return value;
341 }
342
343 /** @brief Get a property variant with mapper lookup. */
344 template <typename Variant>
345 static auto getPropertyVariant(const std::string& path,
346 const std::string& interface,
347 const std::string& property)
348 {
349 return getPropertyVariant<Variant>(getBus(), path, interface, property);
350 }
351
352 /** @brief Get a property without mapper lookup. */
353 template <typename Property>
354 static auto getProperty(sdbusplus::bus::bus& bus,
355 const std::string& service, const std::string& path,
356 const std::string& interface,
357 const std::string& property)
358 {
359 using namespace std::literals::string_literals;
360
361 auto msg = callMethodAndReturn(bus, service, path,
362 "org.freedesktop.DBus.Properties"s,
363 "Get"s, interface, property);
364 if (msg.is_method_error())
365 {
366 throw DBusPropertyError{"DBus get property failed", service, path,
367 interface, property};
368 }
369 std::variant<Property> value;
370 msg.read(value);
371 return std::get<Property>(value);
372 }
373
374 /** @brief Get a property without mapper lookup. */
375 template <typename Property>
376 static auto getProperty(const std::string& service, const std::string& path,
377 const std::string& interface,
378 const std::string& property)
379 {
380 return getProperty<Property>(getBus(), service, path, interface,
381 property);
382 }
383
384 /** @brief Get a property variant without mapper lookup. */
385 template <typename Variant>
386 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
387 const std::string& service,
388 const std::string& path,
389 const std::string& interface,
390 const std::string& property)
391 {
392 using namespace std::literals::string_literals;
393
394 auto msg = callMethodAndReturn(bus, service, path,
395 "org.freedesktop.DBus.Properties"s,
396 "Get"s, interface, property);
397 if (msg.is_method_error())
398 {
399 throw DBusPropertyError{"DBus get property variant failed", service,
400 path, interface, property};
401 }
402 Variant value;
403 msg.read(value);
404 return value;
405 }
406
407 /** @brief Get a property variant without mapper lookup. */
408 template <typename Variant>
409 static auto getPropertyVariant(const std::string& service,
410 const std::string& path,
411 const std::string& interface,
412 const std::string& property)
413 {
414 return getPropertyVariant<Variant>(getBus(), service, path, interface,
415 property);
416 }
417
418 /** @brief Set a property with mapper lookup. */
419 template <typename Property>
420 static void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
421 const std::string& interface,
422 const std::string& property, Property&& value)
423 {
424 using namespace std::literals::string_literals;
425
426 std::variant<Property> varValue(std::forward<Property>(value));
427
428 auto service = getService(bus, path, interface);
429 auto msg = callMethodAndReturn(bus, service, path,
430 "org.freedesktop.DBus.Properties"s,
431 "Set"s, interface, property, varValue);
432 if (msg.is_method_error())
433 {
434 throw DBusPropertyError{"DBus set property failed", service, path,
435 interface, property};
436 }
437 }
438
439 /** @brief Set a property with mapper lookup. */
440 template <typename Property>
441 static void setProperty(const std::string& path,
442 const std::string& interface,
443 const std::string& property, Property&& value)
444 {
445 return setProperty(getBus(), path, interface, property,
446 std::forward<Property>(value));
447 }
448
449 /** @brief Set a property without mapper lookup. */
450 template <typename Property>
451 static void setProperty(sdbusplus::bus::bus& bus,
452 const std::string& service, const std::string& path,
453 const std::string& interface,
454 const std::string& property, Property&& value)
455 {
456 using namespace std::literals::string_literals;
457
458 std::variant<Property> varValue(std::forward<Property>(value));
459
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 without mapper lookup. */
471 template <typename Property>
472 static void setProperty(const std::string& service, const std::string& path,
473 const std::string& interface,
474 const std::string& property, Property&& value)
475 {
476 return setProperty(getBus(), service, path, interface, property,
477 std::forward<Property>(value));
478 }
479
480 /** @brief Invoke method with mapper lookup. */
481 template <typename... Args>
482 static auto lookupAndCallMethod(sdbusplus::bus::bus& bus,
483 const std::string& path,
484 const std::string& interface,
485 const std::string& method, Args&&... args)
486 {
487 return callMethod(bus, getService(bus, path, interface), path,
488 interface, method, std::forward<Args>(args)...);
489 }
490
491 /** @brief Invoke method with mapper lookup. */
492 template <typename... Args>
493 static auto lookupAndCallMethod(const std::string& path,
494 const std::string& interface,
495 const std::string& method, Args&&... args)
496 {
497 return lookupAndCallMethod(getBus(), path, interface, method,
498 std::forward<Args>(args)...);
499 }
500
501 /** @brief Invoke method and read with mapper lookup. */
502 template <typename Ret, typename... Args>
503 static auto lookupCallMethodAndRead(sdbusplus::bus::bus& bus,
504 const std::string& path,
505 const std::string& interface,
506 const std::string& method,
507 Args&&... args)
508 {
509 return callMethodAndRead(bus, getService(bus, path, interface), path,
510 interface, method,
511 std::forward<Args>(args)...);
512 }
513
514 /** @brief Invoke method and read with mapper lookup. */
515 template <typename Ret, typename... Args>
516 static auto lookupCallMethodAndRead(const std::string& path,
517 const std::string& interface,
518 const std::string& method,
519 Args&&... args)
520 {
521 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method,
522 std::forward<Args>(args)...);
523 }
524
525 /** @brief Invoke a method and return without checking for error. */
526 template <typename... Args>
527 static auto callMethodAndReturn(sdbusplus::bus::bus& bus,
528 const std::string& busName,
529 const std::string& path,
530 const std::string& interface,
531 const std::string& method, Args&&... args)
532 {
533 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
534 interface.c_str(), method.c_str());
535 reqMsg.append(std::forward<Args>(args)...);
536 auto respMsg = bus.call(reqMsg);
537
538 return respMsg;
539 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400540};
541
542} // namespace util
543} // namespace fan
544} // namespace phosphor