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 /**
91  * @class DBusPropertyError
92  *
93  * Thrown when a set/get property fails.
94  */
95 class DBusPropertyError : public DBusError
96 {
97     public:
98         DBusPropertyError(
99                 const char* msg,
100                 const std::string& busName,
101                 const std::string& path,
102                 const std::string& interface,
103                 const std::string& property) :
104             DBusError(msg),
105             busName(busName),
106             path(path),
107             interface(interface),
108             property(property)
109             {
110             }
111 
112         const std::string busName;
113         const std::string path;
114         const std::string interface;
115         const std::string property;
116 };
117 
118 /** @brief Alias for PropertiesChanged signal callbacks. */
119 template <typename ...T>
120 using Properties = std::map<std::string, sdbusplus::message::variant<T...>>;
121 
122 /** @class SDBusPlus
123  *  @brief DBus access delegate implementation for sdbusplus.
124  */
125 class SDBusPlus
126 {
127 
128     public:
129         /** @brief Get the bus connection. */
130         static auto& getBus() __attribute__((pure))
131         {
132             static auto bus = sdbusplus::bus::new_default();
133             return bus;
134         }
135 
136         /** @brief Invoke a method. */
137         template <typename ...Args>
138         static auto callMethod(
139             sdbusplus::bus::bus& bus,
140             const std::string& busName,
141             const std::string& path,
142             const std::string& interface,
143             const std::string& method,
144             Args&& ... args)
145         {
146             auto reqMsg = bus.new_method_call(
147                     busName.c_str(),
148                     path.c_str(),
149                     interface.c_str(),
150                     method.c_str());
151             reqMsg.append(std::forward<Args>(args)...);
152             auto respMsg = bus.call(reqMsg);
153 
154             if (respMsg.is_method_error())
155             {
156                 throw DBusMethodError{busName, path, interface, method};
157             }
158 
159             return respMsg;
160         }
161 
162         /** @brief Invoke a method. */
163         template <typename ...Args>
164         static auto callMethod(
165             const std::string& busName,
166             const std::string& path,
167             const std::string& interface,
168             const std::string& method,
169             Args&& ... args)
170         {
171             return callMethod(
172                     getBus(),
173                     busName,
174                     path,
175                     interface,
176                     method,
177                     std::forward<Args>(args)...);
178         }
179 
180         /** @brief Invoke a method and read the response. */
181         template <typename Ret, typename ...Args>
182         static auto callMethodAndRead(
183             sdbusplus::bus::bus& bus,
184             const std::string& busName,
185             const std::string& path,
186             const std::string& interface,
187             const std::string& method,
188             Args&& ... args)
189         {
190             sdbusplus::message::message respMsg =
191                     callMethod<Args...>(
192                             bus,
193                             busName,
194                             path,
195                             interface,
196                             method,
197                             std::forward<Args>(args)...);
198             Ret resp;
199             respMsg.read(resp);
200             return resp;
201         }
202 
203         /** @brief Invoke a method and read the response. */
204         template <typename Ret, typename ...Args>
205         static auto callMethodAndRead(
206             const std::string& busName,
207             const std::string& path,
208             const std::string& interface,
209             const std::string& method,
210             Args&& ... args)
211         {
212             return callMethodAndRead<Ret>(
213                     getBus(),
214                     busName,
215                     path,
216                     interface,
217                     method,
218                     std::forward<Args>(args)...);
219         }
220 
221         /** @brief Get subtree from the mapper. */
222         static auto getSubTree(
223             sdbusplus::bus::bus& bus,
224             const std::string& path,
225             const std::string& interface,
226             int32_t depth)
227         {
228             using namespace std::literals::string_literals;
229 
230             using Path = std::string;
231             using Intf = std::string;
232             using Serv = std::string;
233             using Intfs = std::vector<Intf>;
234             using Objects = std::map<Path, std::map<Serv, Intfs>>;
235             Intfs intfs = {interface};
236 
237             auto mapperResp = callMethodAndRead<Objects>(
238                     bus,
239                     "xyz.openbmc_project.ObjectMapper"s,
240                     "/xyz/openbmc_project/object_mapper"s,
241                     "xyz.openbmc_project.ObjectMapper"s,
242                     "GetSubTree"s,
243                     path,
244                     depth,
245                     intfs);
246 
247             if (mapperResp.empty())
248             {
249                 phosphor::logging::log<phosphor::logging::level::ERR>(
250                         "Empty response from mapper GetSubTree",
251                         phosphor::logging::entry("SUBTREE=%s", path.c_str()),
252                         phosphor::logging::entry(
253                                 "INTERFACE=%s", interface.c_str()),
254                         phosphor::logging::entry("DEPTH=%u", depth));
255                 phosphor::logging::elog<detail::errors::InternalFailure>();
256             }
257             return mapperResp;
258         }
259 
260         /** @brief Get service from the mapper. */
261         static auto getService(
262             sdbusplus::bus::bus& bus,
263             const std::string& path,
264             const std::string& interface)
265         {
266             using namespace std::literals::string_literals;
267             using GetObject = std::map<std::string, std::vector<std::string>>;
268 
269             try
270             {
271                 auto mapperResp = callMethodAndRead<GetObject>(
272                         bus,
273                         "xyz.openbmc_project.ObjectMapper"s,
274                         "/xyz/openbmc_project/object_mapper"s,
275                         "xyz.openbmc_project.ObjectMapper"s,
276                         "GetObject"s,
277                         path,
278                         GetObject::mapped_type{interface});
279 
280                 if (mapperResp.empty())
281                 {
282                     //Should never happen.  A missing object would fail
283                     //in callMethodAndRead()
284                     phosphor::logging::log<phosphor::logging::level::ERR>(
285                             "Empty mapper response on service lookup");
286                     throw DBusServiceError{path, interface};
287                 }
288                 return mapperResp.begin()->first;
289             }
290             catch (DBusMethodError& e)
291             {
292                 throw DBusServiceError{path, interface};
293             }
294         }
295 
296         /** @brief Get service from the mapper. */
297         static auto getService(
298             const std::string& path,
299             const std::string& interface)
300         {
301             return getService(
302                     getBus(),
303                     path,
304                     interface);
305         }
306 
307         /** @brief Get a property with mapper lookup. */
308         template <typename Property>
309         static auto getProperty(
310             sdbusplus::bus::bus& bus,
311             const std::string& path,
312             const std::string& interface,
313             const std::string& property)
314         {
315             using namespace std::literals::string_literals;
316 
317             auto service = getService(bus, path, interface);
318             auto msg = callMethod(
319                     bus,
320                     service,
321                     path,
322                     "org.freedesktop.DBus.Properties"s,
323                     "Get"s,
324                     interface,
325                     property);
326             if (msg.is_method_error())
327             {
328                 throw DBusPropertyError{
329                         "DBus get property failed",
330                         service,
331                         path,
332                         interface,
333                         property};
334             }
335             sdbusplus::message::variant<Property> value;
336             msg.read(value);
337             return value.template get<Property>();
338         }
339 
340         /** @brief Get a property with mapper lookup. */
341         template <typename Property>
342         static auto getProperty(
343             const std::string& path,
344             const std::string& interface,
345             const std::string& property)
346         {
347             return getProperty<Property>(
348                     getBus(),
349                     path,
350                     interface,
351                     property);
352         }
353 
354         /** @brief Get a property variant with mapper lookup. */
355         template <typename Variant>
356         static auto getPropertyVariant(
357             sdbusplus::bus::bus& bus,
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 service = getService(bus, path, interface);
365             auto msg = callMethod(
366                     bus,
367                     service,
368                     path,
369                     "org.freedesktop.DBus.Properties"s,
370                     "Get"s,
371                     interface,
372                     property);
373             if (msg.is_method_error())
374             {
375                 throw DBusPropertyError{
376                         "DBus get property variant failed",
377                         service,
378                         path,
379                         interface,
380                         property};
381             }
382             Variant value;
383             msg.read(value);
384             return value;
385         }
386 
387         /** @brief Get a property variant with mapper lookup. */
388         template <typename Variant>
389         static auto getPropertyVariant(
390             const std::string& path,
391             const std::string& interface,
392             const std::string& property)
393         {
394             return getPropertyVariant<Variant>(
395                     getBus(),
396                     path,
397                     interface,
398                     property);
399         }
400 
401         /** @brief Get a property without mapper lookup. */
402         template <typename Property>
403         static auto getProperty(
404             sdbusplus::bus::bus& bus,
405             const std::string& service,
406             const std::string& path,
407             const std::string& interface,
408             const std::string& property)
409         {
410             using namespace std::literals::string_literals;
411 
412             auto msg = callMethodAndReturn(
413                     bus,
414                     service,
415                     path,
416                     "org.freedesktop.DBus.Properties"s,
417                     "Get"s,
418                     interface,
419                     property);
420             if (msg.is_method_error())
421             {
422                 throw DBusPropertyError{
423                         "DBus get property failed",
424                         service,
425                         path,
426                         interface,
427                         property};
428             }
429             sdbusplus::message::variant<Property> value;
430             msg.read(value);
431             return value.template get<Property>();
432         }
433 
434         /** @brief Get a property without mapper lookup. */
435         template <typename Property>
436         static auto getProperty(
437             const std::string& service,
438             const std::string& path,
439             const std::string& interface,
440             const std::string& property)
441         {
442             return getProperty<Property>(
443                     getBus(),
444                     service,
445                     path,
446                     interface,
447                     property);
448         }
449 
450         /** @brief Get a property variant without mapper lookup. */
451         template <typename Variant>
452         static auto getPropertyVariant(
453             sdbusplus::bus::bus& bus,
454             const std::string& service,
455             const std::string& path,
456             const std::string& interface,
457             const std::string& property)
458         {
459             using namespace std::literals::string_literals;
460 
461             auto msg = callMethodAndReturn(
462                     bus,
463                     service,
464                     path,
465                     "org.freedesktop.DBus.Properties"s,
466                     "Get"s,
467                     interface,
468                     property);
469             if (msg.is_method_error())
470             {
471                 throw DBusPropertyError{
472                         "DBus get property variant failed",
473                         service,
474                         path,
475                         interface,
476                         property};
477             }
478             Variant value;
479             msg.read(value);
480             return value;
481         }
482 
483         /** @brief Get a property variant without mapper lookup. */
484         template <typename Variant>
485         static auto getPropertyVariant(
486             const std::string& service,
487             const std::string& path,
488             const std::string& interface,
489             const std::string& property)
490         {
491             return getPropertyVariant<Variant>(
492                     getBus(),
493                     service,
494                     path,
495                     interface,
496                     property);
497         }
498 
499         /** @brief Set a property with mapper lookup. */
500         template <typename Property>
501         static void setProperty(
502             sdbusplus::bus::bus& bus,
503             const std::string& path,
504             const std::string& interface,
505             const std::string& property,
506             Property&& value)
507         {
508             using namespace std::literals::string_literals;
509 
510             sdbusplus::message::variant<Property> varValue(
511                     std::forward<Property>(value));
512 
513             auto service = getService(bus, path, interface);
514             auto msg = callMethodAndReturn(
515                     bus,
516                     service,
517                     path,
518                     "org.freedesktop.DBus.Properties"s,
519                     "Set"s,
520                     interface,
521                     property,
522                     varValue);
523             if (msg.is_method_error())
524             {
525                 throw DBusPropertyError{
526                         "DBus set property failed",
527                         service,
528                         path,
529                         interface,
530                         property};
531             }
532         }
533 
534         /** @brief Set a property with mapper lookup. */
535         template <typename Property>
536         static void setProperty(
537             const std::string& path,
538             const std::string& interface,
539             const std::string& property,
540             Property&& value)
541         {
542             return setProperty(
543                     getBus(),
544                     path,
545                     interface,
546                     property,
547                     std::forward<Property>(value));
548         }
549 
550         /** @brief Set a property without mapper lookup. */
551         template <typename Property>
552         static void setProperty(
553             sdbusplus::bus::bus& bus,
554             const std::string& service,
555             const std::string& path,
556             const std::string& interface,
557             const std::string& property,
558             Property&& value)
559         {
560             using namespace std::literals::string_literals;
561 
562             sdbusplus::message::variant<Property> varValue(
563                     std::forward<Property>(value));
564 
565             auto msg = callMethodAndReturn(
566                     bus,
567                     service,
568                     path,
569                     "org.freedesktop.DBus.Properties"s,
570                     "Set"s,
571                     interface,
572                     property,
573                     varValue);
574             if (msg.is_method_error())
575             {
576                 throw DBusPropertyError{
577                         "DBus set property failed",
578                         service,
579                         path,
580                         interface,
581                         property};
582             }
583         }
584 
585         /** @brief Set a property without mapper lookup. */
586         template <typename Property>
587         static void setProperty(
588             const std::string& service,
589             const std::string& path,
590             const std::string& interface,
591             const std::string& property,
592             Property&& value)
593         {
594             return setProperty(
595                     getBus(),
596                     service,
597                     path,
598                     interface,
599                     property,
600                     std::forward<Property>(value));
601         }
602 
603         /** @brief Invoke method with mapper lookup. */
604         template <typename ...Args>
605         static auto lookupAndCallMethod(
606             sdbusplus::bus::bus& bus,
607             const std::string& path,
608             const std::string& interface,
609             const std::string& method,
610             Args&& ... args)
611         {
612             return callMethod(
613                     bus,
614                     getService(bus, path, interface),
615                     path,
616                     interface,
617                     method,
618                     std::forward<Args>(args)...);
619         }
620 
621         /** @brief Invoke method with mapper lookup. */
622         template <typename ...Args>
623         static auto lookupAndCallMethod(
624             const std::string& path,
625             const std::string& interface,
626             const std::string& method,
627             Args&& ... args)
628         {
629             return lookupAndCallMethod(
630                     getBus(),
631                     path,
632                     interface,
633                     method,
634                     std::forward<Args>(args)...);
635         }
636 
637         /** @brief Invoke method and read with mapper lookup. */
638         template <typename Ret, typename ...Args>
639         static auto lookupCallMethodAndRead(
640             sdbusplus::bus::bus& bus,
641             const std::string& path,
642             const std::string& interface,
643             const std::string& method,
644             Args&& ... args)
645         {
646             return callMethodAndRead(
647                     bus,
648                     getService(bus, path, interface),
649                     path,
650                     interface,
651                     method,
652                     std::forward<Args>(args)...);
653         }
654 
655         /** @brief Invoke method and read with mapper lookup. */
656         template <typename Ret, typename ...Args>
657         static auto lookupCallMethodAndRead(
658             const std::string& path,
659             const std::string& interface,
660             const std::string& method,
661             Args&& ... args)
662         {
663             return lookupCallMethodAndRead<Ret>(
664                     getBus(),
665                     path,
666                     interface,
667                     method,
668                     std::forward<Args>(args)...);
669         }
670 
671         /** @brief Invoke a method and return without checking for error. */
672         template <typename ...Args>
673         static auto callMethodAndReturn(
674             sdbusplus::bus::bus& bus,
675             const std::string& busName,
676             const std::string& path,
677             const std::string& interface,
678             const std::string& method,
679             Args&& ... args)
680         {
681             auto reqMsg = bus.new_method_call(
682                     busName.c_str(),
683                     path.c_str(),
684                     interface.c_str(),
685                     method.c_str());
686             reqMsg.append(std::forward<Args>(args)...);
687             auto respMsg = bus.call(reqMsg);
688 
689             return respMsg;
690         }
691 };
692 
693 } // namespace util
694 } // namespace fan
695 } // namespace phosphor
696