| #include "update_manager.hpp" | 
 |  | 
 | #include "item_updater.hpp" | 
 | #include "software_utils.hpp" | 
 | #include "version.hpp" | 
 |  | 
 | #include <phosphor-logging/elog-errors.hpp> | 
 | #include <phosphor-logging/elog.hpp> | 
 | #include <phosphor-logging/lg2.hpp> | 
 | #include <sdbusplus/async.hpp> | 
 | #include <xyz/openbmc_project/Common/error.hpp> | 
 | #include <xyz/openbmc_project/Software/Image/error.hpp> | 
 |  | 
 | #include <filesystem> | 
 |  | 
 | PHOSPHOR_LOG2_USING; | 
 |  | 
 | namespace phosphor::software::update | 
 | { | 
 |  | 
 | namespace fs = std::filesystem; | 
 | namespace softwareUtils = phosphor::software::utils; | 
 | namespace SoftwareLogging = phosphor::logging::xyz::openbmc_project::software; | 
 | namespace SoftwareErrors = | 
 |     sdbusplus::error::xyz::openbmc_project::software::image; | 
 | using namespace phosphor::logging; | 
 | using Version = phosphor::software::manager::Version; | 
 | using ActivationIntf = phosphor::software::updater::Activation; | 
 | using ManifestFail = SoftwareLogging::image::ManifestFileFailure; | 
 | using UnTarFail = SoftwareLogging::image::UnTarFailure; | 
 | using InternalFail = SoftwareLogging::image::InternalFailure; | 
 | using ImageFail = SoftwareLogging::image::ImageFailure; | 
 |  | 
 | void Manager::processImageFailed(sdbusplus::message::unix_fd image, | 
 |                                  std::string& id) | 
 | { | 
 |     close(image); | 
 |     updateInProgress = false; | 
 |     itemUpdater.updateActivationStatus(id, | 
 |                                        ActivationIntf::Activations::Invalid); | 
 | } | 
 |  | 
 | bool verifyImagePurpose(Version::VersionPurpose purpose, | 
 |                         ItemUpdaterIntf::UpdaterType type) | 
 | { | 
 |     if (purpose == Version::VersionPurpose::Host) | 
 |     { | 
 |         return (type == ItemUpdaterIntf::UpdaterType::BIOS || | 
 |                 type == ItemUpdaterIntf::UpdaterType::ALL); | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | auto Manager::processImage(sdbusplus::message::unix_fd image, | 
 |                            ApplyTimeIntf::RequestedApplyTimes applyTime, | 
 |                            std::string id, std::string objPath) | 
 |     -> sdbusplus::async::task<> | 
 | { | 
 |     debug("Processing image {FD}", "FD", image.fd); | 
 |     fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR}); | 
 |     tmpDirPath /= "imageXXXXXX"; | 
 |     auto tmpDir = tmpDirPath.string(); | 
 |     // Create a tmp dir to copy tarball. | 
 |     if (!mkdtemp(tmpDir.data())) | 
 |     { | 
 |         error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::InternalFailure>(InternalFail::FAIL("mkdtemp")); | 
 |         co_return; | 
 |     } | 
 |  | 
 |     std::error_code ec; | 
 |     tmpDirPath = tmpDir; | 
 |     softwareUtils::RemovablePath tmpDirToRemove(tmpDirPath); | 
 |  | 
 |     // Untar tarball into the tmp dir | 
 |     if (!softwareUtils::unTar(image, tmpDirPath.string())) | 
 |     { | 
 |         error("Error occurred during untar"); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::UnTarFailure>( | 
 |             UnTarFail::PATH(tmpDirPath.c_str())); | 
 |         co_return; | 
 |     } | 
 |  | 
 |     fs::path manifestPath = tmpDirPath; | 
 |     manifestPath /= MANIFEST_FILE_NAME; | 
 |  | 
 |     // Get version | 
 |     auto version = Version::getValue(manifestPath.string(), "version"); | 
 |     if (version.empty()) | 
 |     { | 
 |         error("Unable to read version from manifest file"); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::ManifestFileFailure>( | 
 |             ManifestFail::PATH(manifestPath.string().c_str())); | 
 |         co_return; | 
 |     } | 
 |  | 
 |     // Get running machine name | 
 |     std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE); | 
 |     if (currMachine.empty()) | 
 |     { | 
 |         auto path = OS_RELEASE_FILE; | 
 |         error("Failed to read machine name from osRelease: {PATH}", "PATH", | 
 |               path); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::ImageFailure>( | 
 |             ImageFail::FAIL("Failed to read machine name"), | 
 |             ImageFail::PATH(path)); | 
 |         co_return; | 
 |     } | 
 |  | 
 |     // Get machine name for image to be upgraded | 
 |     std::string machineStr = | 
 |         Version::getValue(manifestPath.string(), "MachineName"); | 
 |     if (!machineStr.empty()) | 
 |     { | 
 |         if (machineStr != currMachine) | 
 |         { | 
 |             error( | 
 |                 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}", | 
 |                 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr); | 
 |             processImageFailed(image, id); | 
 |             report<SoftwareErrors::ImageFailure>( | 
 |                 ImageFail::FAIL("Machine name does not match"), | 
 |                 ImageFail::PATH(manifestPath.string().c_str())); | 
 |             co_return; | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         warning("No machine name in Manifest file"); | 
 |         report<SoftwareErrors::ImageFailure>( | 
 |             ImageFail::FAIL("MANIFEST is missing machine name"), | 
 |             ImageFail::PATH(manifestPath.string().c_str())); | 
 |     } | 
 |  | 
 |     // Get purpose | 
 |     auto purposeString = Version::getValue(manifestPath.string(), "purpose"); | 
 |     if (purposeString.empty()) | 
 |     { | 
 |         error("Unable to read purpose from manifest file"); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::ManifestFileFailure>( | 
 |             ManifestFail::PATH(manifestPath.string().c_str())); | 
 |         co_return; | 
 |     } | 
 |     auto convertedPurpose = | 
 |         sdbusplus::message::convert_from_string<Version::VersionPurpose>( | 
 |             purposeString); | 
 |     if (!convertedPurpose) | 
 |     { | 
 |         warning( | 
 |             "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.", | 
 |             "PURPOSE", purposeString); | 
 |     } | 
 |     auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown); | 
 |  | 
 |     if (!verifyImagePurpose(purpose, itemUpdater.type)) | 
 |     { | 
 |         error("Purpose ({PURPOSE}) is not supported", "PURPOSE", purpose); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::ImageFailure>( | 
 |             ImageFail::FAIL("Purpose is not supported"), | 
 |             ImageFail::PATH(manifestPath.string().c_str())); | 
 |         co_return; | 
 |     } | 
 |  | 
 |     // Get ExtendedVersion | 
 |     std::string extendedVersion = | 
 |         Version::getValue(manifestPath.string(), "ExtendedVersion"); | 
 |  | 
 |     // Get CompatibleNames | 
 |     std::vector<std::string> compatibleNames = | 
 |         Version::getRepeatedValues(manifestPath.string(), "CompatibleName"); | 
 |  | 
 |     // Rename IMG_UPLOAD_DIR/imageXXXXXX to IMG_UPLOAD_DIR/id as Manifest | 
 |     // parsing succedded. | 
 |     fs::path imageDirPath = std::string{IMG_UPLOAD_DIR}; | 
 |     imageDirPath /= id; | 
 |     fs::rename(tmpDirPath, imageDirPath, ec); | 
 |     tmpDirToRemove.path.clear(); | 
 |  | 
 |     auto filePath = imageDirPath.string(); | 
 |     // Create Version object | 
 |     auto state = itemUpdater.verifyAndCreateObjects( | 
 |         id, objPath, version, purpose, extendedVersion, filePath, | 
 |         compatibleNames); | 
 |     if (state != ActivationIntf::Activations::Ready) | 
 |     { | 
 |         error("Software image is invalid"); | 
 |         processImageFailed(image, id); | 
 |         report<SoftwareErrors::ImageFailure>( | 
 |             ImageFail::FAIL("Image is invalid"), | 
 |             ImageFail::PATH(filePath.c_str())); | 
 |         co_return; | 
 |     } | 
 |     if (applyTime == ApplyTimeIntf::RequestedApplyTimes::Immediate || | 
 |         applyTime == ApplyTimeIntf::RequestedApplyTimes::OnReset) | 
 |     { | 
 |         itemUpdater.requestActivation(id); | 
 |     } | 
 |  | 
 |     updateInProgress = false; | 
 |     close(image); | 
 |     co_return; | 
 | } | 
 |  | 
 | sdbusplus::message::object_path Manager::startUpdate( | 
 |     sdbusplus::message::unix_fd image, | 
 |     ApplyTimeIntf::RequestedApplyTimes applyTime) | 
 | { | 
 |     info("Starting update for image {FD}", "FD", static_cast<int>(image)); | 
 |     using sdbusplus::xyz::openbmc_project::Common::Error::Unavailable; | 
 |     if (updateInProgress) | 
 |     { | 
 |         error("Failed to start as update is already in progress"); | 
 |         report<Unavailable>(); | 
 |         return sdbusplus::message::object_path(); | 
 |     } | 
 |     updateInProgress = true; | 
 |  | 
 |     auto id = Version::getId(std::to_string(randomGen())); | 
 |     auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; | 
 |  | 
 |     // Create Activation Object | 
 |     itemUpdater.createActivationWithApplyTime(id, objPath, applyTime); | 
 |  | 
 |     int newFd = dup(image); | 
 |     ctx.spawn(processImage(newFd, applyTime, id, objPath)); | 
 |  | 
 |     return sdbusplus::message::object_path(objPath); | 
 | } | 
 |  | 
 | } // namespace phosphor::software::update |