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/activation.cpp b/activation.cpp
index 56ce157..a09b580 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -196,8 +196,11 @@
std::make_unique<RedundancyPriority>(bus, path, *this, 0);
}
- // Remove version object from image manager
- Activation::deleteImageManagerObject();
+ if (!parent.useUpdateDBusInterface)
+ {
+ // Remove version object from image manager
+ Activation::deleteImageManagerObject();
+ }
// Create active association
parent.createActiveAssociation(path);
@@ -216,6 +219,9 @@
info("BMC image ready; need reboot to get activated.");
}
+ // Create Update Object for this version.
+ parent.createUpdateObject(versionId, path);
+
activation(softwareServer::Activation::Activations::Active);
}
diff --git a/item_updater.cpp b/item_updater.cpp
index 247e1d3..356a2ac 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -231,6 +231,18 @@
return true;
}
+void ItemUpdater::createUpdateObject(const std::string& id,
+ const std::string& path)
+{
+ if (updateManagers.find(id) != updateManagers.end())
+ {
+ error("UpdateManager object already exists for id: {ID}", "ID", id);
+ return;
+ }
+ updateManagers.insert(
+ std::make_pair(id, std::make_unique<UpdateManager>(ctx, path, *this)));
+}
+
void ItemUpdater::processBMCImage()
{
using VersionClass = phosphor::software::manager::Version;
@@ -391,6 +403,12 @@
id, std::make_unique<Activation>(
bus, path, *this, id, activationState, associations)));
+ // Create Update object for this version.
+ if (useUpdateDBusInterface)
+ {
+ createUpdateObject(id, path);
+ }
+
#ifdef BMC_STATIC_DUAL_IMAGE
uint8_t priority;
if ((functional && (runningImageSlot == 0)) ||
@@ -517,6 +535,13 @@
this->versions.erase(entryId);
}
+ // Removing entry in updateManagers map
+ auto updateManagerIt = updateManagers.find(entryId);
+ if (updateManagerIt != updateManagers.end())
+ {
+ updateManagers.erase(entryId);
+ }
+
return;
}
@@ -945,6 +970,11 @@
biosVersion->deleteObject =
std::make_unique<phosphor::software::manager::Delete>(
bus, path, *biosVersion);
+
+ if (useUpdateDBusInterface)
+ {
+ createUpdateObject(versionId, path);
+ }
}
#endif
diff --git a/item_updater.hpp b/item_updater.hpp
index 1f3c0f1..e1a8d3f 100644
--- a/item_updater.hpp
+++ b/item_updater.hpp
@@ -3,6 +3,7 @@
#include "activation.hpp"
#include "item_updater_helper.hpp"
#include "msl_verify.hpp"
+#include "update_manager.hpp"
#include "version.hpp"
#include "xyz/openbmc_project/Collection/DeleteAll/server.hpp"
@@ -37,6 +38,7 @@
using VersionClass = phosphor::software::manager::Version;
using AssociationList =
std::vector<std::tuple<std::string, std::string, std::string>>;
+using UpdateManager = phosphor::software::update::Manager;
/** @class MinimumVersion
* @brief OpenBMC MinimumVersion implementation.
@@ -80,8 +82,8 @@
bool useUpdateDBusInterface = true) :
ItemUpdaterInherit(ctx.get_bus(), path.c_str(),
ItemUpdaterInherit::action::defer_emit),
- useUpdateDBusInterface(useUpdateDBusInterface), bus(ctx.get_bus()),
- helper(bus)
+ useUpdateDBusInterface(useUpdateDBusInterface), ctx(ctx),
+ bus(ctx.get_bus()), helper(bus)
{
if (!useUpdateDBusInterface)
{
@@ -171,6 +173,13 @@
ActivationIntf::Activations status);
/**
+ * @brief Create the Update object
+ * @param[in] id - The unique identifier for the update.
+ * @param[in] path - The object path for the update object.
+ */
+ void createUpdateObject(const std::string& id, const std::string& path);
+
+ /**
* @brief Erase specified entry D-Bus object
* if Action property is not set to Active
*
@@ -302,6 +311,9 @@
*/
void createFunctionalAssociation(const std::string& path);
+ /** @brief D-Bus context */
+ sdbusplus::async::context& ctx;
+
/** @brief Persistent sdbusplus D-Bus bus connection. */
sdbusplus::bus_t& bus;
@@ -344,6 +356,9 @@
/** @brief Persistent MinimumVersion D-Bus object */
std::unique_ptr<MinimumVersion> minimumVersionObject;
+ /** @brief Persistent map of Update D-Bus objects and their SwIds */
+ std::map<std::string, std::unique_ptr<UpdateManager>> updateManagers;
+
#ifdef HOST_BIOS_UPGRADE
/** @brief Create the BIOS object without knowing the version.
*
diff --git a/meson.build b/meson.build
index 82326d8..e4808d0 100644
--- a/meson.build
+++ b/meson.build
@@ -249,19 +249,14 @@
)
software_common_sources = files(
- 'software_utils.cpp'
-)
-
-executable(
- 'phosphor-image-updater',
- image_updater_sources,
- software_common_sources,
- 'item_updater_main.cpp',
- dependencies: [deps, ssl, boost_dep],
- install: true
+ 'software_utils.cpp',
)
if get_option('software-update-dbus-interface').allowed()
+ image_updater_sources += files(
+ 'update_manager.cpp'
+ )
+
executable(
'phosphor-software-manager',
'software_manager.cpp',
@@ -276,6 +271,15 @@
endif
executable(
+ 'phosphor-image-updater',
+ image_updater_sources,
+ software_common_sources,
+ 'item_updater_main.cpp',
+ dependencies: [deps, ssl, boost_dep],
+ install: true
+)
+
+executable(
'phosphor-version-software-manager',
'image_manager.cpp',
'image_manager_main.cpp',
diff --git a/software_manager.cpp b/software_manager.cpp
index 15f7dbe..3ee9f1a 100644
--- a/software_manager.cpp
+++ b/software_manager.cpp
@@ -1,27 +1,24 @@
-#include "config.h"
+#include "item_updater.hpp"
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/async.hpp>
-#include <xyz/openbmc_project/Software/Update/server.hpp>
+
+using ItemUpdaterIntf = phosphor::software::updater::ItemUpdater;
PHOSPHOR_LOG2_USING;
int main()
{
info("Creating Software Manager");
-
auto path = std::string(SOFTWARE_OBJPATH) + "/bmc";
sdbusplus::async::context ctx;
- sdbusplus::server::manager_t manager{ctx, path.c_str()};
+ sdbusplus::server::manager_t manager{ctx, SOFTWARE_OBJPATH};
- // NOLINTNEXTLINE(readability-static-accessed-through-instance)
- ctx.spawn([=](sdbusplus::async::context& ctx) -> sdbusplus::async::task<> {
- constexpr auto serviceName = "xyz.openbmc_project.Software.Manager";
- ctx.request_name(serviceName);
- co_return;
- }(ctx));
+ constexpr auto serviceName = "xyz.openbmc_project.Software.Manager";
+
+ ItemUpdaterIntf itemUpdater{ctx, path};
+ ctx.request_name(serviceName);
ctx.run();
-
return 0;
}
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
diff --git a/update_manager.hpp b/update_manager.hpp
new file mode 100644
index 0000000..5214335
--- /dev/null
+++ b/update_manager.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "config.h"
+
+#include <sdbusplus/async.hpp>
+#include <xyz/openbmc_project/Software/Update/server.hpp>
+
+#include <random>
+#include <string>
+#include <tuple>
+
+namespace phosphor::software::updater
+{
+class ItemUpdater;
+}
+
+namespace phosphor::software::update
+{
+
+using UpdateIntf = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::Software::server::Update>;
+using ItemUpdaterIntf = phosphor::software::updater::ItemUpdater;
+
+using ApplyTimeIntf =
+ sdbusplus::common::xyz::openbmc_project::software::ApplyTime;
+
+/** @class Manager
+ * @brief Processes the image file from update D-Bus interface.
+ * @details The update manager class handles software updates and manages
+ * software info through version and activation objects.
+ */
+class Manager : public UpdateIntf
+{
+ public:
+ /** @brief Constructs Manager Class
+ *
+ * @param[in] bus - The Dbus bus object
+ */
+ explicit Manager(sdbusplus::async::context& ctx, const std::string& path,
+ ItemUpdaterIntf& itemUpdater) :
+ UpdateIntf(ctx.get_bus(), path.c_str(), UpdateIntf::action::defer_emit),
+ ctx(ctx), itemUpdater(itemUpdater)
+ {
+ emit_object_added();
+ }
+
+ private:
+ /** @brief Implementation for StartUpdate
+ * Start a firware update to be performed asynchronously.
+ */
+ sdbusplus::message::object_path
+ startUpdate(sdbusplus::message::unix_fd image,
+ ApplyTimeIntf::RequestedApplyTimes applyTime) override;
+
+ /* @brief Process the image supplied via image fd */
+ auto processImage(sdbusplus::message::unix_fd image,
+ ApplyTimeIntf::RequestedApplyTimes applyTime,
+ std::string id,
+ std::string objPath) -> sdbusplus::async::task<>;
+
+ /* @brief The handler for the image processing failure */
+ void processImageFailed(sdbusplus::message::unix_fd image, std::string& id);
+
+ /** @brief The random generator for the software id */
+ std::mt19937 randomGen{static_cast<unsigned>(
+ std::chrono::system_clock::now().time_since_epoch().count())};
+
+ /** @brief D-Bus context */
+ sdbusplus::async::context& ctx;
+ /** @brief item_updater reference */
+ ItemUpdaterIntf& itemUpdater;
+ /** @brief State whether update is in progress */
+ bool updateInProgress = false;
+};
+
+} // namespace phosphor::software::update