1 #include "config.h" 2 3 #include "utils.hpp" 4 5 #include <openssl/evp.h> 6 7 #include <phosphor-logging/log.hpp> 8 9 #include <algorithm> 10 #include <fstream> 11 #include <sstream> 12 13 using namespace phosphor::logging; 14 15 namespace utils 16 { 17 18 namespace // anonymous 19 { 20 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 21 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 22 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 23 } // namespace 24 25 namespace internal 26 { 27 template <typename... Ts> 28 std::string concat_string(const Ts&... ts) 29 { 30 std::stringstream s; 31 ((s << ts << " "), ...) << std::endl; 32 return s.str(); 33 } 34 35 // Helper function to run command 36 // Returns return code and the stdout 37 template <typename... Ts> 38 std::pair<int, std::string> exec(const Ts&... ts) 39 { 40 std::array<char, 512> buffer; 41 std::string cmd = concat_string(ts...); 42 std::stringstream result; 43 int rc; 44 FILE* pipe = popen(cmd.c_str(), "r"); 45 if (!pipe) 46 { 47 throw std::runtime_error("popen() failed!"); 48 } 49 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) 50 { 51 result << buffer.data(); 52 } 53 rc = pclose(pipe); 54 return {rc, result.str()}; 55 } 56 57 } // namespace internal 58 const UtilsInterface& getUtils() 59 { 60 static Utils utils; 61 return utils; 62 } 63 64 std::vector<std::string> Utils::getPSUInventoryPath(sdbusplus::bus_t& 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_t& 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_t& 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_t& 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 using EVP_MD_CTX_Ptr = 133 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>; 134 135 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{}; 136 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free); 137 138 EVP_DigestInit(ctx.get(), EVP_sha512()); 139 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str())); 140 EVP_DigestFinal(ctx.get(), digest.data(), nullptr); 141 142 // Only need 8 hex digits. 143 char mdString[9]; 144 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x", 145 (unsigned int)digest[0], (unsigned int)digest[1], 146 (unsigned int)digest[2], (unsigned int)digest[3]); 147 148 return mdString; 149 } 150 151 std::string Utils::getVersion(const std::string& inventoryPath) const 152 { 153 // Invoke vendor-specify tool to get the version string, e.g. 154 // psutils get-version 155 // /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0 156 auto [rc, r] = internal::exec(PSU_VERSION_UTIL, inventoryPath); 157 return (rc == 0) ? r : ""; 158 } 159 160 std::string Utils::getLatestVersion(const std::set<std::string>& versions) const 161 { 162 if (versions.empty()) 163 { 164 return {}; 165 } 166 std::stringstream args; 167 for (const auto& s : versions) 168 { 169 args << s << " "; 170 } 171 auto [rc, r] = internal::exec(PSU_VERSION_COMPARE_UTIL, args.str()); 172 return (rc == 0) ? r : ""; 173 } 174 175 bool Utils::isAssociated(const std::string& psuInventoryPath, 176 const AssociationList& assocs) const 177 { 178 return std::find_if(assocs.begin(), assocs.end(), 179 [&psuInventoryPath](const auto& assoc) { 180 return psuInventoryPath == std::get<2>(assoc); 181 }) != assocs.end(); 182 } 183 184 any Utils::getPropertyImpl(sdbusplus::bus_t& bus, const char* service, 185 const char* path, const char* interface, 186 const char* propertyName) const 187 { 188 auto method = bus.new_method_call(service, path, 189 "org.freedesktop.DBus.Properties", "Get"); 190 method.append(interface, propertyName); 191 try 192 { 193 PropertyType value{}; 194 auto reply = bus.call(method); 195 reply.read(value); 196 return any(value); 197 } 198 catch (const sdbusplus::exception_t& ex) 199 { 200 log<level::ERR>("GetProperty call failed", entry("PATH=%s", path), 201 entry("INTERFACE=%s", interface), 202 entry("PROPERTY=%s", propertyName)); 203 throw std::runtime_error("GetProperty call failed"); 204 } 205 } 206 207 } // namespace utils 208