Activation: initially support psu update
Initial support for PSU update by starting a systemd unit with PSU
inventory path and image dir as arguments.
Add an example psu-update@.service that shows how the arguments are
passed to systemd unit and expanded to command line arguments.
Tested: Upload a dummy tarball, create a dummy service that only prints
the arguments, and verify the service is invoked correctly when
the RequestedActivation is set to Active.
Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I7e122f1cce234caf4951d3e3daad5bee406b507b
diff --git a/src/activation.cpp b/src/activation.cpp
index 8bfed0b..df84879 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -1,5 +1,12 @@
+#include "config.h"
+
#include "activation.hpp"
+#include "utils.hpp"
+
+#include <cassert>
+#include <filesystem>
+
namespace phosphor
{
namespace software
@@ -7,19 +14,114 @@
namespace updater
{
+constexpr auto SYSTEMD_BUSNAME = "org.freedesktop.systemd1";
+constexpr auto SYSTEMD_PATH = "/org/freedesktop/systemd1";
+constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
+
+namespace fs = std::filesystem;
namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
+using SoftwareActivation = softwareServer::Activation;
+
+namespace internal
+{
+/** Construct the systemd service name */
+std::string getUpdateService(const std::string& psuInventoryPath,
+ const std::string& versionId)
+{
+ fs::path imagePath(IMG_DIR);
+ imagePath /= versionId;
+
+ // The systemd unit shall be escaped
+ std::string args = psuInventoryPath;
+ args += "\\x20";
+ args += imagePath;
+ std::replace(args.begin(), args.end(), '/', '-');
+
+ std::string service = PSU_UPDATE_SERVICE;
+ auto p = service.find('@');
+ assert(p != std::string::npos);
+ service.insert(p + 1, args);
+ return service;
+}
+
+} // namespace internal
auto Activation::activation(Activations value) -> Activations
{
- // TODO
- return softwareServer::Activation::activation(value);
+ if (value == Status::Activating)
+ {
+ startActivation();
+ }
+ else
+ {
+ // TODO
+ }
+
+ return SoftwareActivation::activation(value);
}
auto Activation::requestedActivation(RequestedActivations value)
-> RequestedActivations
{
- // TODO
- return softwareServer::Activation::requestedActivation(value);
+ if ((value == SoftwareActivation::RequestedActivations::Active) &&
+ (SoftwareActivation::requestedActivation() !=
+ SoftwareActivation::RequestedActivations::Active))
+ {
+ if ((activation() == Status::Ready) || (activation() == Status::Failed))
+ {
+ activation(Status::Activating);
+ }
+ }
+ return SoftwareActivation::requestedActivation(value);
+}
+
+void Activation::unitStateChange(sdbusplus::message::message& msg)
+{
+ uint32_t newStateID{};
+ sdbusplus::message::object_path newStateObjPath;
+ std::string newStateUnit{};
+ std::string newStateResult{};
+
+ // Read the msg and populate each variable
+ msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
+
+ if (newStateUnit == psuUpdateUnit)
+ {
+ if (newStateResult == "done")
+ {
+ finishActivation();
+ }
+ if (newStateResult == "failed" || newStateResult == "dependency")
+ {
+ activation(Status::Failed);
+ }
+ }
+}
+
+void Activation::startActivation()
+{
+ // TODO: for now only update one psu, future commits shall handle update
+ // multiple psus
+ auto psuPaths = utils::getPSUInventoryPath(bus);
+ if (psuPaths.empty())
+ {
+ return;
+ }
+
+ psuUpdateUnit = internal::getUpdateService(psuPaths[0], versionId);
+
+ auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+ SYSTEMD_INTERFACE, "StartUnit");
+ method.append(psuUpdateUnit, "replace");
+ bus.call_noreply(method);
+}
+
+void Activation::finishActivation()
+{
+ // TODO: delete the interfaces created by phosphor-software-manager
+ // TODO: delete the old software object
+ // TODO: create related associations
+ activation(Status::Active);
}
} // namespace updater
diff --git a/src/activation.hpp b/src/activation.hpp
index 45aa521..cde55c5 100644
--- a/src/activation.hpp
+++ b/src/activation.hpp
@@ -16,6 +16,8 @@
namespace updater
{
+namespace sdbusRule = sdbusplus::bus::match::rules;
+
using ActivationInherit = sdbusplus::server::object::object<
sdbusplus::xyz::openbmc_project::Software::server::ExtendedVersion,
sdbusplus::xyz::openbmc_project::Software::server::Activation,
@@ -29,6 +31,7 @@
class Activation : public ActivationInherit
{
public:
+ using Status = Activations;
/** @brief Constructs Activation Software Manager
*
* @param[in] bus - The Dbus bus object
@@ -44,7 +47,14 @@
Activations activationStatus,
const AssociationList& assocs) :
ActivationInherit(bus, path.c_str(), true),
- bus(bus), path(path), versionId(versionId)
+ versionId(versionId), bus(bus), path(path),
+ systemdSignals(
+ bus,
+ sdbusRule::type::signal() + sdbusRule::member("JobRemoved") +
+ sdbusRule::path("/org/freedesktop/systemd1") +
+ sdbusRule::interface("org.freedesktop.systemd1.Manager"),
+ std::bind(&Activation::unitStateChange, this,
+ std::placeholders::_1))
{
// Set Properties.
extendedVersion(extVersion);
@@ -75,14 +85,37 @@
RequestedActivations
requestedActivation(RequestedActivations value) override;
+ /** @brief Version id */
+ std::string versionId;
+
+ private:
+ /** @brief Check if systemd state change is relevant to this object
+ *
+ * Instance specific interface to handle the detected systemd state
+ * change
+ *
+ * @param[in] msg - Data associated with subscribed signal
+ *
+ */
+ void unitStateChange(sdbusplus::message::message& msg);
+
+ /** @brief Start PSU update */
+ void startActivation();
+
+ /** @brief Finish PSU update */
+ void finishActivation();
+
/** @brief Persistent sdbusplus DBus bus connection */
sdbusplus::bus::bus& bus;
/** @brief Persistent DBus object path */
std::string path;
- /** @brief Version id */
- std::string versionId;
+ /** @brief Used to subscribe to dbus systemd signals */
+ sdbusplus::bus::match_t systemdSignals;
+
+ /** @brief The PSU update systemd unit */
+ std::string psuUpdateUnit;
};
} // namespace updater