1 #pragma once
2 
3 #include <phosphor-logging/elog-errors.hpp>
4 #include <phosphor-logging/elog.hpp>
5 #include <phosphor-logging/lg2.hpp>
6 #include <sdbusplus/bus.hpp>
7 #include <sdbusplus/exception.hpp>
8 #include <sdbusplus/message.hpp>
9 #include <xyz/openbmc_project/Common/error.hpp>
10 
11 #include <string>
12 
13 namespace util
14 {
15 namespace detail
16 {
17 namespace errors = sdbusplus::xyz::openbmc_project::Common::Error;
18 } // namespace detail
19 
20 /** @brief Alias for PropertiesChanged signal callbacks. */
21 template <typename... T>
22 using Properties = std::map<std::string, std::variant<T...>>;
23 
24 namespace sdbusplus
25 {
26 
27 /** @brief Get the bus connection. */
28 static auto& getBus() __attribute__((pure));
29 static auto& getBus()
30 {
31     static auto bus = ::sdbusplus::bus::new_default();
32     return bus;
33 }
34 
35 /** @brief Invoke a method. */
36 template <typename... Args>
37 static auto callMethod(::sdbusplus::bus_t& bus, const std::string& busName,
38                        const std::string& path, const std::string& interface,
39                        const std::string& method, Args&&... args)
40 {
41     auto reqMsg = bus.new_method_call(busName.c_str(), path.c_str(),
42                                       interface.c_str(), method.c_str());
43     reqMsg.append(std::forward<Args>(args)...);
44     try
45     {
46         return bus.call(reqMsg);
47     }
48     catch (const std::exception& e)
49     {
50         lg2::error("Failed to invoke DBus method: {PATH}, {INTF}, {METHOD}",
51                    "PATH", path, "INTF", interface, "METHOD", method);
52         phosphor::logging::elog<detail::errors::InternalFailure>();
53     }
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_t& 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_t 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_t& 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         lg2::error("Object not found. {PATH}, {INTF}", "PATH", path, "INTF",
107                    interface);
108         phosphor::logging::elog<detail::errors::InternalFailure>();
109     }
110     return mapperResp.begin()->first;
111 }
112 
113 /** @brief Get a property without mapper lookup. */
114 template <typename Property>
115 static auto getProperty(::sdbusplus::bus_t& bus, const std::string& busName,
116                         const std::string& path, const std::string& interface,
117                         const std::string& property)
118 {
119     using namespace std::literals::string_literals;
120 
121     auto msg =
122         callMethod(bus, busName, path, "org.freedesktop.DBus.Properties"s,
123                    "Get"s, interface, property);
124     ::std::variant<Property> value;
125     msg.read(value);
126     return std::get<Property>(value);
127 }
128 
129 /** @brief Get a property without mapper lookup. */
130 template <typename Property>
131 static auto getProperty(const std::string& busName, const std::string& path,
132                         const std::string& interface,
133                         const std::string& property)
134 {
135     return getProperty<Property>(getBus(), busName, path, interface, property);
136 }
137 
138 /** @brief Get a property with mapper lookup. */
139 template <typename Property>
140 static auto getProperty(::sdbusplus::bus_t& bus, const std::string& path,
141                         const std::string& interface,
142                         const std::string& property)
143 {
144     return getProperty<Property>(bus, getService(bus, path, interface), path,
145                                  interface, property);
146 }
147 
148 /** @brief Get a property with mapper lookup. */
149 template <typename Property>
150 static auto getProperty(const std::string& path, const std::string& interface,
151                         const std::string& property)
152 {
153     return getProperty<Property>(getBus(), path, interface, property);
154 }
155 
156 } // namespace sdbusplus
157 } // namespace util
158