1 #pragma once
2 
3 #include <phosphor-logging/elog-errors.hpp>
4 #include <phosphor-logging/elog.hpp>
5 #include <phosphor-logging/log.hpp>
6 #include <sdbusplus/bus.hpp>
7 #include <sdbusplus/message.hpp>
8 #include <string>
9 #include <xyz/openbmc_project/Common/error.hpp>
10 
11 namespace util
12 {
13 namespace detail
14 {
15 namespace errors = sdbusplus::xyz::openbmc_project::Common::Error;
16 } // namespace detail
17 
18 /** @brief Alias for PropertiesChanged signal callbacks. */
19 template <typename... T>
20 using Properties = std::map<std::string, std::variant<T...>>;
21 
22 namespace sdbusplus
23 {
24 
25 /** @brief Get the bus connection. */
26 static auto& getBus() __attribute__((pure));
27 static auto& getBus()
28 {
29     static auto bus = ::sdbusplus::bus::new_default();
30     return bus;
31 }
32 
33 /** @brief Invoke a method. */
34 template <typename... Args>
35 static auto callMethod(::sdbusplus::bus::bus& bus, const std::string& busName,
36                        const std::string& path, const std::string& interface,
37                        const std::string& method, Args&&... args)
38 {
39     auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
40                                       interface.c_str(), method.c_str());
41     reqMsg.append(std::forward<Args>(args)...);
42     auto respMsg = bus.call(reqMsg);
43 
44     if (respMsg.is_method_error())
45     {
46         phosphor::logging::log<phosphor::logging::level::INFO>(
47             "Failed to invoke DBus method.",
48             phosphor::logging::entry("PATH=%s", path.c_str()),
49             phosphor::logging::entry("INTERFACE=%s", interface.c_str()),
50             phosphor::logging::entry("METHOD=%s", method.c_str()));
51         phosphor::logging::elog<detail::errors::InternalFailure>();
52     }
53 
54     return respMsg;
55 }
56 
57 /** @brief Invoke a method. */
58 template <typename... Args>
59 static auto callMethod(const std::string& busName, const std::string& path,
60                        const std::string& interface, const std::string& method,
61                        Args&&... args)
62 {
63     return callMethod(getBus(), busName, path, interface, method,
64                       std::forward<Args>(args)...);
65 }
66 
67 /** @brief Invoke a method and read the response. */
68 template <typename Ret, typename... Args>
69 static auto
70     callMethodAndRead(::sdbusplus::bus::bus& bus, const std::string& busName,
71                       const std::string& path, const std::string& interface,
72                       const std::string& method, Args&&... args)
73 {
74     ::sdbusplus::message::message respMsg = callMethod<Args...>(
75         bus, busName, path, interface, method, std::forward<Args>(args)...);
76     Ret resp;
77     respMsg.read(resp);
78     return resp;
79 }
80 
81 /** @brief Invoke a method and read the response. */
82 template <typename Ret, typename... Args>
83 static auto callMethodAndRead(const std::string& busName,
84                               const std::string& path,
85                               const std::string& interface,
86                               const std::string& method, Args&&... args)
87 {
88     return callMethodAndRead<Ret>(getBus(), busName, path, interface, method,
89                                   std::forward<Args>(args)...);
90 }
91 
92 /** @brief Get service from the mapper. */
93 static auto getService(::sdbusplus::bus::bus& bus, const std::string& path,
94                        const std::string& interface)
95 {
96     using namespace std::literals::string_literals;
97     using GetObject = std::map<std::string, std::vector<std::string>>;
98 
99     auto mapperResp = callMethodAndRead<GetObject>(
100         bus, "xyz.openbmc_project.ObjectMapper"s,
101         "/xyz/openbmc_project/object_mapper"s,
102         "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
103         GetObject::mapped_type{interface});
104 
105     if (mapperResp.empty())
106     {
107         phosphor::logging::log<phosphor::logging::level::INFO>(
108             "Object not found.",
109             phosphor::logging::entry("PATH=%s", path.c_str()),
110             phosphor::logging::entry("INTERFACE=%s", interface.c_str()));
111         phosphor::logging::elog<detail::errors::InternalFailure>();
112     }
113     return mapperResp.begin()->first;
114 }
115 
116 /** @brief Get a property without mapper lookup. */
117 template <typename Property>
118 static auto getProperty(::sdbusplus::bus::bus& bus, const std::string& busName,
119                         const std::string& path, const std::string& interface,
120                         const std::string& property)
121 {
122     using namespace std::literals::string_literals;
123 
124     auto msg =
125         callMethod(bus, busName, path, "org.freedesktop.DBus.Properties"s,
126                    "Get"s, interface, property);
127     ::std::variant<Property> value;
128     msg.read(value);
129     return std::get<Property>(value);
130 }
131 
132 /** @brief Get a property without mapper lookup. */
133 template <typename Property>
134 static auto getProperty(const std::string& busName, const std::string& path,
135                         const std::string& interface,
136                         const std::string& property)
137 {
138     return getProperty<Property>(getBus(), busName, path, interface, property);
139 }
140 
141 /** @brief Get a property with mapper lookup. */
142 template <typename Property>
143 static auto getProperty(::sdbusplus::bus::bus& bus, const std::string& path,
144                         const std::string& interface,
145                         const std::string& property)
146 {
147     return getProperty<Property>(bus, getService(bus, path, interface), path,
148                                  interface, property);
149 }
150 
151 /** @brief Get a property with mapper lookup. */
152 template <typename Property>
153 static auto getProperty(const std::string& path, const std::string& interface,
154                         const std::string& property)
155 {
156     return getProperty<Property>(getBus(), path, interface, property);
157 }
158 
159 } // namespace sdbusplus
160 } // namespace util
161