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