1 #include "config.h"
2 
3 #include "utils.hpp"
4 
5 #include <openssl/sha.h>
6 
7 #include <algorithm>
8 #include <fstream>
9 #include <phosphor-logging/log.hpp>
10 #include <sstream>
11 
12 using namespace phosphor::logging;
13 
14 namespace utils
15 {
16 
17 namespace // anonymous
18 {
19 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
20 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
21 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
22 } // namespace
23 
24 namespace internal
25 {
26 template <typename... Ts>
27 std::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
36 template <typename... Ts>
37 std::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
57 const UtilsInterface& getUtils()
58 {
59     static Utils utils;
60     return utils;
61 }
62 
63 std::vector<std::string>
64     Utils::getPSUInventoryPath(sdbusplus::bus::bus& bus) const
65 {
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 
78 std::string Utils::getService(sdbusplus::bus::bus& bus, const char* path,
79                               const char* interface) const
80 {
81     auto services = getServices(bus, path, interface);
82     if (services.empty())
83     {
84         return {};
85     }
86     return services[0];
87 }
88 
89 std::vector<std::string> Utils::getServices(sdbusplus::bus::bus& bus,
90                                             const char* path,
91                                             const char* interface) const
92 {
93     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         }
109         std::vector<std::string> ret;
110         for (const auto& i : mapperResponse)
111         {
112             ret.emplace_back(i.first);
113         }
114         return ret;
115     }
116     catch (const sdbusplus::exception::SdBusError& ex)
117     {
118         log<level::ERR>("GetObject call failed", entry("PATH=%s", path),
119                         entry("INTERFACE=%s", interface));
120         throw std::runtime_error("GetObject call failed");
121     }
122 }
123 
124 std::string Utils::getVersionId(const std::string& version) const
125 {
126     if (version.empty())
127     {
128         log<level::ERR>("Error version is empty");
129         return {};
130     }
131 
132     unsigned char digest[SHA512_DIGEST_LENGTH];
133     SHA512_CTX ctx;
134     SHA512_Init(&ctx);
135     SHA512_Update(&ctx, version.c_str(), strlen(version.c_str()));
136     SHA512_Final(digest, &ctx);
137     char mdString[SHA512_DIGEST_LENGTH * 2 + 1];
138     for (int i = 0; i < SHA512_DIGEST_LENGTH; i++)
139     {
140         snprintf(&mdString[i * 2], 3, "%02x", (unsigned int)digest[i]);
141     }
142 
143     // Only need 8 hex digits.
144     std::string hexId = std::string(mdString);
145     return (hexId.substr(0, 8));
146 }
147 
148 std::string Utils::getVersion(const std::string& inventoryPath) const
149 {
150     // Invoke vendor-specify tool to get the version string, e.g.
151     //   psutils get-version
152     //   /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
153     auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
154     return (rc == 0) ? r : "";
155 }
156 
157 std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
158 {
159     if (versions.empty())
160     {
161         return {};
162     }
163     std::stringstream args;
164     for (const auto& s : versions)
165     {
166         args << s << " ";
167     }
168     auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
169     return (rc == 0) ? r : "";
170 }
171 
172 bool Utils::isAssociated(const std::string& psuInventoryPath,
173                          const AssociationList& assocs) const
174 {
175     return std::find_if(assocs.begin(), assocs.end(),
176                         [&psuInventoryPath](const auto& assoc) {
177                             return psuInventoryPath == std::get<2>(assoc);
178                         }) != assocs.end();
179 }
180 
181 any Utils::getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
182                            const char* path, const char* interface,
183                            const char* propertyName) const
184 {
185     auto method = bus.new_method_call(service, path,
186                                       "org.freedesktop.DBus.Properties", "Get");
187     method.append(interface, propertyName);
188     try
189     {
190         PropertyType value{};
191         auto reply = bus.call(method);
192         reply.read(value);
193         return any(value);
194     }
195     catch (const sdbusplus::exception::SdBusError& ex)
196     {
197         log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
198                         entry("INTERFACE=%s", interface),
199                         entry("PROPERTY=%s", propertyName));
200         throw std::runtime_error("GetProperty call failed");
201     }
202 }
203 
204 } // namespace utils
205