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