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