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 /** @brief Alias for PropertiesChanged signal callbacks. */
23 template <typename ...T>
24 using Properties = std::map<std::string, sdbusplus::message::variant<T...>>;
25 
26 /** @class SDBusPlus
27  *  @brief DBus access delegate implementation for sdbusplus.
28  */
29 class SDBusPlus
30 {
31 
32     public:
33         /** @brief Get the bus connection. */
34         static auto& getBus() __attribute__((pure))
35         {
36             static auto bus = sdbusplus::bus::new_default();
37             return bus;
38         }
39 
40         /** @brief Invoke a method. */
41         template <typename ...Args>
42         static auto callMethod(
43             const std::string& busName,
44             const std::string& path,
45             const std::string& interface,
46             const std::string& method,
47             Args&& ... args)
48         {
49             auto reqMsg = getBus().new_method_call(
50                               busName.c_str(),
51                               path.c_str(),
52                               interface.c_str(),
53                               method.c_str());
54             reqMsg.append(std::forward<Args>(args)...);
55             auto respMsg = getBus().call(reqMsg);
56 
57             if (respMsg.is_method_error())
58             {
59                 phosphor::logging::log<phosphor::logging::level::INFO>(
60                         "Failed to invoke DBus method.",
61                         phosphor::logging::entry("PATH=%s", path.c_str()),
62                         phosphor::logging::entry(
63                             "INTERFACE=%s", interface.c_str()),
64                         phosphor::logging::entry("METHOD=%s", method.c_str()));
65                 phosphor::logging::elog<detail::errors::InternalFailure>();
66             }
67 
68             return respMsg;
69         }
70 
71         /** @brief Invoke a method and read the response. */
72         template <typename Ret, typename ...Args>
73         static auto callMethodAndRead(
74             const std::string& busName,
75             const std::string& path,
76             const std::string& interface,
77             const std::string& method,
78             Args&& ... args)
79         {
80             sdbusplus::message::message respMsg =
81                 callMethod<Args...>(
82                     busName,
83                     path,
84                     interface,
85                     method,
86                     std::forward<Args>(args)...);
87             Ret resp;
88             respMsg.read(resp);
89             return resp;
90         }
91 
92         /** @brief Get service from the mapper. */
93         static auto getService(
94             const std::string& path,
95             const std::string& interface)
96         {
97             using namespace std::literals::string_literals;
98             using GetObject = std::map<std::string, std::vector<std::string>>;
99 
100             auto mapperResp = callMethodAndRead<GetObject>(
101                                   "xyz.openbmc_project.ObjectMapper"s,
102                                   "/xyz/openbmc_project/object_mapper"s,
103                                   "xyz.openbmc_project.ObjectMapper"s,
104                                   "GetObject"s,
105                                   path,
106                                   GetObject::mapped_type{interface});
107 
108             if (mapperResp.empty())
109             {
110                 phosphor::logging::log<phosphor::logging::level::INFO>(
111                         "Object not found.",
112                         phosphor::logging::entry("PATH=%s", path.c_str()),
113                         phosphor::logging::entry(
114                             "INTERFACE=%s", interface.c_str()));
115                 phosphor::logging::elog<detail::errors::InternalFailure>();
116             }
117             return mapperResp.begin()->first;
118         }
119 
120         /** @brief Get a property with mapper lookup. */
121         template <typename Property>
122         static auto getProperty(
123             const std::string& path,
124             const std::string& interface,
125             const std::string& property)
126         {
127             using namespace std::literals::string_literals;
128 
129             auto msg = callMethod(
130                            getService(path, interface),
131                            path,
132                            "org.freedesktop.DBus.Properties"s,
133                            "Get"s,
134                            interface,
135                            property);
136             sdbusplus::message::variant<Property> value;
137             msg.read(value);
138             return value.template get<Property>();
139         }
140 
141         /** @brief Set a property with mapper lookup. */
142         template <typename Property>
143         static void setProperty(
144             const std::string& path,
145             const std::string& interface,
146             const std::string& property,
147             Property&& value)
148         {
149             using namespace std::literals::string_literals;
150 
151             sdbusplus::message::variant<Property> varValue(
152                 std::forward<Property>(value));
153 
154             callMethod(
155                 getService(path, interface),
156                 path,
157                 "org.freedesktop.DBus.Properties"s,
158                 "Set"s,
159                 interface,
160                 property,
161                 varValue);
162         }
163 
164         /** @brief Invoke method with mapper lookup. */
165         template <typename ...Args>
166         static auto lookupAndCallMethod(
167             const std::string& path,
168             const std::string& interface,
169             const std::string& method,
170             Args&& ... args)
171         {
172             return callMethod(
173                        getService(path, interface),
174                        path,
175                        interface,
176                        method,
177                        std::forward<Args>(args)...);
178         }
179 
180         /** @brief Invoke method and read with mapper lookup. */
181         template <typename Ret, typename ...Args>
182         static auto lookupCallMethodAndRead(
183             const std::string& path,
184             const std::string& interface,
185             const std::string& method,
186             Args&& ... args)
187         {
188             return callMethodAndRead(
189                        getService(path, interface),
190                        path,
191                        interface,
192                        method,
193                        std::forward<Args>(args)...);
194         }
195 };
196 
197 } // namespace util
198 } // namespace fan
199 } // namespace phosphor
200