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