#include "config.h" #include "version.hpp" #include "xyz/openbmc_project/Common/error.hpp" #include #include #include #include #include #include #include #include namespace phosphor { namespace software { namespace manager { PHOSPHOR_LOG2_USING; using namespace phosphor::logging; using Argument = xyz::openbmc_project::common::InvalidArgument; using namespace sdbusplus::error::xyz::openbmc_project::common; std::string Version::getValue(const std::string& manifestFilePath, std::string key) { std::vector values = getRepeatedValues(manifestFilePath, key); if (values.empty()) { return std::string{}; } if (values.size() > 1) { error("Multiple values found in MANIFEST file for key: {KEY}", "KEY", key); } return values.at(0); } std::vector Version::getRepeatedValues(const std::string& manifestFilePath, std::string key) { key = key + "="; auto keySize = key.length(); if (manifestFilePath.empty()) { error("ManifestFilePath is empty."); elog( Argument::ARGUMENT_NAME("manifestFilePath"), Argument::ARGUMENT_VALUE(manifestFilePath.c_str())); } std::vector values{}; std::ifstream efile; std::string line; efile.exceptions(std::ifstream::failbit | std::ifstream::badbit); // Too many GCC bugs (53984, 66145) to do this the right way... try { efile.open(manifestFilePath); while (getline(efile, line)) { if (!line.empty() && line.back() == '\r') { // If the manifest has CRLF line terminators, e.g. is created on // Windows, the line will contain \r at the end, remove it. line.pop_back(); } if (line.compare(0, keySize, key) == 0) { values.push_back(line.substr(keySize)); } } efile.close(); } catch (const std::exception& e) { if (!efile.eof()) { error("Error occurred when reading MANIFEST file: {ERROR}", "KEY", key, "ERROR", e); } } if (values.empty()) { info("No values found in MANIFEST file for key: {KEY}", "KEY", key); } return values; } using EVP_MD_CTX_Ptr = std::unique_ptr; std::string Version::getId(const std::string& version) { if (version.empty()) { error("Version is empty."); elog(Argument::ARGUMENT_NAME("Version"), Argument::ARGUMENT_VALUE(version.c_str())); } std::array digest{}; EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free); EVP_DigestInit(ctx.get(), EVP_sha512()); EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str())); EVP_DigestFinal(ctx.get(), digest.data(), nullptr); // We are only using the first 8 characters. char mdString[9]; snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x", (unsigned int)digest[0], (unsigned int)digest[1], (unsigned int)digest[2], (unsigned int)digest[3]); return mdString; } std::string Version::getBMCMachine(const std::string& releaseFilePath) { std::string machineKey = "OPENBMC_TARGET_MACHINE="; std::string machine{}; std::ifstream efile(releaseFilePath); std::string line; while (getline(efile, line)) { if (line.substr(0, machineKey.size()).find(machineKey) != std::string::npos) { std::size_t pos = line.find_first_of('"') + 1; machine = line.substr(pos, line.find_last_of('"') - pos); break; } } if (machine.empty()) { error("Unable to find OPENBMC_TARGET_MACHINE"); elog(); } return machine; } std::string Version::getBMCExtendedVersion(const std::string& releaseFilePath) { std::string extendedVersionKey = "EXTENDED_VERSION="; std::string extendedVersionValue{}; std::string extendedVersion{}; std::ifstream efile(releaseFilePath); std::string line; while (getline(efile, line)) { if (line.substr(0, extendedVersionKey.size()) .find(extendedVersionKey) != std::string::npos) { extendedVersionValue = line.substr(extendedVersionKey.size()); std::size_t pos = extendedVersionValue.find_first_of('"') + 1; extendedVersion = extendedVersionValue.substr( pos, extendedVersionValue.find_last_of('"') - pos); break; } } return extendedVersion; } std::string Version::getBMCVersion(const std::string& releaseFilePath) { std::string versionKey = "VERSION_ID="; std::string versionValue{}; std::string version{}; std::ifstream efile; std::string line; efile.open(releaseFilePath); while (getline(efile, line)) { if (line.substr(0, versionKey.size()).find(versionKey) != std::string::npos) { // Support quoted and unquoted values // 1. Remove the versionKey so that we process the value only. versionValue = line.substr(versionKey.size()); // 2. Look for a starting quote, then increment the position by 1 to // skip the quote character. If no quote is found, // find_first_of() returns npos (-1), which by adding +1 sets pos // to 0 (beginning of unquoted string). std::size_t pos = versionValue.find_first_of('"') + 1; // 3. Look for ending quote, then decrease the position by pos to // get the size of the string up to before the ending quote. If // no quote is found, find_last_of() returns npos (-1), and pos // is 0 for the unquoted case, so substr() is called with a len // parameter of npos (-1) which according to the documentation // indicates to use all characters until the end of the string. version = versionValue.substr(pos, versionValue.find_last_of('"') - pos); break; } } efile.close(); if (version.empty()) { error("BMC current version is empty"); elog(); } return version; } void Delete::delete_() { if (parent.eraseCallback) { parent.eraseCallback(parent.id); } } } // namespace manager } // namespace software } // namespace phosphor