1 #include "update_manager.hpp" 2 3 #include "item_updater.hpp" 4 #include "software_utils.hpp" 5 #include "version.hpp" 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/lg2.hpp> 10 #include <sdbusplus/async.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 #include <xyz/openbmc_project/Software/Image/error.hpp> 13 14 #include <filesystem> 15 16 PHOSPHOR_LOG2_USING; 17 18 namespace phosphor::software::update 19 { 20 21 namespace fs = std::filesystem; 22 namespace softwareUtils = phosphor::software::utils; 23 namespace SoftwareLogging = phosphor::logging::xyz::openbmc_project::software; 24 namespace SoftwareErrors = 25 sdbusplus::error::xyz::openbmc_project::software::image; 26 using namespace phosphor::logging; 27 using Version = phosphor::software::manager::Version; 28 using ActivationIntf = phosphor::software::updater::Activation; 29 using ManifestFail = SoftwareLogging::image::ManifestFileFailure; 30 using UnTarFail = SoftwareLogging::image::UnTarFailure; 31 using InternalFail = SoftwareLogging::image::InternalFailure; 32 using ImageFail = SoftwareLogging::image::ImageFailure; 33 34 void Manager::processImageFailed(sdbusplus::message::unix_fd image, 35 std::string& id) 36 { 37 close(image); 38 updateInProgress = false; 39 itemUpdater.updateActivationStatus(id, 40 ActivationIntf::Activations::Invalid); 41 } 42 43 bool verifyImagePurpose(Version::VersionPurpose purpose, 44 ItemUpdaterIntf::UpdaterType type) 45 { 46 if (purpose == Version::VersionPurpose::Host) 47 { 48 return (type == ItemUpdaterIntf::UpdaterType::BIOS || 49 type == ItemUpdaterIntf::UpdaterType::ALL); 50 } 51 return true; 52 } 53 54 auto Manager::processImage(sdbusplus::message::unix_fd image, 55 ApplyTimeIntf::RequestedApplyTimes applyTime, 56 std::string id, std::string objPath) 57 -> sdbusplus::async::task<> 58 { 59 debug("Processing image {FD}", "FD", image.fd); 60 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR}); 61 tmpDirPath /= "imageXXXXXX"; 62 auto tmpDir = tmpDirPath.string(); 63 // Create a tmp dir to copy tarball. 64 if (!mkdtemp(tmpDir.data())) 65 { 66 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno); 67 processImageFailed(image, id); 68 report<SoftwareErrors::InternalFailure>(InternalFail::FAIL("mkdtemp")); 69 co_return; 70 } 71 72 std::error_code ec; 73 tmpDirPath = tmpDir; 74 softwareUtils::RemovablePath tmpDirToRemove(tmpDirPath); 75 76 // Untar tarball into the tmp dir 77 if (!softwareUtils::unTar(image, tmpDirPath.string())) 78 { 79 error("Error occurred during untar"); 80 processImageFailed(image, id); 81 report<SoftwareErrors::UnTarFailure>( 82 UnTarFail::PATH(tmpDirPath.c_str())); 83 co_return; 84 } 85 86 fs::path manifestPath = tmpDirPath; 87 manifestPath /= MANIFEST_FILE_NAME; 88 89 // Get version 90 auto version = Version::getValue(manifestPath.string(), "version"); 91 if (version.empty()) 92 { 93 error("Unable to read version from manifest file"); 94 processImageFailed(image, id); 95 report<SoftwareErrors::ManifestFileFailure>( 96 ManifestFail::PATH(manifestPath.string().c_str())); 97 co_return; 98 } 99 100 // Get running machine name 101 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE); 102 if (currMachine.empty()) 103 { 104 auto path = OS_RELEASE_FILE; 105 error("Failed to read machine name from osRelease: {PATH}", "PATH", 106 path); 107 processImageFailed(image, id); 108 report<SoftwareErrors::ImageFailure>( 109 ImageFail::FAIL("Failed to read machine name"), 110 ImageFail::PATH(path)); 111 co_return; 112 } 113 114 // Get machine name for image to be upgraded 115 std::string machineStr = 116 Version::getValue(manifestPath.string(), "MachineName"); 117 if (!machineStr.empty()) 118 { 119 if (machineStr != currMachine) 120 { 121 error( 122 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}", 123 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr); 124 processImageFailed(image, id); 125 report<SoftwareErrors::ImageFailure>( 126 ImageFail::FAIL("Machine name does not match"), 127 ImageFail::PATH(manifestPath.string().c_str())); 128 co_return; 129 } 130 } 131 else 132 { 133 warning("No machine name in Manifest file"); 134 report<SoftwareErrors::ImageFailure>( 135 ImageFail::FAIL("MANIFEST is missing machine name"), 136 ImageFail::PATH(manifestPath.string().c_str())); 137 } 138 139 // Get purpose 140 auto purposeString = Version::getValue(manifestPath.string(), "purpose"); 141 if (purposeString.empty()) 142 { 143 error("Unable to read purpose from manifest file"); 144 processImageFailed(image, id); 145 report<SoftwareErrors::ManifestFileFailure>( 146 ManifestFail::PATH(manifestPath.string().c_str())); 147 co_return; 148 } 149 auto convertedPurpose = 150 sdbusplus::message::convert_from_string<Version::VersionPurpose>( 151 purposeString); 152 if (!convertedPurpose) 153 { 154 warning( 155 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.", 156 "PURPOSE", purposeString); 157 } 158 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown); 159 160 if (!verifyImagePurpose(purpose, itemUpdater.type)) 161 { 162 error("Purpose ({PURPOSE}) is not supported", "PURPOSE", purpose); 163 processImageFailed(image, id); 164 report<SoftwareErrors::ImageFailure>( 165 ImageFail::FAIL("Purpose is not supported"), 166 ImageFail::PATH(manifestPath.string().c_str())); 167 co_return; 168 } 169 170 // Get ExtendedVersion 171 std::string extendedVersion = 172 Version::getValue(manifestPath.string(), "ExtendedVersion"); 173 174 // Get CompatibleNames 175 std::vector<std::string> compatibleNames = 176 Version::getRepeatedValues(manifestPath.string(), "CompatibleName"); 177 178 // Rename IMG_UPLOAD_DIR/imageXXXXXX to IMG_UPLOAD_DIR/id as Manifest 179 // parsing succedded. 180 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR}; 181 imageDirPath /= id; 182 fs::rename(tmpDirPath, imageDirPath, ec); 183 tmpDirToRemove.path.clear(); 184 185 auto filePath = imageDirPath.string(); 186 // Create Version object 187 auto state = itemUpdater.verifyAndCreateObjects( 188 id, objPath, version, purpose, extendedVersion, filePath, 189 compatibleNames); 190 if (state != ActivationIntf::Activations::Ready) 191 { 192 error("Software image is invalid"); 193 processImageFailed(image, id); 194 report<SoftwareErrors::ImageFailure>( 195 ImageFail::FAIL("Image is invalid"), 196 ImageFail::PATH(filePath.c_str())); 197 co_return; 198 } 199 if (applyTime == ApplyTimeIntf::RequestedApplyTimes::Immediate || 200 applyTime == ApplyTimeIntf::RequestedApplyTimes::OnReset) 201 { 202 itemUpdater.requestActivation(id); 203 } 204 205 updateInProgress = false; 206 close(image); 207 co_return; 208 } 209 210 sdbusplus::message::object_path Manager::startUpdate( 211 sdbusplus::message::unix_fd image, 212 ApplyTimeIntf::RequestedApplyTimes applyTime) 213 { 214 info("Starting update for image {FD}", "FD", static_cast<int>(image)); 215 using sdbusplus::xyz::openbmc_project::Common::Error::Unavailable; 216 if (updateInProgress) 217 { 218 error("Failed to start as update is already in progress"); 219 report<Unavailable>(); 220 return sdbusplus::message::object_path(); 221 } 222 updateInProgress = true; 223 224 auto id = Version::getId(std::to_string(randomGen())); 225 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; 226 227 // Create Activation Object 228 itemUpdater.createActivationWithApplyTime(id, objPath, applyTime); 229 230 int newFd = dup(image); 231 ctx.spawn(processImage(newFd, applyTime, id, objPath)); 232 233 return sdbusplus::message::object_path(objPath); 234 } 235 236 } // namespace phosphor::software::update 237