1 #include "config.h" 2 3 #include "utils.hpp" 4 5 #include <openssl/evp.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> Utils::getPSUInventoryPath(sdbusplus::bus_t& bus) const 64 { 65 std::vector<std::string> paths; 66 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 67 MAPPER_INTERFACE, "GetSubTreePaths"); 68 method.append(PSU_INVENTORY_PATH_BASE); 69 method.append(0); // Depth 0 to search all 70 method.append(std::vector<std::string>({PSU_INVENTORY_IFACE})); 71 auto reply = bus.call(method); 72 73 reply.read(paths); 74 return paths; 75 } 76 77 std::string Utils::getService(sdbusplus::bus_t& bus, const char* path, 78 const char* interface) const 79 { 80 auto services = getServices(bus, path, interface); 81 if (services.empty()) 82 { 83 return {}; 84 } 85 return services[0]; 86 } 87 88 std::vector<std::string> Utils::getServices(sdbusplus::bus_t& bus, 89 const char* path, 90 const char* interface) const 91 { 92 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 93 MAPPER_INTERFACE, "GetObject"); 94 95 mapper.append(path, std::vector<std::string>({interface})); 96 try 97 { 98 auto mapperResponseMsg = bus.call(mapper); 99 100 std::vector<std::pair<std::string, std::vector<std::string>>> 101 mapperResponse; 102 mapperResponseMsg.read(mapperResponse); 103 if (mapperResponse.empty()) 104 { 105 log<level::ERR>("Error reading mapper response"); 106 throw std::runtime_error("Error reading mapper response"); 107 } 108 std::vector<std::string> ret; 109 for (const auto& i : mapperResponse) 110 { 111 ret.emplace_back(i.first); 112 } 113 return ret; 114 } 115 catch (const sdbusplus::exception_t& ex) 116 { 117 log<level::ERR>("GetObject call failed", entry("PATH=%s", path), 118 entry("INTERFACE=%s", interface)); 119 throw std::runtime_error("GetObject call failed"); 120 } 121 } 122 123 std::string Utils::getVersionId(const std::string& version) const 124 { 125 if (version.empty()) 126 { 127 log<level::ERR>("Error version is empty"); 128 return {}; 129 } 130 131 using EVP_MD_CTX_Ptr = 132 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>; 133 134 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{}; 135 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free); 136 137 EVP_DigestInit(ctx.get(), EVP_sha512()); 138 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str())); 139 EVP_DigestFinal(ctx.get(), digest.data(), nullptr); 140 141 // Only need 8 hex digits. 142 char mdString[9]; 143 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x", 144 (unsigned int)digest[0], (unsigned int)digest[1], 145 (unsigned int)digest[2], (unsigned int)digest[3]); 146 147 return mdString; 148 } 149 150 std::string Utils::getVersion(const std::string& inventoryPath) const 151 { 152 // Invoke vendor-specify tool to get the version string, e.g. 153 // psutils get-version 154 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0 155 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath); 156 return (rc == 0) ? r : ""; 157 } 158 159 std::string Utils::getLatestVersion(const std::set<std::string>& versions) const 160 { 161 if (versions.empty()) 162 { 163 return {}; 164 } 165 std::stringstream args; 166 for (const auto& s : versions) 167 { 168 args << s << " "; 169 } 170 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str()); 171 return (rc == 0) ? r : ""; 172 } 173 174 bool Utils::isAssociated(const std::string& psuInventoryPath, 175 const AssociationList& assocs) const 176 { 177 return std::find_if(assocs.begin(), assocs.end(), 178 [&psuInventoryPath](const auto& assoc) { 179 return psuInventoryPath == std::get<2>(assoc); 180 }) != assocs.end(); 181 } 182 183 any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service, 184 const char* path, const char* interface, 185 const char* propertyName) const 186 { 187 auto method = bus.new_method_call(service, path, 188 "org.freedesktop.DBus.Properties", "Get"); 189 method.append(interface, propertyName); 190 try 191 { 192 PropertyType value{}; 193 auto reply = bus.call(method); 194 reply.read(value); 195 return any(value); 196 } 197 catch (const sdbusplus::exception_t& ex) 198 { 199 log<level::ERR>("GetProperty call failed", entry("PATH=%s", path), 200 entry("INTERFACE=%s", interface), 201 entry("PROPERTY=%s", propertyName)); 202 throw std::runtime_error("GetProperty call failed"); 203 } 204 } 205 206 } // namespace utils 207