start-update-interface: implement update manager
Implement the update manager module for the new daemon inheriting from
the Update D-Bus interface. Link with new interface APIs from
item_updater and software utils. New daemon will continue to exist at
runtime alongwith old flow, until old flow is deprecated and removed.
This change is based on -
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/65738
https://gerrit.openbmc.org/c/openbmc/docs/+/65739
Change-Id: I0ecbbb8fc5340de7f66f8870ae389b405a2debee
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/update_manager.cpp b/update_manager.cpp
new file mode 100644
index 0000000..9d90c95
--- /dev/null
+++ b/update_manager.cpp
@@ -0,0 +1,189 @@
+#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