1 #include "config.h"
2
3 #include "utils.hpp"
4
5 #include <openssl/evp.h>
6
7 #include <phosphor-logging/log.hpp>
8
9 #include <algorithm>
10 #include <fstream>
11 #include <sstream>
12
13 using namespace phosphor::logging;
14
15 namespace utils
16 {
17
18 namespace // anonymous
19 {
20 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
21 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
22 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
23 } // namespace
24
25 namespace internal
26 {
27 template <typename... Ts>
concat_string(const Ts &...ts)28 std::string concat_string(const Ts&... ts)
29 {
30 std::stringstream s;
31 ((s << ts << " "), ...) << std::endl;
32 return s.str();
33 }
34
35 // Helper function to run command
36 // Returns return code and the stdout
37 template <typename... Ts>
exec(const Ts &...ts)38 std::pair<int, std::string> exec(const Ts&... ts)
39 {
40 std::array<char, 512> buffer;
41 std::string cmd = concat_string(ts...);
42 std::stringstream result;
43 int rc;
44 FILE* pipe = popen(cmd.c_str(), "r");
45 if (!pipe)
46 {
47 throw std::runtime_error("popen() failed!");
48 }
49 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
50 {
51 result << buffer.data();
52 }
53 rc = pclose(pipe);
54 return {rc, result.str()};
55 }
56
57 } // namespace internal
getUtils()58 const UtilsInterface& getUtils()
59 {
60 static Utils utils;
61 return utils;
62 }
63
getPSUInventoryPath(sdbusplus::bus_t & bus) const64 std::vector<std::string> Utils::getPSUInventoryPath(sdbusplus::bus_t& bus) const
65 {
66 std::vector<std::string> paths;
67 try
68 {
69 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
70 MAPPER_INTERFACE, "GetSubTreePaths");
71 method.append(PSU_INVENTORY_PATH_BASE);
72 method.append(0); // Depth 0 to search all
73 method.append(std::vector<std::string>({PSU_INVENTORY_IFACE}));
74 auto reply = bus.call(method);
75
76 reply.read(paths);
77 }
78 catch (const sdbusplus::exception_t&)
79 {
80 // Inventory base path not there yet.
81 }
82 return paths;
83 }
84
getService(sdbusplus::bus_t & bus,const char * path,const char * interface) const85 std::string Utils::getService(sdbusplus::bus_t& bus, const char* path,
86 const char* interface) const
87 {
88 auto services = getServices(bus, path, interface);
89 if (services.empty())
90 {
91 return {};
92 }
93 return services[0];
94 }
95
getServices(sdbusplus::bus_t & bus,const char * path,const char * interface) const96 std::vector<std::string> Utils::getServices(
97 sdbusplus::bus_t& bus, const char* path, const char* interface) const
98 {
99 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
100 MAPPER_INTERFACE, "GetObject");
101
102 mapper.append(path, std::vector<std::string>({interface}));
103 try
104 {
105 auto mapperResponseMsg = bus.call(mapper);
106
107 std::vector<std::pair<std::string, std::vector<std::string>>>
108 mapperResponse;
109 mapperResponseMsg.read(mapperResponse);
110 if (mapperResponse.empty())
111 {
112 log<level::ERR>("Error reading mapper response");
113 throw std::runtime_error("Error reading mapper response");
114 }
115 std::vector<std::string> ret;
116 ret.reserve(mapperResponse.size());
117 for (const auto& i : mapperResponse)
118 {
119 ret.emplace_back(i.first);
120 }
121 return ret;
122 }
123 catch (const sdbusplus::exception_t& ex)
124 {
125 log<level::ERR>("GetObject call failed", entry("PATH=%s", path),
126 entry("INTERFACE=%s", interface));
127 throw std::runtime_error("GetObject call failed");
128 }
129 }
130
getVersionId(const std::string & version) const131 std::string Utils::getVersionId(const std::string& version) const
132 {
133 if (version.empty())
134 {
135 log<level::ERR>("Error version is empty");
136 return {};
137 }
138
139 using EVP_MD_CTX_Ptr =
140 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
141
142 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
143 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free);
144
145 EVP_DigestInit(ctx.get(), EVP_sha512());
146 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str()));
147 EVP_DigestFinal(ctx.get(), digest.data(), nullptr);
148
149 // Only need 8 hex digits.
150 char mdString[9];
151 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x",
152 (unsigned int)digest[0], (unsigned int)digest[1],
153 (unsigned int)digest[2], (unsigned int)digest[3]);
154
155 return mdString;
156 }
157
getVersion(const std::string & inventoryPath) const158 std::string Utils::getVersion(const std::string& inventoryPath) const
159 {
160 // Invoke vendor-specify tool to get the version string, e.g.
161 // psutils get-version
162 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
163 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
164 return (rc == 0) ? r : "";
165 }
166
getLatestVersion(const std::set<std::string> & versions) const167 std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
168 {
169 if (versions.empty())
170 {
171 return {};
172 }
173 std::stringstream args;
174 for (const auto& s : versions)
175 {
176 args << s << " ";
177 }
178 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
179 return (rc == 0) ? r : "";
180 }
181
isAssociated(const std::string & psuInventoryPath,const AssociationList & assocs) const182 bool Utils::isAssociated(const std::string& psuInventoryPath,
183 const AssociationList& assocs) const
184 {
185 return std::find_if(assocs.begin(), assocs.end(),
186 [&psuInventoryPath](const auto& assoc) {
187 return psuInventoryPath == std::get<2>(assoc);
188 }) != assocs.end();
189 }
190
getPropertyImpl(sdbusplus::bus_t & bus,const char * service,const char * path,const char * interface,const char * propertyName) const191 any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service,
192 const char* path, const char* interface,
193 const char* propertyName) const
194 {
195 auto method = bus.new_method_call(service, path,
196 "org.freedesktop.DBus.Properties", "Get");
197 method.append(interface, propertyName);
198 try
199 {
200 PropertyType value{};
201 auto reply = bus.call(method);
202 reply.read(value);
203 return any(value);
204 }
205 catch (const sdbusplus::exception_t& ex)
206 {
207 log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
208 entry("INTERFACE=%s", interface),
209 entry("PROPERTY=%s", propertyName));
210 throw std::runtime_error("GetProperty call failed");
211 }
212 }
213
214 } // namespace utils
215