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