1 #pragma once
2 
3 #include <sdbusplus/bus.hpp>
4 #include <sdbusplus/message.hpp>
5 #include <sdbusplus/bus/match.hpp>
6 #include <phosphor-logging/log.hpp>
7 #include <phosphor-logging/elog.hpp>
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <xyz/openbmc_project/Common/error.hpp>
10 
11 namespace phosphor
12 {
13 namespace fan
14 {
15 namespace util
16 {
17 namespace detail
18 {
19 namespace errors = sdbusplus::xyz::openbmc_project::Common::Error;
20 } // namespace detail
21 
22 /**
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  */
32 class DBusError : public std::runtime_error
33 {
34     public:
35         DBusError(const char* msg) :
36             std::runtime_error(msg)
37         {
38         }
39 };
40 
41 /**
42  * @class DBusMethodError
43  *
44  * Thrown on a DBus Method call failure
45  */
46 class DBusMethodError : public DBusError
47 {
48     public:
49         DBusMethodError(
50                 const std::string& busName,
51                 const std::string& path,
52                 const std::string& interface,
53                 const std::string& method) :
54             DBusError("DBus method call failed"),
55             busName(busName),
56             path(path),
57             interface(interface),
58             method(method)
59             {
60             }
61 
62         const std::string busName;
63         const std::string path;
64         const std::string interface;
65         const std::string method;
66 };
67 
68 /**
69  * @class DBusServiceError
70  *
71  * Thrown when a service lookup fails.  Usually this points to
72  * the object path not being present in D-Bus.
73  */
74 class DBusServiceError : public DBusError
75 {
76     public:
77         DBusServiceError(
78                 const std::string& path,
79                 const std::string& interface) :
80             DBusError("DBus service lookup failed"),
81             path(path),
82             interface(interface)
83             {
84             }
85 
86         const std::string path;
87         const std::string interface;
88 };
89 
90 /** @brief Alias for PropertiesChanged signal callbacks. */
91 template <typename ...T>
92 using Properties = std::map<std::string, sdbusplus::message::variant<T...>>;
93 
94 /** @class SDBusPlus
95  *  @brief DBus access delegate implementation for sdbusplus.
96  */
97 class SDBusPlus
98 {
99 
100     public:
101         /** @brief Get the bus connection. */
102         static auto& getBus() __attribute__((pure))
103         {
104             static auto bus = sdbusplus::bus::new_default();
105             return bus;
106         }
107 
108         /** @brief Invoke a method. */
109         template <typename ...Args>
110         static auto callMethod(
111             sdbusplus::bus::bus& bus,
112             const std::string& busName,
113             const std::string& path,
114             const std::string& interface,
115             const std::string& method,
116             Args&& ... args)
117         {
118             auto reqMsg = bus.new_method_call(
119                     busName.c_str(),
120                     path.c_str(),
121                     interface.c_str(),
122                     method.c_str());
123             reqMsg.append(std::forward<Args>(args)...);
124             auto respMsg = bus.call(reqMsg);
125 
126             if (respMsg.is_method_error())
127             {
128                 throw DBusMethodError{busName, path, interface, method};
129             }
130 
131             return respMsg;
132         }
133 
134         /** @brief Invoke a method. */
135         template <typename ...Args>
136         static auto callMethod(
137             const std::string& busName,
138             const std::string& path,
139             const std::string& interface,
140             const std::string& method,
141             Args&& ... args)
142         {
143             return callMethod(
144                     getBus(),
145                     busName,
146                     path,
147                     interface,
148                     method,
149                     std::forward<Args>(args)...);
150         }
151 
152         /** @brief Invoke a method and read the response. */
153         template <typename Ret, typename ...Args>
154         static auto callMethodAndRead(
155             sdbusplus::bus::bus& bus,
156             const std::string& busName,
157             const std::string& path,
158             const std::string& interface,
159             const std::string& method,
160             Args&& ... args)
161         {
162             sdbusplus::message::message respMsg =
163                     callMethod<Args...>(
164                             bus,
165                             busName,
166                             path,
167                             interface,
168                             method,
169                             std::forward<Args>(args)...);
170             Ret resp;
171             respMsg.read(resp);
172             return resp;
173         }
174 
175         /** @brief Invoke a method and read the response. */
176         template <typename Ret, typename ...Args>
177         static auto callMethodAndRead(
178             const std::string& busName,
179             const std::string& path,
180             const std::string& interface,
181             const std::string& method,
182             Args&& ... args)
183         {
184             return callMethodAndRead<Ret>(
185                     getBus(),
186                     busName,
187                     path,
188                     interface,
189                     method,
190                     std::forward<Args>(args)...);
191         }
192 
193         /** @brief Get subtree from the mapper. */
194         static auto getSubTree(
195             sdbusplus::bus::bus& bus,
196             const std::string& path,
197             const std::string& interface,
198             int32_t depth)
199         {
200             using namespace std::literals::string_literals;
201 
202             using Path = std::string;
203             using Intf = std::string;
204             using Serv = std::string;
205             using Intfs = std::vector<Intf>;
206             using Objects = std::map<Path, std::map<Serv, Intfs>>;
207             Intfs intfs = {interface};
208 
209             auto mapperResp = callMethodAndRead<Objects>(
210                     bus,
211                     "xyz.openbmc_project.ObjectMapper"s,
212                     "/xyz/openbmc_project/object_mapper"s,
213                     "xyz.openbmc_project.ObjectMapper"s,
214                     "GetSubTree"s,
215                     path,
216                     depth,
217                     intfs);
218 
219             if (mapperResp.empty())
220             {
221                 phosphor::logging::log<phosphor::logging::level::ERR>(
222                         "Empty response from mapper GetSubTree",
223                         phosphor::logging::entry("SUBTREE=%s", path.c_str()),
224                         phosphor::logging::entry(
225                                 "INTERFACE=%s", interface.c_str()),
226                         phosphor::logging::entry("DEPTH=%u", depth));
227                 phosphor::logging::elog<detail::errors::InternalFailure>();
228             }
229             return mapperResp;
230         }
231 
232         /** @brief Get service from the mapper. */
233         static auto getService(
234             sdbusplus::bus::bus& bus,
235             const std::string& path,
236             const std::string& interface)
237         {
238             using namespace std::literals::string_literals;
239             using GetObject = std::map<std::string, std::vector<std::string>>;
240 
241             try
242             {
243                 auto mapperResp = callMethodAndRead<GetObject>(
244                         bus,
245                         "xyz.openbmc_project.ObjectMapper"s,
246                         "/xyz/openbmc_project/object_mapper"s,
247                         "xyz.openbmc_project.ObjectMapper"s,
248                         "GetObject"s,
249                         path,
250                         GetObject::mapped_type{interface});
251 
252                 if (mapperResp.empty())
253                 {
254                     //Should never happen.  A missing object would fail
255                     //in callMethodAndRead()
256                     phosphor::logging::log<phosphor::logging::level::ERR>(
257                             "Empty mapper response on service lookup");
258                     throw DBusServiceError{path, interface};
259                 }
260                 return mapperResp.begin()->first;
261             }
262             catch (DBusMethodError& e)
263             {
264                 throw DBusServiceError{path, interface};
265             }
266         }
267 
268         /** @brief Get service from the mapper. */
269         static auto getService(
270             const std::string& path,
271             const std::string& interface)
272         {
273             return getService(
274                     getBus(),
275                     path,
276                     interface);
277         }
278 
279         /** @brief Get a property with mapper lookup. */
280         template <typename Property>
281         static auto getProperty(
282             sdbusplus::bus::bus& bus,
283             const std::string& path,
284             const std::string& interface,
285             const std::string& property)
286         {
287             using namespace std::literals::string_literals;
288 
289             auto msg = callMethod(
290                     bus,
291                     getService(bus, path, interface),
292                     path,
293                     "org.freedesktop.DBus.Properties"s,
294                     "Get"s,
295                     interface,
296                     property);
297             sdbusplus::message::variant<Property> value;
298             msg.read(value);
299             return value.template get<Property>();
300         }
301 
302         /** @brief Get a property with mapper lookup. */
303         template <typename Property>
304         static auto getProperty(
305             const std::string& path,
306             const std::string& interface,
307             const std::string& property)
308         {
309             return getProperty<Property>(
310                     getBus(),
311                     path,
312                     interface,
313                     property);
314         }
315 
316         /** @brief Get a property variant with mapper lookup. */
317         template <typename Variant>
318         static auto getPropertyVariant(
319             sdbusplus::bus::bus& bus,
320             const std::string& path,
321             const std::string& interface,
322             const std::string& property)
323         {
324             using namespace std::literals::string_literals;
325 
326             auto msg = callMethod(
327                     bus,
328                     getService(bus, path, interface),
329                     path,
330                     "org.freedesktop.DBus.Properties"s,
331                     "Get"s,
332                     interface,
333                     property);
334             Variant value;
335             msg.read(value);
336             return value;
337         }
338 
339         /** @brief Get a property variant with mapper lookup. */
340         template <typename Variant>
341         static auto getPropertyVariant(
342             const std::string& path,
343             const std::string& interface,
344             const std::string& property)
345         {
346             return getPropertyVariant<Variant>(
347                     getBus(),
348                     path,
349                     interface,
350                     property);
351         }
352 
353         /** @brief Get a property without mapper lookup. */
354         template <typename Property>
355         static auto getProperty(
356             sdbusplus::bus::bus& bus,
357             const std::string& service,
358             const std::string& path,
359             const std::string& interface,
360             const std::string& property)
361         {
362             using namespace std::literals::string_literals;
363 
364             auto msg = callMethod(
365                     bus,
366                     service,
367                     path,
368                     "org.freedesktop.DBus.Properties"s,
369                     "Get"s,
370                     interface,
371                     property);
372             sdbusplus::message::variant<Property> value;
373             msg.read(value);
374             return value.template get<Property>();
375         }
376 
377         /** @brief Get a property without mapper lookup. */
378         template <typename Property>
379         static auto getProperty(
380             const std::string& service,
381             const std::string& path,
382             const std::string& interface,
383             const std::string& property)
384         {
385             return getProperty<Property>(
386                     getBus(),
387                     service,
388                     path,
389                     interface,
390                     property);
391         }
392 
393         /** @brief Get a property variant without mapper lookup. */
394         template <typename Variant>
395         static auto getPropertyVariant(
396             sdbusplus::bus::bus& bus,
397             const std::string& service,
398             const std::string& path,
399             const std::string& interface,
400             const std::string& property)
401         {
402             using namespace std::literals::string_literals;
403 
404             auto msg = callMethod(
405                     bus,
406                     service,
407                     path,
408                     "org.freedesktop.DBus.Properties"s,
409                     "Get"s,
410                     interface,
411                     property);
412             Variant value;
413             msg.read(value);
414             return value;
415         }
416 
417         /** @brief Get a property variant without mapper lookup. */
418         template <typename Variant>
419         static auto getPropertyVariant(
420             const std::string& service,
421             const std::string& path,
422             const std::string& interface,
423             const std::string& property)
424         {
425             return getPropertyVariant<Variant>(
426                     getBus(),
427                     service,
428                     path,
429                     interface,
430                     property);
431         }
432 
433         /** @brief Set a property with mapper lookup. */
434         template <typename Property>
435         static void setProperty(
436             sdbusplus::bus::bus& bus,
437             const std::string& path,
438             const std::string& interface,
439             const std::string& property,
440             Property&& value)
441         {
442             using namespace std::literals::string_literals;
443 
444             sdbusplus::message::variant<Property> varValue(
445                     std::forward<Property>(value));
446 
447             callMethod(
448                     bus,
449                     getService(bus, path, interface),
450                     path,
451                     "org.freedesktop.DBus.Properties"s,
452                     "Set"s,
453                     interface,
454                     property,
455                     varValue);
456         }
457 
458         /** @brief Set a property with mapper lookup. */
459         template <typename Property>
460         static void setProperty(
461             const std::string& path,
462             const std::string& interface,
463             const std::string& property,
464             Property&& value)
465         {
466             return setProperty(
467                     getBus(),
468                     path,
469                     interface,
470                     property,
471                     std::forward<Property>(value));
472         }
473 
474         /** @brief Invoke method with mapper lookup. */
475         template <typename ...Args>
476         static auto lookupAndCallMethod(
477             sdbusplus::bus::bus& bus,
478             const std::string& path,
479             const std::string& interface,
480             const std::string& method,
481             Args&& ... args)
482         {
483             return callMethod(
484                     bus,
485                     getService(bus, path, interface),
486                     path,
487                     interface,
488                     method,
489                     std::forward<Args>(args)...);
490         }
491 
492         /** @brief Invoke method with mapper lookup. */
493         template <typename ...Args>
494         static auto lookupAndCallMethod(
495             const std::string& path,
496             const std::string& interface,
497             const std::string& method,
498             Args&& ... args)
499         {
500             return lookupAndCallMethod(
501                     getBus(),
502                     path,
503                     interface,
504                     method,
505                     std::forward<Args>(args)...);
506         }
507 
508         /** @brief Invoke method and read with mapper lookup. */
509         template <typename Ret, typename ...Args>
510         static auto lookupCallMethodAndRead(
511             sdbusplus::bus::bus& bus,
512             const std::string& path,
513             const std::string& interface,
514             const std::string& method,
515             Args&& ... args)
516         {
517             return callMethodAndRead(
518                     bus,
519                     getService(bus, path, interface),
520                     path,
521                     interface,
522                     method,
523                     std::forward<Args>(args)...);
524         }
525 
526         /** @brief Invoke method and read with mapper lookup. */
527         template <typename Ret, typename ...Args>
528         static auto lookupCallMethodAndRead(
529             const std::string& path,
530             const std::string& interface,
531             const std::string& method,
532             Args&& ... args)
533         {
534             return lookupCallMethodAndRead<Ret>(
535                     getBus(),
536                     path,
537                     interface,
538                     method,
539                     std::forward<Args>(args)...);
540         }
541 };
542 
543 } // namespace util
544 } // namespace fan
545 } // namespace phosphor
546