blob: 8d782abb1ae49a2b1b21548d837376426411ba9c [file] [log] [blame]
Lei YU5e0dcb32019-08-02 18:04:34 +08001#include "config.h"
2
3#include "utils.hpp"
4
Lei YUad90ad52019-08-06 11:19:28 +08005#include <openssl/sha.h>
6
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
63std::vector<std::string>
64 Utils::getPSUInventoryPath(sdbusplus::bus::bus& bus) const
Lei YU5e0dcb32019-08-02 18:04:34 +080065{
66 std::vector<std::string> paths;
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);
73
74 reply.read(paths);
75 return paths;
76}
77
Lei YUf77189f2019-08-07 14:26:30 +080078std::string Utils::getService(sdbusplus::bus::bus& bus, const char* path,
79 const char* interface) const
Lei YUad90ad52019-08-06 11:19:28 +080080{
Lei YUd0bbfa92019-09-11 16:10:54 +080081 auto services = getServices(bus, path, interface);
82 if (services.empty())
83 {
84 return {};
85 }
86 return services[0];
87}
88
89std::vector<std::string> Utils::getServices(sdbusplus::bus::bus& bus,
90 const char* path,
91 const char* interface) const
92{
Lei YUad90ad52019-08-06 11:19:28 +080093 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
94 MAPPER_INTERFACE, "GetObject");
95
96 mapper.append(path, std::vector<std::string>({interface}));
97 try
98 {
99 auto mapperResponseMsg = bus.call(mapper);
100
101 std::vector<std::pair<std::string, std::vector<std::string>>>
102 mapperResponse;
103 mapperResponseMsg.read(mapperResponse);
104 if (mapperResponse.empty())
105 {
106 log<level::ERR>("Error reading mapper response");
107 throw std::runtime_error("Error reading mapper response");
108 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800109 std::vector<std::string> ret;
110 for (const auto& i : mapperResponse)
Lei YUad90ad52019-08-06 11:19:28 +0800111 {
Lei YUd0bbfa92019-09-11 16:10:54 +0800112 ret.emplace_back(i.first);
Lei YUad90ad52019-08-06 11:19:28 +0800113 }
Lei YUd0bbfa92019-09-11 16:10:54 +0800114 return ret;
Lei YUad90ad52019-08-06 11:19:28 +0800115 }
116 catch (const sdbusplus::exception::SdBusError& ex)
117 {
118 log<level::ERR>("Mapper call failed", entry("METHOD=%d", "GetObject"),
119 entry("PATH=%s", path),
120 entry("INTERFACE=%s", interface));
121 throw std::runtime_error("Mapper call failed");
122 }
123}
124
Lei YUf77189f2019-08-07 14:26:30 +0800125std::string Utils::getVersionId(const std::string& version) const
Lei YUad90ad52019-08-06 11:19:28 +0800126{
127 if (version.empty())
128 {
129 log<level::ERR>("Error version is empty");
130 return {};
131 }
132
133 unsigned char digest[SHA512_DIGEST_LENGTH];
134 SHA512_CTX ctx;
135 SHA512_Init(&ctx);
136 SHA512_Update(&ctx, version.c_str(), strlen(version.c_str()));
137 SHA512_Final(digest, &ctx);
138 char mdString[SHA512_DIGEST_LENGTH * 2 + 1];
139 for (int i = 0; i < SHA512_DIGEST_LENGTH; i++)
140 {
141 snprintf(&mdString[i * 2], 3, "%02x", (unsigned int)digest[i]);
142 }
143
144 // Only need 8 hex digits.
145 std::string hexId = std::string(mdString);
146 return (hexId.substr(0, 8));
147}
148
Lei YU5f3584d2019-08-27 16:28:53 +0800149std::string Utils::getVersion(const std::string& inventoryPath) const
150{
151 // Invoke vendor-specify tool to get the version string, e.g.
152 // psutils get-version
153 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
154 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
155 return (rc == 0) ? r : "";
156}
157
Lei YU65207482019-10-11 16:39:36 +0800158std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
159{
160 if (versions.empty())
161 {
162 return {};
163 }
164 std::stringstream args;
165 for (const auto& s : versions)
166 {
167 args << s << " ";
168 }
169 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
170 return (rc == 0) ? r : "";
171}
172
Lei YU4b9ac392019-10-12 11:02:26 +0800173bool Utils::isAssociated(const std::string& psuInventoryPath,
174 const AssociationList& assocs) const
175{
176 return std::find_if(assocs.begin(), assocs.end(),
177 [&psuInventoryPath](const auto& assoc) {
178 return psuInventoryPath == std::get<2>(assoc);
179 }) != assocs.end();
180}
181
Lei YUf77189f2019-08-07 14:26:30 +0800182any Utils::getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
183 const char* path, const char* interface,
184 const char* propertyName) const
185{
186 auto method = bus.new_method_call(service, path,
187 "org.freedesktop.DBus.Properties", "Get");
188 method.append(interface, propertyName);
189 try
190 {
191 PropertyType value{};
192 auto reply = bus.call(method);
193 reply.read(value);
194 return any(value);
195 }
196 catch (const sdbusplus::exception::SdBusError& ex)
197 {
198 log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
199 entry("INTERFACE=%s", interface),
200 entry("PROPERTY=%s", propertyName));
201 throw std::runtime_error("GetProperty call failed");
202 }
203}
204
Lei YU5e0dcb32019-08-02 18:04:34 +0800205} // namespace utils