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