1 #pragma once
2 
3 #include "data_types.hpp"
4 
5 #include <sdbusplus/bus.hpp>
6 #include <sdbusplus/bus/match.hpp>
7 #include <sdbusplus/exception.hpp>
8 #include <sdbusplus/message.hpp>
9 
10 #include <string>
11 
12 struct Loop;
13 
14 namespace phosphor
15 {
16 namespace dbus
17 {
18 namespace monitoring
19 {
20 
21 /** @class SDBusPlus
22  *  @brief DBus access delegate implementation for sdbusplus.
23  */
24 class SDBusPlus
25 {
26   private:
getWatches()27     static auto& getWatches()
28     {
29         static std::vector<sdbusplus::bus::match_t> watches;
30         return watches;
31     }
32 
33   public:
getBus()34     static auto& getBus()
35     {
36         static auto bus = sdbusplus::bus::new_default();
37         return bus;
38     }
39 
40     /** @brief Invoke a method; ignore reply. */
41     template <typename... Args>
callMethodNoReply(const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)42     static void callMethodNoReply(
43         const std::string& busName, const std::string& path,
44         const std::string& interface, const std::string& method, Args&&... args)
45     {
46         auto reqMsg = getBus().new_method_call(
47             busName.c_str(), path.c_str(), interface.c_str(), method.c_str());
48         reqMsg.append(std::forward<Args>(args)...);
49         getBus().call_noreply(reqMsg);
50 
51         // TODO: openbmc/openbmc#1719
52         // invoke these methods async, with a callback
53         // handler that checks for errors and logs.
54     }
55 
56     /** @brief Invoke a method. */
57     template <typename... Args>
callMethod(const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)58     static auto callMethod(const std::string& busName, const std::string& path,
59                            const std::string& interface,
60                            const std::string& method, Args&&... args)
61     {
62         auto reqMsg = getBus().new_method_call(
63             busName.c_str(), path.c_str(), interface.c_str(), method.c_str());
64         reqMsg.append(std::forward<Args>(args)...);
65         return getBus().call(reqMsg);
66     }
67 
68     /** @brief Invoke a method and read the response. */
69     template <typename Ret, typename... Args>
callMethodAndRead(const std::string & busName,const std::string & path,const std::string & interface,const std::string & method,Args &&...args)70     static auto callMethodAndRead(
71         const std::string& busName, const std::string& path,
72         const std::string& interface, const std::string& method, Args&&... args)
73     {
74         Ret resp;
75         sdbusplus::message_t respMsg = callMethod<Args...>(
76             busName, path, interface, method, std::forward<Args>(args)...);
77         try
78         {
79             respMsg.read(resp);
80         }
81         catch (const sdbusplus::exception_t& e)
82         {
83             // Empty responses are expected sometimes, and the calling
84             // code is set up to handle it.
85         }
86         return resp;
87     }
88 
89     /** @brief Register a DBus signal callback. */
addMatch(const std::string & match,sdbusplus::bus::match_t::callback_t && callback)90     static auto addMatch(const std::string& match,
91                          sdbusplus::bus::match_t::callback_t&& callback)
92     {
93         getWatches().emplace_back(getBus(), match, std::move(callback));
94     }
95 
96     /** @brief Look up the bus name for a path and interface */
getBusName(const std::string & path,const std::string & interface)97     static auto getBusName(const std::string& path,
98                            const std::string& interface)
99     {
100         std::vector<std::string> interfaces{interface};
101         std::string name;
102 
103         try
104         {
105             auto object = callMethodAndRead<GetObject>(
106                 MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, "GetObject",
107                 path, interfaces);
108 
109             if (!object.empty())
110             {
111                 name = object.begin()->first;
112             }
113         }
114         catch (const sdbusplus::exception_t& e)
115         {
116             // Empty responses are expected sometimes, and the calling
117             // code is set up to handle it.
118         }
119         return name;
120     }
121 
122     friend Loop;
123 };
124 
125 } // namespace monitoring
126 } // namespace dbus
127 } // namespace phosphor
128