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