blob: 9c3d08d78c236534c3acefaab712a14f4878e1d4 [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:
35 DBusError(const char* msg) : std::runtime_error(msg)
36 {}
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
213 /** @brief Get service from the mapper. */
214 static auto getService(sdbusplus::bus::bus& bus, const std::string& path,
215 const std::string& interface)
216 {
217 using namespace std::literals::string_literals;
218 using GetObject = std::map<std::string, std::vector<std::string>>;
219
220 try
221 {
222 auto mapperResp = callMethodAndRead<GetObject>(
223 bus, "xyz.openbmc_project.ObjectMapper"s,
224 "/xyz/openbmc_project/object_mapper"s,
225 "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
226 GetObject::mapped_type{interface});
227
228 if (mapperResp.empty())
229 {
230 // Should never happen. A missing object would fail
231 // in callMethodAndRead()
232 phosphor::logging::log<phosphor::logging::level::ERR>(
233 "Empty mapper response on service lookup");
234 throw DBusServiceError{path, interface};
235 }
236 return mapperResp.begin()->first;
237 }
238 catch (DBusMethodError& e)
239 {
240 throw DBusServiceError{path, interface};
241 }
242 }
243
244 /** @brief Get service from the mapper. */
245 static auto getService(const std::string& path,
246 const std::string& interface)
247 {
248 return getService(getBus(), path, interface);
249 }
250
251 /** @brief Get a property with mapper lookup. */
252 template <typename Property>
253 static auto getProperty(sdbusplus::bus::bus& bus, const std::string& path,
254 const std::string& interface,
255 const std::string& property)
256 {
257 using namespace std::literals::string_literals;
258
259 auto service = getService(bus, path, interface);
260 auto msg =
261 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
262 "Get"s, interface, property);
263 if (msg.is_method_error())
264 {
265 throw DBusPropertyError{"DBus get property failed", service, path,
266 interface, property};
267 }
268 std::variant<Property> value;
269 msg.read(value);
270 return std::get<Property>(value);
271 }
272
273 /** @brief Get a property with mapper lookup. */
274 template <typename Property>
275 static auto getProperty(const std::string& path,
276 const std::string& interface,
277 const std::string& property)
278 {
279 return getProperty<Property>(getBus(), path, interface, property);
280 }
281
282 /** @brief Get a property variant with mapper lookup. */
283 template <typename Variant>
284 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
285 const std::string& path,
286 const std::string& interface,
287 const std::string& property)
288 {
289 using namespace std::literals::string_literals;
290
291 auto service = getService(bus, path, interface);
292 auto msg =
293 callMethod(bus, service, path, "org.freedesktop.DBus.Properties"s,
294 "Get"s, interface, property);
295 if (msg.is_method_error())
296 {
297 throw DBusPropertyError{"DBus get property variant failed", service,
298 path, interface, property};
299 }
300 Variant value;
301 msg.read(value);
302 return value;
303 }
304
305 /** @brief Get a property variant with mapper lookup. */
306 template <typename Variant>
307 static auto getPropertyVariant(const std::string& path,
308 const std::string& interface,
309 const std::string& property)
310 {
311 return getPropertyVariant<Variant>(getBus(), path, interface, property);
312 }
313
314 /** @brief Get a property without mapper lookup. */
315 template <typename Property>
316 static auto getProperty(sdbusplus::bus::bus& bus,
317 const std::string& service, const std::string& path,
318 const std::string& interface,
319 const std::string& property)
320 {
321 using namespace std::literals::string_literals;
322
323 auto msg = callMethodAndReturn(bus, service, path,
324 "org.freedesktop.DBus.Properties"s,
325 "Get"s, interface, property);
326 if (msg.is_method_error())
327 {
328 throw DBusPropertyError{"DBus get property failed", service, path,
329 interface, property};
330 }
331 std::variant<Property> value;
332 msg.read(value);
333 return std::get<Property>(value);
334 }
335
336 /** @brief Get a property without mapper lookup. */
337 template <typename Property>
338 static auto getProperty(const std::string& service, const std::string& path,
339 const std::string& interface,
340 const std::string& property)
341 {
342 return getProperty<Property>(getBus(), service, path, interface,
343 property);
344 }
345
346 /** @brief Get a property variant without mapper lookup. */
347 template <typename Variant>
348 static auto getPropertyVariant(sdbusplus::bus::bus& bus,
349 const std::string& service,
350 const std::string& path,
351 const std::string& interface,
352 const std::string& property)
353 {
354 using namespace std::literals::string_literals;
355
356 auto msg = callMethodAndReturn(bus, service, path,
357 "org.freedesktop.DBus.Properties"s,
358 "Get"s, interface, property);
359 if (msg.is_method_error())
360 {
361 throw DBusPropertyError{"DBus get property variant failed", service,
362 path, interface, property};
363 }
364 Variant value;
365 msg.read(value);
366 return value;
367 }
368
369 /** @brief Get a property variant without mapper lookup. */
370 template <typename Variant>
371 static auto getPropertyVariant(const std::string& service,
372 const std::string& path,
373 const std::string& interface,
374 const std::string& property)
375 {
376 return getPropertyVariant<Variant>(getBus(), service, path, interface,
377 property);
378 }
379
380 /** @brief Set a property with mapper lookup. */
381 template <typename Property>
382 static void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
383 const std::string& interface,
384 const std::string& property, Property&& value)
385 {
386 using namespace std::literals::string_literals;
387
388 std::variant<Property> varValue(std::forward<Property>(value));
389
390 auto service = getService(bus, path, interface);
391 auto msg = callMethodAndReturn(bus, service, path,
392 "org.freedesktop.DBus.Properties"s,
393 "Set"s, interface, property, varValue);
394 if (msg.is_method_error())
395 {
396 throw DBusPropertyError{"DBus set property failed", service, path,
397 interface, property};
398 }
399 }
400
401 /** @brief Set a property with mapper lookup. */
402 template <typename Property>
403 static void setProperty(const std::string& path,
404 const std::string& interface,
405 const std::string& property, Property&& value)
406 {
407 return setProperty(getBus(), path, interface, property,
408 std::forward<Property>(value));
409 }
410
411 /** @brief Set a property without mapper lookup. */
412 template <typename Property>
413 static void setProperty(sdbusplus::bus::bus& bus,
414 const std::string& service, const std::string& path,
415 const std::string& interface,
416 const std::string& property, Property&& value)
417 {
418 using namespace std::literals::string_literals;
419
420 std::variant<Property> varValue(std::forward<Property>(value));
421
422 auto msg = callMethodAndReturn(bus, service, path,
423 "org.freedesktop.DBus.Properties"s,
424 "Set"s, interface, property, varValue);
425 if (msg.is_method_error())
426 {
427 throw DBusPropertyError{"DBus set property failed", service, path,
428 interface, property};
429 }
430 }
431
432 /** @brief Set a property without mapper lookup. */
433 template <typename Property>
434 static void setProperty(const std::string& service, const std::string& path,
435 const std::string& interface,
436 const std::string& property, Property&& value)
437 {
438 return setProperty(getBus(), service, path, interface, property,
439 std::forward<Property>(value));
440 }
441
442 /** @brief Invoke method with mapper lookup. */
443 template <typename... Args>
444 static auto lookupAndCallMethod(sdbusplus::bus::bus& bus,
445 const std::string& path,
446 const std::string& interface,
447 const std::string& method, Args&&... args)
448 {
449 return callMethod(bus, getService(bus, path, interface), path,
450 interface, method, std::forward<Args>(args)...);
451 }
452
453 /** @brief Invoke method with mapper lookup. */
454 template <typename... Args>
455 static auto lookupAndCallMethod(const std::string& path,
456 const std::string& interface,
457 const std::string& method, Args&&... args)
458 {
459 return lookupAndCallMethod(getBus(), path, interface, method,
460 std::forward<Args>(args)...);
461 }
462
463 /** @brief Invoke method and read with mapper lookup. */
464 template <typename Ret, typename... Args>
465 static auto lookupCallMethodAndRead(sdbusplus::bus::bus& bus,
466 const std::string& path,
467 const std::string& interface,
468 const std::string& method,
469 Args&&... args)
470 {
471 return callMethodAndRead(bus, getService(bus, path, interface), path,
472 interface, method,
473 std::forward<Args>(args)...);
474 }
475
476 /** @brief Invoke method and read with mapper lookup. */
477 template <typename Ret, typename... Args>
478 static auto lookupCallMethodAndRead(const std::string& path,
479 const std::string& interface,
480 const std::string& method,
481 Args&&... args)
482 {
483 return lookupCallMethodAndRead<Ret>(getBus(), path, interface, method,
484 std::forward<Args>(args)...);
485 }
486
487 /** @brief Invoke a method and return without checking for error. */
488 template <typename... Args>
489 static auto callMethodAndReturn(sdbusplus::bus::bus& bus,
490 const std::string& busName,
491 const std::string& path,
492 const std::string& interface,
493 const std::string& method, Args&&... args)
494 {
495 auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
496 interface.c_str(), method.c_str());
497 reqMsg.append(std::forward<Args>(args)...);
498 auto respMsg = bus.call(reqMsg);
499
500 return respMsg;
501 }
Brad Bishop6e9cfdb2017-06-08 11:59:58 -0400502};
503
504} // namespace util
505} // namespace fan
506} // namespace phosphor