blob: 6b7f60278b79434befcebb669da87ba544892a3d [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
Shawn McCarneycdf86de2024-11-26 10:02:14 -06007#include <phosphor-logging/lg2.hpp>
Patrick Williams5670b182023-05-10 07:50:50 -05008
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
Lei YU5e0dcb32019-08-02 18:04:34 +080013namespace utils
14{
15
16namespace // anonymous
17{
18constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
19constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
20constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
21} // namespace
22
Lei YU5f3584d2019-08-27 16:28:53 +080023namespace internal
24{
25template <typename... Ts>
Patrick Williams5670b182023-05-10 07:50:50 -050026std::string concat_string(const Ts&... ts)
Lei YU5f3584d2019-08-27 16:28:53 +080027{
28 std::stringstream s;
29 ((s << ts << " "), ...) << std::endl;
30 return s.str();
31}
32
33// Helper function to run command
34// Returns return code and the stdout
35template <typename... Ts>
Patrick Williams5670b182023-05-10 07:50:50 -050036std::pair<int, std::string> exec(const Ts&... ts)
Lei YU5f3584d2019-08-27 16:28:53 +080037{
38 std::array<char, 512> buffer;
39 std::string cmd = concat_string(ts...);
40 std::stringstream result;
41 int rc;
42 FILE* pipe = popen(cmd.c_str(), "r");
43 if (!pipe)
44 {
45 throw std::runtime_error("popen() failed!");
46 }
47 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
48 {
49 result << buffer.data();
50 }
51 rc = pclose(pipe);
52 return {rc, result.str()};
53}
54
55} // namespace internal
Lei YUf77189f2019-08-07 14:26:30 +080056const UtilsInterface& getUtils()
57{
58 static Utils utils;
59 return utils;
60}
61
Patrick Williams374fae52022-07-22 19:26:55 -050062std::vector<std::string> Utils::getPSUInventoryPath(sdbusplus::bus_t& bus) const
Lei YU5e0dcb32019-08-02 18:04:34 +080063{
64 std::vector<std::string> paths;
Matt Spinlere183edc2024-07-09 11:25:34 -050065 try
66 {
67 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
68 MAPPER_INTERFACE, "GetSubTreePaths");
69 method.append(PSU_INVENTORY_PATH_BASE);
70 method.append(0); // Depth 0 to search all
71 method.append(std::vector<std::string>({PSU_INVENTORY_IFACE}));
72 auto reply = bus.call(method);
Lei YU5e0dcb32019-08-02 18:04:34 +080073
Matt Spinlere183edc2024-07-09 11:25:34 -050074 reply.read(paths);
75 }
76 catch (const sdbusplus::exception_t&)
77 {
78 // Inventory base path not there yet.
79 }
Lei YU5e0dcb32019-08-02 18:04:34 +080080 return paths;
81}
82
Patrick Williams374fae52022-07-22 19:26:55 -050083std::string Utils::getService(sdbusplus::bus_t& bus, const char* path,
Lei YUf77189f2019-08-07 14:26:30 +080084 const char* interface) const
Lei YUad90ad52019-08-06 11:19:28 +080085{
Lei YUd0bbfa92019-09-11 16:10:54 +080086 auto services = getServices(bus, path, interface);
87 if (services.empty())
88 {
89 return {};
90 }
91 return services[0];
92}
93
Patrick Williamsbab5ed92024-08-16 15:20:54 -040094std::vector<std::string> Utils::getServices(
95 sdbusplus::bus_t& bus, const char* path, const char* interface) const
Lei YUd0bbfa92019-09-11 16:10:54 +080096{
Lei YUad90ad52019-08-06 11:19:28 +080097 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
98 MAPPER_INTERFACE, "GetObject");
99
100 mapper.append(path, std::vector<std::string>({interface}));
101 try
102 {
103 auto mapperResponseMsg = bus.call(mapper);
104
105 std::vector<std::pair<std::string, std::vector<std::string>>>
106 mapperResponse;
107 mapperResponseMsg.read(mapperResponse);
108 if (mapperResponse.empty())
109 {
Shawn McCarneycdf86de2024-11-26 10:02:14 -0600110 lg2::error("Error reading mapper response");
Lei YUad90ad52019-08-06 11:19:28 +0800111 throw std::runtime_error("Error reading mapper response");
112 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800113 std::vector<std::string> ret;
George Liuaf025bc2024-08-23 14:54:42 +0800114 ret.reserve(mapperResponse.size());
Lei YUd0bbfa92019-09-11 16:10:54 +0800115 for (const auto& i : mapperResponse)
Lei YUad90ad52019-08-06 11:19:28 +0800116 {
Lei YUd0bbfa92019-09-11 16:10:54 +0800117 ret.emplace_back(i.first);
Lei YUad90ad52019-08-06 11:19:28 +0800118 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800119 return ret;
Lei YUad90ad52019-08-06 11:19:28 +0800120 }
Patrick Williams374fae52022-07-22 19:26:55 -0500121 catch (const sdbusplus::exception_t& ex)
Lei YUad90ad52019-08-06 11:19:28 +0800122 {
Shawn McCarneycdf86de2024-11-26 10:02:14 -0600123 lg2::error("GetObject call failed: path={PATH}, interface={INTERFACE}",
124 "PATH", path, "INTERFACE", interface);
Lei YU2d156252019-10-18 10:02:20 +0800125 throw std::runtime_error("GetObject call failed");
Lei YUad90ad52019-08-06 11:19:28 +0800126 }
127}
128
Lei YUf77189f2019-08-07 14:26:30 +0800129std::string Utils::getVersionId(const std::string& version) const
Lei YUad90ad52019-08-06 11:19:28 +0800130{
131 if (version.empty())
132 {
Shawn McCarneycdf86de2024-11-26 10:02:14 -0600133 lg2::error("Error version is empty");
Lei YUad90ad52019-08-06 11:19:28 +0800134 return {};
135 }
136
George Liu0b7c7b32022-02-07 16:30:23 +0800137 using EVP_MD_CTX_Ptr =
138 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
139
140 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
141 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free);
142
143 EVP_DigestInit(ctx.get(), EVP_sha512());
144 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str()));
145 EVP_DigestFinal(ctx.get(), digest.data(), nullptr);
Lei YUad90ad52019-08-06 11:19:28 +0800146
147 // Only need 8 hex digits.
George Liu0b7c7b32022-02-07 16:30:23 +0800148 char mdString[9];
149 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x",
150 (unsigned int)digest[0], (unsigned int)digest[1],
151 (unsigned int)digest[2], (unsigned int)digest[3]);
152
153 return mdString;
Lei YUad90ad52019-08-06 11:19:28 +0800154}
155
Lei YU5f3584d2019-08-27 16:28:53 +0800156std::string Utils::getVersion(const std::string& inventoryPath) const
157{
Shawn McCarney783406e2024-11-17 21:49:37 -0600158 // Invoke vendor-specific tool to get the version string, e.g.
159 // psutils --get-version
Lei YU5f3584d2019-08-27 16:28:53 +0800160 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
161 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
162 return (rc == 0) ? r : "";
163}
164
Shawn McCarney783406e2024-11-17 21:49:37 -0600165std::string Utils::getModel(const std::string& inventoryPath) const
166{
167 // Invoke vendor-specific tool to get the model string, e.g.
168 // psutils --get-model
169 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
170 auto [rc, r] = internal::exec(PSU_MODEL_UTIL, inventoryPath);
171 return (rc == 0) ? r : "";
172}
173
Lei YU65207482019-10-11 16:39:36 +0800174std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
175{
176 if (versions.empty())
177 {
178 return {};
179 }
180 std::stringstream args;
181 for (const auto& s : versions)
182 {
183 args << s << " ";
184 }
185 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
186 return (rc == 0) ? r : "";
187}
188
Lei YU4b9ac392019-10-12 11:02:26 +0800189bool Utils::isAssociated(const std::string& psuInventoryPath,
190 const AssociationList& assocs) const
191{
192 return std::find_if(assocs.begin(), assocs.end(),
193 [&psuInventoryPath](const auto& assoc) {
Patrick Williamsbab5ed92024-08-16 15:20:54 -0400194 return psuInventoryPath == std::get<2>(assoc);
195 }) != assocs.end();
Lei YU4b9ac392019-10-12 11:02:26 +0800196}
197
Patrick Williams374fae52022-07-22 19:26:55 -0500198any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service,
Lei YUf77189f2019-08-07 14:26:30 +0800199 const char* path, const char* interface,
200 const char* propertyName) const
201{
202 auto method = bus.new_method_call(service, path,
203 "org.freedesktop.DBus.Properties", "Get");
204 method.append(interface, propertyName);
205 try
206 {
207 PropertyType value{};
208 auto reply = bus.call(method);
209 reply.read(value);
210 return any(value);
211 }
Patrick Williams374fae52022-07-22 19:26:55 -0500212 catch (const sdbusplus::exception_t& ex)
Lei YUf77189f2019-08-07 14:26:30 +0800213 {
Shawn McCarneycdf86de2024-11-26 10:02:14 -0600214 lg2::error(
215 "GetProperty call failed: path={PATH}, interface={INTERFACE}, "
216 "property={PROPERTY}",
217 "PATH", path, "INTERFACE", interface, "PROPERTY", propertyName);
Lei YUf77189f2019-08-07 14:26:30 +0800218 throw std::runtime_error("GetProperty call failed");
219 }
220}
221
Lei YU5e0dcb32019-08-02 18:04:34 +0800222} // namespace utils