blob: 9d32f3bdb752cbdfe5e532e5b0767caedab34015 [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
Patrick Williams5670b182023-05-10 07:50:50 -05007#include <phosphor-logging/log.hpp>
8
Lei YU4b9ac392019-10-12 11:02:26 +08009#include <algorithm>
Lei YU5e0dcb32019-08-02 18:04:34 +080010#include <fstream>
Lei YU5f3584d2019-08-27 16:28:53 +080011#include <sstream>
Lei YUf77189f2019-08-07 14:26:30 +080012
13using namespace phosphor::logging;
Lei YU5e0dcb32019-08-02 18:04:34 +080014
15namespace utils
16{
17
18namespace // anonymous
19{
20constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
21constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
22constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
23} // namespace
24
Lei YU5f3584d2019-08-27 16:28:53 +080025namespace internal
26{
27template <typename... Ts>
Patrick Williams5670b182023-05-10 07:50:50 -050028std::string concat_string(const Ts&... ts)
Lei YU5f3584d2019-08-27 16:28:53 +080029{
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
37template <typename... Ts>
Patrick Williams5670b182023-05-10 07:50:50 -050038std::pair<int, std::string> exec(const Ts&... ts)
Lei YU5f3584d2019-08-27 16:28:53 +080039{
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
Lei YUf77189f2019-08-07 14:26:30 +080058const UtilsInterface& getUtils()
59{
60 static Utils utils;
61 return utils;
62}
63
Patrick Williams374fae52022-07-22 19:26:55 -050064std::vector<std::string> Utils::getPSUInventoryPath(sdbusplus::bus_t& bus) const
Lei YU5e0dcb32019-08-02 18:04:34 +080065{
66 std::vector<std::string> paths;
Matt Spinlere183edc2024-07-09 11:25:34 -050067 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);
Lei YU5e0dcb32019-08-02 18:04:34 +080075
Matt Spinlere183edc2024-07-09 11:25:34 -050076 reply.read(paths);
77 }
78 catch (const sdbusplus::exception_t&)
79 {
80 // Inventory base path not there yet.
81 }
Lei YU5e0dcb32019-08-02 18:04:34 +080082 return paths;
83}
84
Patrick Williams374fae52022-07-22 19:26:55 -050085std::string Utils::getService(sdbusplus::bus_t& bus, const char* path,
Lei YUf77189f2019-08-07 14:26:30 +080086 const char* interface) const
Lei YUad90ad52019-08-06 11:19:28 +080087{
Lei YUd0bbfa92019-09-11 16:10:54 +080088 auto services = getServices(bus, path, interface);
89 if (services.empty())
90 {
91 return {};
92 }
93 return services[0];
94}
95
Patrick Williamsbab5ed92024-08-16 15:20:54 -040096std::vector<std::string> Utils::getServices(
97 sdbusplus::bus_t& bus, const char* path, const char* interface) const
Lei YUd0bbfa92019-09-11 16:10:54 +080098{
Lei YUad90ad52019-08-06 11:19:28 +080099 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 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800115 std::vector<std::string> ret;
116 for (const auto& i : mapperResponse)
Lei YUad90ad52019-08-06 11:19:28 +0800117 {
Lei YUd0bbfa92019-09-11 16:10:54 +0800118 ret.emplace_back(i.first);
Lei YUad90ad52019-08-06 11:19:28 +0800119 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800120 return ret;
Lei YUad90ad52019-08-06 11:19:28 +0800121 }
Patrick Williams374fae52022-07-22 19:26:55 -0500122 catch (const sdbusplus::exception_t& ex)
Lei YUad90ad52019-08-06 11:19:28 +0800123 {
Lei YU2d156252019-10-18 10:02:20 +0800124 log<level::ERR>("GetObject call failed", entry("PATH=%s", path),
Lei YUad90ad52019-08-06 11:19:28 +0800125 entry("INTERFACE=%s", interface));
Lei YU2d156252019-10-18 10:02:20 +0800126 throw std::runtime_error("GetObject call failed");
Lei YUad90ad52019-08-06 11:19:28 +0800127 }
128}
129
Lei YUf77189f2019-08-07 14:26:30 +0800130std::string Utils::getVersionId(const std::string& version) const
Lei YUad90ad52019-08-06 11:19:28 +0800131{
132 if (version.empty())
133 {
134 log<level::ERR>("Error version is empty");
135 return {};
136 }
137
George Liu0b7c7b32022-02-07 16:30:23 +0800138 using EVP_MD_CTX_Ptr =
139 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
140
141 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
142 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free);
143
144 EVP_DigestInit(ctx.get(), EVP_sha512());
145 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str()));
146 EVP_DigestFinal(ctx.get(), digest.data(), nullptr);
Lei YUad90ad52019-08-06 11:19:28 +0800147
148 // Only need 8 hex digits.
George Liu0b7c7b32022-02-07 16:30:23 +0800149 char mdString[9];
150 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x",
151 (unsigned int)digest[0], (unsigned int)digest[1],
152 (unsigned int)digest[2], (unsigned int)digest[3]);
153
154 return mdString;
Lei YUad90ad52019-08-06 11:19:28 +0800155}
156
Lei YU5f3584d2019-08-27 16:28:53 +0800157std::string Utils::getVersion(const std::string& inventoryPath) const
158{
159 // Invoke vendor-specify tool to get the version string, e.g.
160 // psutils get-version
161 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
162 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
163 return (rc == 0) ? r : "";
164}
165
Lei YU65207482019-10-11 16:39:36 +0800166std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
167{
168 if (versions.empty())
169 {
170 return {};
171 }
172 std::stringstream args;
173 for (const auto& s : versions)
174 {
175 args << s << " ";
176 }
177 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
178 return (rc == 0) ? r : "";
179}
180
Lei YU4b9ac392019-10-12 11:02:26 +0800181bool Utils::isAssociated(const std::string& psuInventoryPath,
182 const AssociationList& assocs) const
183{
184 return std::find_if(assocs.begin(), assocs.end(),
185 [&psuInventoryPath](const auto& assoc) {
Patrick Williamsbab5ed92024-08-16 15:20:54 -0400186 return psuInventoryPath == std::get<2>(assoc);
187 }) != assocs.end();
Lei YU4b9ac392019-10-12 11:02:26 +0800188}
189
Patrick Williams374fae52022-07-22 19:26:55 -0500190any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service,
Lei YUf77189f2019-08-07 14:26:30 +0800191 const char* path, const char* interface,
192 const char* propertyName) const
193{
194 auto method = bus.new_method_call(service, path,
195 "org.freedesktop.DBus.Properties", "Get");
196 method.append(interface, propertyName);
197 try
198 {
199 PropertyType value{};
200 auto reply = bus.call(method);
201 reply.read(value);
202 return any(value);
203 }
Patrick Williams374fae52022-07-22 19:26:55 -0500204 catch (const sdbusplus::exception_t& ex)
Lei YUf77189f2019-08-07 14:26:30 +0800205 {
206 log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
207 entry("INTERFACE=%s", interface),
208 entry("PROPERTY=%s", propertyName));
209 throw std::runtime_error("GetProperty call failed");
210 }
211}
212
Lei YU5e0dcb32019-08-02 18:04:34 +0800213} // namespace utils