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