blob: 9d90c95f3c6b13a411344dc63abd8f2e55ac01a9 [file] [log] [blame]
#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 <filesystem>
PHOSPHOR_LOG2_USING;
namespace phosphor::software::update
{
namespace fs = std::filesystem;
namespace softwareUtils = phosphor::software::utils;
using namespace phosphor::logging;
using Version = phosphor::software::manager::Version;
using ActivationIntf = phosphor::software::updater::Activation;
void Manager::processImageFailed(sdbusplus::message::unix_fd image,
std::string& id)
{
close(image);
updateInProgress = false;
itemUpdater.updateActivationStatus(id,
ActivationIntf::Activations::Invalid);
}
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
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);
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);
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);
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);
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);
co_return;
}
}
else
{
warning("No machine name in Manifest file");
}
// Get purpose
auto purposeString = Version::getValue(manifestPath.string(), "purpose");
if (purposeString.empty())
{
error("Unable to read purpose from manifest file");
processImageFailed(image, id);
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);
// 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);
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