1 #include "config.h" 2 3 #include "version.hpp" 4 5 #include "xyz/openbmc_project/Common/error.hpp" 6 7 #include <openssl/evp.h> 8 9 #include <phosphor-logging/elog-errors.hpp> 10 #include <phosphor-logging/lg2.hpp> 11 12 #include <fstream> 13 #include <iostream> 14 #include <sstream> 15 #include <stdexcept> 16 #include <string> 17 18 namespace phosphor 19 { 20 namespace software 21 { 22 namespace manager 23 { 24 25 PHOSPHOR_LOG2_USING; 26 using namespace phosphor::logging; 27 using Argument = xyz::openbmc_project::Common::InvalidArgument; 28 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 29 30 std::string Version::getValue(const std::string& manifestFilePath, 31 std::string key) 32 { 33 key = key + "="; 34 auto keySize = key.length(); 35 36 if (manifestFilePath.empty()) 37 { 38 error("ManifestFilePath is empty."); 39 elog<InvalidArgument>( 40 Argument::ARGUMENT_NAME("manifestFilePath"), 41 Argument::ARGUMENT_VALUE(manifestFilePath.c_str())); 42 } 43 44 std::string value{}; 45 std::ifstream efile; 46 std::string line; 47 efile.exceptions(std::ifstream::failbit | std::ifstream::badbit | 48 std::ifstream::eofbit); 49 50 // Too many GCC bugs (53984, 66145) to do this the right way... 51 try 52 { 53 efile.open(manifestFilePath); 54 while (getline(efile, line)) 55 { 56 if (!line.empty() && line.back() == '\r') 57 { 58 // If the manifest has CRLF line terminators, e.g. is created on 59 // Windows, the line will contain \r at the end, remove it. 60 line.pop_back(); 61 } 62 if (line.compare(0, keySize, key) == 0) 63 { 64 value = line.substr(keySize); 65 break; 66 } 67 } 68 efile.close(); 69 } 70 catch (const std::exception& e) 71 { 72 error("Error occurred when reading MANIFEST file: {ERROR}", "KEY", key, 73 "ERROR", e); 74 } 75 76 return value; 77 } 78 79 using EVP_MD_CTX_Ptr = 80 std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>; 81 82 std::string Version::getId(const std::string& version) 83 { 84 85 if (version.empty()) 86 { 87 error("Version is empty."); 88 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Version"), 89 Argument::ARGUMENT_VALUE(version.c_str())); 90 } 91 92 std::array<unsigned char, EVP_MAX_MD_SIZE> digest{}; 93 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free); 94 95 EVP_DigestInit(ctx.get(), EVP_sha512()); 96 EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str())); 97 EVP_DigestFinal(ctx.get(), digest.data(), nullptr); 98 99 // We are only using the first 8 characters. 100 char mdString[9]; 101 snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x", 102 (unsigned int)digest[0], (unsigned int)digest[1], 103 (unsigned int)digest[2], (unsigned int)digest[3]); 104 105 return mdString; 106 } 107 108 std::string Version::getBMCMachine(const std::string& releaseFilePath) 109 { 110 std::string machineKey = "OPENBMC_TARGET_MACHINE="; 111 std::string machine{}; 112 std::ifstream efile(releaseFilePath); 113 std::string line; 114 115 while (getline(efile, line)) 116 { 117 if (line.substr(0, machineKey.size()).find(machineKey) != 118 std::string::npos) 119 { 120 std::size_t pos = line.find_first_of('"') + 1; 121 machine = line.substr(pos, line.find_last_of('"') - pos); 122 break; 123 } 124 } 125 126 if (machine.empty()) 127 { 128 error("Unable to find OPENBMC_TARGET_MACHINE"); 129 elog<InternalFailure>(); 130 } 131 132 return machine; 133 } 134 135 std::string Version::getBMCExtendedVersion(const std::string& releaseFilePath) 136 { 137 std::string extendedVersionKey = "EXTENDED_VERSION="; 138 std::string extendedVersionValue{}; 139 std::string extendedVersion{}; 140 std::ifstream efile(releaseFilePath); 141 std::string line; 142 143 while (getline(efile, line)) 144 { 145 if (line.substr(0, extendedVersionKey.size()) 146 .find(extendedVersionKey) != std::string::npos) 147 { 148 extendedVersionValue = line.substr(extendedVersionKey.size()); 149 std::size_t pos = extendedVersionValue.find_first_of('"') + 1; 150 extendedVersion = extendedVersionValue.substr( 151 pos, extendedVersionValue.find_last_of('"') - pos); 152 break; 153 } 154 } 155 156 return extendedVersion; 157 } 158 159 std::string Version::getBMCVersion(const std::string& releaseFilePath) 160 { 161 std::string versionKey = "VERSION_ID="; 162 std::string versionValue{}; 163 std::string version{}; 164 std::ifstream efile; 165 std::string line; 166 efile.open(releaseFilePath); 167 168 while (getline(efile, line)) 169 { 170 if (line.substr(0, versionKey.size()).find(versionKey) != 171 std::string::npos) 172 { 173 // Support quoted and unquoted values 174 // 1. Remove the versionKey so that we process the value only. 175 versionValue = line.substr(versionKey.size()); 176 177 // 2. Look for a starting quote, then increment the position by 1 to 178 // skip the quote character. If no quote is found, 179 // find_first_of() returns npos (-1), which by adding +1 sets pos 180 // to 0 (beginning of unquoted string). 181 std::size_t pos = versionValue.find_first_of('"') + 1; 182 183 // 3. Look for ending quote, then decrease the position by pos to 184 // get the size of the string up to before the ending quote. If 185 // no quote is found, find_last_of() returns npos (-1), and pos 186 // is 0 for the unquoted case, so substr() is called with a len 187 // parameter of npos (-1) which according to the documentation 188 // indicates to use all characters until the end of the string. 189 version = 190 versionValue.substr(pos, versionValue.find_last_of('"') - pos); 191 break; 192 } 193 } 194 efile.close(); 195 196 if (version.empty()) 197 { 198 error("BMC current version is empty"); 199 elog<InternalFailure>(); 200 } 201 202 return version; 203 } 204 205 void Delete::delete_() 206 { 207 if (parent.eraseCallback) 208 { 209 parent.eraseCallback(parent.id); 210 } 211 } 212 213 } // namespace manager 214 } // namespace software 215 } // namespace phosphor 216