blob: 06265430f03c155ff973070515c8ca12f5ab4e6f [file] [log] [blame]
Lei YU5e0dcb32019-08-02 18:04:34 +08001#include "config.h"
2
3#include "utils.hpp"
4
George Liu0b7c7b32022-02-07 16:30:23 +08005#include <openssl/evp.h>
Lei YUad90ad52019-08-06 11:19:28 +08006
Lei YU4b9ac392019-10-12 11:02:26 +08007#include <algorithm>
Lei YU5e0dcb32019-08-02 18:04:34 +08008#include <fstream>
Lei YUf77189f2019-08-07 14:26:30 +08009#include <phosphor-logging/log.hpp>
Lei YU5f3584d2019-08-27 16:28:53 +080010#include <sstream>
Lei YUf77189f2019-08-07 14:26:30 +080011
12using namespace phosphor::logging;
Lei YU5e0dcb32019-08-02 18:04:34 +080013
14namespace utils
15{
16
17namespace // anonymous
18{
19constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
20constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
21constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
22} // namespace
23
Lei YU5f3584d2019-08-27 16:28:53 +080024namespace internal
25{
26template <typename... Ts>
27std::string concat_string(Ts const&... ts)
28{
29 std::stringstream s;
30 ((s << ts << " "), ...) << std::endl;
31 return s.str();
32}
33
34// Helper function to run command
35// Returns return code and the stdout
36template <typename... Ts>
37std::pair<int, std::string> exec(Ts const&... ts)
38{
39 std::array<char, 512> buffer;
40 std::string cmd = concat_string(ts...);
41 std::stringstream result;
42 int rc;
43 FILE* pipe = popen(cmd.c_str(), "r");
44 if (!pipe)
45 {
46 throw std::runtime_error("popen() failed!");
47 }
48 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
49 {
50 result << buffer.data();
51 }
52 rc = pclose(pipe);
53 return {rc, result.str()};
54}
55
56} // namespace internal
Lei YUf77189f2019-08-07 14:26:30 +080057const UtilsInterface& getUtils()
58{
59 static Utils utils;
60 return utils;
61}
62
Patrick Williams374fae52022-07-22 19:26:55 -050063std::vector<std::string> Utils::getPSUInventoryPath(sdbusplus::bus_t& bus) const
Lei YU5e0dcb32019-08-02 18:04:34 +080064{
65 std::vector<std::string> paths;
66 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
67 MAPPER_INTERFACE, "GetSubTreePaths");
68 method.append(PSU_INVENTORY_PATH_BASE);
69 method.append(0); // Depth 0 to search all
70 method.append(std::vector<std::string>({PSU_INVENTORY_IFACE}));
71 auto reply = bus.call(method);
72
73 reply.read(paths);
74 return paths;
75}
76
Patrick Williams374fae52022-07-22 19:26:55 -050077std::string Utils::getService(sdbusplus::bus_t& bus, const char* path,
Lei YUf77189f2019-08-07 14:26:30 +080078 const char* interface) const
Lei YUad90ad52019-08-06 11:19:28 +080079{
Lei YUd0bbfa92019-09-11 16:10:54 +080080 auto services = getServices(bus, path, interface);
81 if (services.empty())
82 {
83 return {};
84 }
85 return services[0];
86}
87
Patrick Williams374fae52022-07-22 19:26:55 -050088std::vector<std::string> Utils::getServices(sdbusplus::bus_t& bus,
Lei YUd0bbfa92019-09-11 16:10:54 +080089 const char* path,
90 const char* interface) const
91{
Lei YUad90ad52019-08-06 11:19:28 +080092 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
93 MAPPER_INTERFACE, "GetObject");
94
95 mapper.append(path, std::vector<std::string>({interface}));
96 try
97 {
98 auto mapperResponseMsg = bus.call(mapper);
99
100 std::vector<std::pair<std::string, std::vector<std::string>>>
101 mapperResponse;
102 mapperResponseMsg.read(mapperResponse);
103 if (mapperResponse.empty())
104 {
105 log<level::ERR>("Error reading mapper response");
106 throw std::runtime_error("Error reading mapper response");
107 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800108 std::vector<std::string> ret;
109 for (const auto& i : mapperResponse)
Lei YUad90ad52019-08-06 11:19:28 +0800110 {
Lei YUd0bbfa92019-09-11 16:10:54 +0800111 ret.emplace_back(i.first);
Lei YUad90ad52019-08-06 11:19:28 +0800112 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800113 return ret;
Lei YUad90ad52019-08-06 11:19:28 +0800114 }
Patrick Williams374fae52022-07-22 19:26:55 -0500115 catch (const sdbusplus::exception_t& ex)
Lei YUad90ad52019-08-06 11:19:28 +0800116 {
Lei YU2d156252019-10-18 10:02:20 +0800117 log<level::ERR>("GetObject call failed", entry("PATH=%s", path),
Lei YUad90ad52019-08-06 11:19:28 +0800118 entry("INTERFACE=%s", interface));
Lei YU2d156252019-10-18 10:02:20 +0800119 throw std::runtime_error("GetObject call failed");
Lei YUad90ad52019-08-06 11:19:28 +0800120 }
121}
122
Lei YUf77189f2019-08-07 14:26:30 +0800123std::string Utils::getVersionId(const std::string& version) const
Lei YUad90ad52019-08-06 11:19:28 +0800124{
125 if (version.empty())
126 {
127 log<level::ERR>("Error version is empty");
128 return {};
129 }
130
George Liu0b7c7b32022-02-07 16:30:23 +0800131 using EVP_MD_CTX_Ptr =
132 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
133
134 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
135 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free);
136
137 EVP_DigestInit(ctx.get(), EVP_sha512());
138 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str()));
139 EVP_DigestFinal(ctx.get(), digest.data(), nullptr);
Lei YUad90ad52019-08-06 11:19:28 +0800140
141 // Only need 8 hex digits.
George Liu0b7c7b32022-02-07 16:30:23 +0800142 char mdString[9];
143 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x",
144 (unsigned int)digest[0], (unsigned int)digest[1],
145 (unsigned int)digest[2], (unsigned int)digest[3]);
146
147 return mdString;
Lei YUad90ad52019-08-06 11:19:28 +0800148}
149
Lei YU5f3584d2019-08-27 16:28:53 +0800150std::string Utils::getVersion(const std::string& inventoryPath) const
151{
152 // Invoke vendor-specify tool to get the version string, e.g.
153 // psutils get-version
154 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
155 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
156 return (rc == 0) ? r : "";
157}
158
Lei YU65207482019-10-11 16:39:36 +0800159std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
160{
161 if (versions.empty())
162 {
163 return {};
164 }
165 std::stringstream args;
166 for (const auto& s : versions)
167 {
168 args << s << " ";
169 }
170 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
171 return (rc == 0) ? r : "";
172}
173
Lei YU4b9ac392019-10-12 11:02:26 +0800174bool Utils::isAssociated(const std::string& psuInventoryPath,
175 const AssociationList& assocs) const
176{
177 return std::find_if(assocs.begin(), assocs.end(),
178 [&psuInventoryPath](const auto& assoc) {
179 return psuInventoryPath == std::get<2>(assoc);
180 }) != assocs.end();
181}
182
Patrick Williams374fae52022-07-22 19:26:55 -0500183any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service,
Lei YUf77189f2019-08-07 14:26:30 +0800184 const char* path, const char* interface,
185 const char* propertyName) const
186{
187 auto method = bus.new_method_call(service, path,
188 "org.freedesktop.DBus.Properties", "Get");
189 method.append(interface, propertyName);
190 try
191 {
192 PropertyType value{};
193 auto reply = bus.call(method);
194 reply.read(value);
195 return any(value);
196 }
Patrick Williams374fae52022-07-22 19:26:55 -0500197 catch (const sdbusplus::exception_t& ex)
Lei YUf77189f2019-08-07 14:26:30 +0800198 {
199 log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
200 entry("INTERFACE=%s", interface),
201 entry("PROPERTY=%s", propertyName));
202 throw std::runtime_error("GetProperty call failed");
203 }
204}
205
Lei YU5e0dcb32019-08-02 18:04:34 +0800206} // namespace utils