| #include "config.h" | 
 |  | 
 | #include "version.hpp" | 
 |  | 
 | #include "xyz/openbmc_project/Common/error.hpp" | 
 |  | 
 | #include <openssl/evp.h> | 
 |  | 
 | #include <phosphor-logging/elog-errors.hpp> | 
 | #include <phosphor-logging/lg2.hpp> | 
 |  | 
 | #include <fstream> | 
 | #include <iostream> | 
 | #include <sstream> | 
 | #include <stdexcept> | 
 | #include <string> | 
 |  | 
 | 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<std::string> 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<std::string> Version::getRepeatedValues( | 
 |     const std::string& manifestFilePath, std::string key) | 
 | { | 
 |     key = key + "="; | 
 |     auto keySize = key.length(); | 
 |  | 
 |     if (manifestFilePath.empty()) | 
 |     { | 
 |         error("ManifestFilePath is empty."); | 
 |         elog<InvalidArgument>( | 
 |             Argument::ARGUMENT_NAME("manifestFilePath"), | 
 |             Argument::ARGUMENT_VALUE(manifestFilePath.c_str())); | 
 |     } | 
 |  | 
 |     std::vector<std::string> 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<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>; | 
 |  | 
 | std::string Version::getId(const std::string& version) | 
 | { | 
 |     if (version.empty()) | 
 |     { | 
 |         error("Version is empty."); | 
 |         elog<InvalidArgument>(Argument::ARGUMENT_NAME("Version"), | 
 |                               Argument::ARGUMENT_VALUE(version.c_str())); | 
 |     } | 
 |  | 
 |     std::array<unsigned char, EVP_MAX_MD_SIZE> 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<InternalFailure>(); | 
 |     } | 
 |  | 
 |     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<InternalFailure>(); | 
 |     } | 
 |  | 
 |     return version; | 
 | } | 
 |  | 
 | void Delete::delete_() | 
 | { | 
 |     if (parent.eraseCallback) | 
 |     { | 
 |         parent.eraseCallback(parent.id); | 
 |     } | 
 | } | 
 |  | 
 | } // namespace manager | 
 | } // namespace software | 
 | } // namespace phosphor |