15e0dcb39SLei YU #include "config.h"
25e0dcb39SLei YU 
35e0dcb39SLei YU #include "utils.hpp"
45e0dcb39SLei YU 
50b7c7b3aSGeorge Liu #include <openssl/evp.h>
6ad90ad51SLei YU 
7*5670b188SPatrick Williams #include <phosphor-logging/log.hpp>
8*5670b188SPatrick Williams 
94b9ac392SLei YU #include <algorithm>
105e0dcb39SLei YU #include <fstream>
115f3584d4SLei YU #include <sstream>
12f77189f7SLei YU 
13f77189f7SLei YU using namespace phosphor::logging;
145e0dcb39SLei YU 
155e0dcb39SLei YU namespace utils
165e0dcb39SLei YU {
175e0dcb39SLei YU 
185e0dcb39SLei YU namespace // anonymous
195e0dcb39SLei YU {
205e0dcb39SLei YU constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
215e0dcb39SLei YU constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
225e0dcb39SLei YU constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
235e0dcb39SLei YU } // namespace
245e0dcb39SLei YU 
255f3584d4SLei YU namespace internal
265f3584d4SLei YU {
275f3584d4SLei YU template <typename... Ts>
concat_string(const Ts &...ts)28*5670b188SPatrick Williams std::string concat_string(const Ts&... ts)
295f3584d4SLei YU {
305f3584d4SLei YU     std::stringstream s;
315f3584d4SLei YU     ((s << ts << " "), ...) << std::endl;
325f3584d4SLei YU     return s.str();
335f3584d4SLei YU }
345f3584d4SLei YU 
355f3584d4SLei YU // Helper function to run command
365f3584d4SLei YU // Returns return code and the stdout
375f3584d4SLei YU template <typename... Ts>
exec(const Ts &...ts)38*5670b188SPatrick Williams std::pair<int, std::string> exec(const Ts&... ts)
395f3584d4SLei YU {
405f3584d4SLei YU     std::array<char, 512> buffer;
415f3584d4SLei YU     std::string cmd = concat_string(ts...);
425f3584d4SLei YU     std::stringstream result;
435f3584d4SLei YU     int rc;
445f3584d4SLei YU     FILE* pipe = popen(cmd.c_str(), "r");
455f3584d4SLei YU     if (!pipe)
465f3584d4SLei YU     {
475f3584d4SLei YU         throw std::runtime_error("popen() failed!");
485f3584d4SLei YU     }
495f3584d4SLei YU     while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
505f3584d4SLei YU     {
515f3584d4SLei YU         result << buffer.data();
525f3584d4SLei YU     }
535f3584d4SLei YU     rc = pclose(pipe);
545f3584d4SLei YU     return {rc, result.str()};
555f3584d4SLei YU }
565f3584d4SLei YU 
575f3584d4SLei YU } // namespace internal
getUtils()58f77189f7SLei YU const UtilsInterface& getUtils()
59f77189f7SLei YU {
60f77189f7SLei YU     static Utils utils;
61f77189f7SLei YU     return utils;
62f77189f7SLei YU }
63f77189f7SLei YU 
getPSUInventoryPath(sdbusplus::bus_t & bus) const64374fae56SPatrick Williams std::vector<std::string> Utils::getPSUInventoryPath(sdbusplus::bus_t& bus) const
655e0dcb39SLei YU {
665e0dcb39SLei YU     std::vector<std::string> paths;
675e0dcb39SLei YU     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
685e0dcb39SLei YU                                       MAPPER_INTERFACE, "GetSubTreePaths");
695e0dcb39SLei YU     method.append(PSU_INVENTORY_PATH_BASE);
705e0dcb39SLei YU     method.append(0); // Depth 0 to search all
715e0dcb39SLei YU     method.append(std::vector<std::string>({PSU_INVENTORY_IFACE}));
725e0dcb39SLei YU     auto reply = bus.call(method);
735e0dcb39SLei YU 
745e0dcb39SLei YU     reply.read(paths);
755e0dcb39SLei YU     return paths;
765e0dcb39SLei YU }
775e0dcb39SLei YU 
getService(sdbusplus::bus_t & bus,const char * path,const char * interface) const78374fae56SPatrick Williams std::string Utils::getService(sdbusplus::bus_t& bus, const char* path,
79f77189f7SLei YU                               const char* interface) const
80ad90ad51SLei YU {
81d0bbfa9eSLei YU     auto services = getServices(bus, path, interface);
82d0bbfa9eSLei YU     if (services.empty())
83d0bbfa9eSLei YU     {
84d0bbfa9eSLei YU         return {};
85d0bbfa9eSLei YU     }
86d0bbfa9eSLei YU     return services[0];
87d0bbfa9eSLei YU }
88d0bbfa9eSLei YU 
getServices(sdbusplus::bus_t & bus,const char * path,const char * interface) const89374fae56SPatrick Williams std::vector<std::string> Utils::getServices(sdbusplus::bus_t& bus,
90d0bbfa9eSLei YU                                             const char* path,
91d0bbfa9eSLei YU                                             const char* interface) const
92d0bbfa9eSLei YU {
93ad90ad51SLei YU     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
94ad90ad51SLei YU                                       MAPPER_INTERFACE, "GetObject");
95ad90ad51SLei YU 
96ad90ad51SLei YU     mapper.append(path, std::vector<std::string>({interface}));
97ad90ad51SLei YU     try
98ad90ad51SLei YU     {
99ad90ad51SLei YU         auto mapperResponseMsg = bus.call(mapper);
100ad90ad51SLei YU 
101ad90ad51SLei YU         std::vector<std::pair<std::string, std::vector<std::string>>>
102ad90ad51SLei YU             mapperResponse;
103ad90ad51SLei YU         mapperResponseMsg.read(mapperResponse);
104ad90ad51SLei YU         if (mapperResponse.empty())
105ad90ad51SLei YU         {
106ad90ad51SLei YU             log<level::ERR>("Error reading mapper response");
107ad90ad51SLei YU             throw std::runtime_error("Error reading mapper response");
108ad90ad51SLei YU         }
109d0bbfa9eSLei YU         std::vector<std::string> ret;
110d0bbfa9eSLei YU         for (const auto& i : mapperResponse)
111ad90ad51SLei YU         {
112d0bbfa9eSLei YU             ret.emplace_back(i.first);
113ad90ad51SLei YU         }
114d0bbfa9eSLei YU         return ret;
115ad90ad51SLei YU     }
116374fae56SPatrick Williams     catch (const sdbusplus::exception_t& ex)
117ad90ad51SLei YU     {
1182d156252SLei YU         log<level::ERR>("GetObject call failed", entry("PATH=%s", path),
119ad90ad51SLei YU                         entry("INTERFACE=%s", interface));
1202d156252SLei YU         throw std::runtime_error("GetObject call failed");
121ad90ad51SLei YU     }
122ad90ad51SLei YU }
123ad90ad51SLei YU 
getVersionId(const std::string & version) const124f77189f7SLei YU std::string Utils::getVersionId(const std::string& version) const
125ad90ad51SLei YU {
126ad90ad51SLei YU     if (version.empty())
127ad90ad51SLei YU     {
128ad90ad51SLei YU         log<level::ERR>("Error version is empty");
129ad90ad51SLei YU         return {};
130ad90ad51SLei YU     }
131ad90ad51SLei YU 
1320b7c7b3aSGeorge Liu     using EVP_MD_CTX_Ptr =
1330b7c7b3aSGeorge Liu         std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
1340b7c7b3aSGeorge Liu 
1350b7c7b3aSGeorge Liu     std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
1360b7c7b3aSGeorge Liu     EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free);
1370b7c7b3aSGeorge Liu 
1380b7c7b3aSGeorge Liu     EVP_DigestInit(ctx.get(), EVP_sha512());
1390b7c7b3aSGeorge Liu     EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str()));
1400b7c7b3aSGeorge Liu     EVP_DigestFinal(ctx.get(), digest.data(), nullptr);
141ad90ad51SLei YU 
142ad90ad51SLei YU     // Only need 8 hex digits.
1430b7c7b3aSGeorge Liu     char mdString[9];
1440b7c7b3aSGeorge Liu     snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x",
1450b7c7b3aSGeorge Liu              (unsigned int)digest[0], (unsigned int)digest[1],
1460b7c7b3aSGeorge Liu              (unsigned int)digest[2], (unsigned int)digest[3]);
1470b7c7b3aSGeorge Liu 
1480b7c7b3aSGeorge Liu     return mdString;
149ad90ad51SLei YU }
150ad90ad51SLei YU 
getVersion(const std::string & inventoryPath) const1515f3584d4SLei YU std::string Utils::getVersion(const std::string& inventoryPath) const
1525f3584d4SLei YU {
1535f3584d4SLei YU     // Invoke vendor-specify tool to get the version string, e.g.
1545f3584d4SLei YU     //   psutils get-version
1555f3584d4SLei YU     //   /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0
1565f3584d4SLei YU     auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath);
1575f3584d4SLei YU     return (rc == 0) ? r : "";
1585f3584d4SLei YU }
1595f3584d4SLei YU 
getLatestVersion(const std::set<std::string> & versions) const1606520748dSLei YU std::string Utils::getLatestVersion(const std::set<std::string>& versions) const
1616520748dSLei YU {
1626520748dSLei YU     if (versions.empty())
1636520748dSLei YU     {
1646520748dSLei YU         return {};
1656520748dSLei YU     }
1666520748dSLei YU     std::stringstream args;
1676520748dSLei YU     for (const auto& s : versions)
1686520748dSLei YU     {
1696520748dSLei YU         args << s << " ";
1706520748dSLei YU     }
1716520748dSLei YU     auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str());
1726520748dSLei YU     return (rc == 0) ? r : "";
1736520748dSLei YU }
1746520748dSLei YU 
isAssociated(const std::string & psuInventoryPath,const AssociationList & assocs) const1754b9ac392SLei YU bool Utils::isAssociated(const std::string& psuInventoryPath,
1764b9ac392SLei YU                          const AssociationList& assocs) const
1774b9ac392SLei YU {
1784b9ac392SLei YU     return std::find_if(assocs.begin(), assocs.end(),
1794b9ac392SLei YU                         [&psuInventoryPath](const auto& assoc) {
1804b9ac392SLei YU         return psuInventoryPath == std::get<2>(assoc);
1814b9ac392SLei YU     }) != assocs.end();
1824b9ac392SLei YU }
1834b9ac392SLei YU 
getPropertyImpl(sdbusplus::bus_t & bus,const char * service,const char * path,const char * interface,const char * propertyName) const184374fae56SPatrick Williams any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service,
185f77189f7SLei YU                            const char* path, const char* interface,
186f77189f7SLei YU                            const char* propertyName) const
187f77189f7SLei YU {
188f77189f7SLei YU     auto method = bus.new_method_call(service, path,
189f77189f7SLei YU                                       "org.freedesktop.DBus.Properties", "Get");
190f77189f7SLei YU     method.append(interface, propertyName);
191f77189f7SLei YU     try
192f77189f7SLei YU     {
193f77189f7SLei YU         PropertyType value{};
194f77189f7SLei YU         auto reply = bus.call(method);
195f77189f7SLei YU         reply.read(value);
196f77189f7SLei YU         return any(value);
197f77189f7SLei YU     }
198374fae56SPatrick Williams     catch (const sdbusplus::exception_t& ex)
199f77189f7SLei YU     {
200f77189f7SLei YU         log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
201f77189f7SLei YU                         entry("INTERFACE=%s", interface),
202f77189f7SLei YU                         entry("PROPERTY=%s", propertyName));
203f77189f7SLei YU         throw std::runtime_error("GetProperty call failed");
204f77189f7SLei YU     }
205f77189f7SLei YU }
206f77189f7SLei YU 
2075e0dcb39SLei YU } // namespace utils
208