fw update: common code

new daemons to implement the flow as described in
https://github.com/openbmc/docs/blob/master/designs/code-update.md

- common/
  common code folder
  - common update flow
  - base class for the device specific update daemons

The new daemons are all following the generic template of Code Updater
daemon as outlined in the design.

The idea is that they are separate daemons (per device, as outlined in
the design) but share all the code that's not device specific.

Tested: next patch in series

Change-Id: If2438b8506aceb8c5313ec13a0bf7cb68f3cc279
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/common/src/software.cpp b/common/src/software.cpp
new file mode 100644
index 0000000..03e9199
--- /dev/null
+++ b/common/src/software.cpp
@@ -0,0 +1,174 @@
+#include "software.hpp"
+
+#include "device.hpp"
+#include "software_update.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/Software/Activation/aserver.hpp>
+#include <xyz/openbmc_project/Software/Update/aserver.hpp>
+#include <xyz/openbmc_project/State/Host/client.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+using namespace phosphor::software;
+using namespace phosphor::software::device;
+using namespace phosphor::software::config;
+using namespace phosphor::software::update;
+
+const static std::string baseObjPathSoftware = "/xyz/openbmc_project/software/";
+
+SoftwareActivationProgress::SoftwareActivationProgress(
+    sdbusplus::async::context& ctx, const char* objPath) :
+    ActivationProgress(ctx, objPath)
+{
+    // This prevents "Conditional jump or move depends on uninitialised
+    // value(s)"
+    // when properties are updated for the first time
+    progress_ = 0;
+}
+
+void SoftwareActivationProgress::setProgress(int progressArg)
+{
+    progress(progressArg);
+}
+
+Software::Software(sdbusplus::async::context& ctx, Device& parent) :
+    Software(ctx, parent, getRandomSoftwareId(parent))
+{}
+
+Software::Software(sdbusplus::async::context& ctx, Device& parent,
+                   const std::string& swid) :
+    SoftwareActivation(ctx, (baseObjPathSoftware + swid).c_str()),
+    objectPath(baseObjPathSoftware + swid), parentDevice(parent), swid(swid),
+    ctx(ctx)
+{
+    // initialize the members of our base class to prevent
+    // "Conditional jump or move depends on uninitialised value(s)"
+    activation_ = Activations::NotReady;
+    requested_activation_ = RequestedActivations::None;
+
+    std::string objPath = baseObjPathSoftware + swid;
+
+    debug("{SWID}: created dbus interfaces on path {OBJPATH}", "SWID", swid,
+          "OBJPATH", objPath);
+};
+
+static long int getRandomId()
+{
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+    unsigned int seed = ts.tv_nsec ^ getpid();
+    srandom(seed);
+    return random() % 10000;
+}
+
+std::string Software::getRandomSoftwareId(Device& parent)
+{
+    return std::format("{}_{}", parent.config.configName, getRandomId());
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<> Software::createInventoryAssociations(bool isRunning)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+    debug("{SWID}: setting association definitions", "SWID", swid);
+
+    std::string endpoint = "";
+
+    try
+    {
+        endpoint = co_await parentDevice.config.getInventoryItemObjectPath(ctx);
+    }
+    catch (std::exception& e)
+    {
+        error(e.what());
+    }
+
+    if (!associationDefinitions)
+    {
+        std::string path = objectPath;
+        associationDefinitions =
+            std::make_unique<SoftwareAssociationDefinitions>(ctx, path.c_str());
+    }
+
+    std::vector<std::tuple<std::string, std::string, std::string>> assocs;
+
+    if (endpoint.empty())
+    {
+        associationDefinitions->associations(assocs);
+        co_return;
+    }
+
+    if (isRunning)
+    {
+        debug("{SWID}: creating 'running' association to {OBJPATH}", "SWID",
+              swid, "OBJPATH", endpoint);
+        std::tuple<std::string, std::string, std::string> assocRunning = {
+            "running", "ran_on", endpoint};
+        assocs.push_back(assocRunning);
+    }
+    else
+    {
+        debug("{SWID}: creating 'activating' association to {OBJPATH}", "SWID",
+              swid, "OBJPATH", endpoint);
+        std::tuple<std::string, std::string, std::string> assocActivating = {
+            "activating", "activated_on", endpoint};
+        assocs.push_back(assocActivating);
+    }
+
+    associationDefinitions->associations(assocs);
+
+    co_return;
+}
+
+void Software::setVersion(const std::string& versionStr)
+{
+    debug("{SWID}: set version {VERSION}", "SWID", swid, "VERSION", versionStr);
+
+    if (version)
+    {
+        error("{SWID}: version was already set", "SWID", swid);
+        return;
+    }
+
+    version = std::make_unique<SoftwareVersion>(ctx, objectPath.str.c_str());
+    version->version(versionStr);
+}
+
+void Software::setActivationBlocksTransition(bool enabled)
+{
+    if (!enabled)
+    {
+        activationBlocksTransition = nullptr;
+        return;
+    }
+
+    std::string path = objectPath;
+    activationBlocksTransition =
+        std::make_unique<SoftwareActivationBlocksTransition>(ctx, path.c_str());
+}
+
+void Software::setActivation(SoftwareActivation::Activations act)
+{
+    activation(act);
+}
+
+void Software::enableUpdate(
+    const std::set<RequestedApplyTimes>& allowedApplyTimes)
+{
+    if (updateIntf != nullptr)
+    {
+        error("[Software] update of {OBJPATH} has already been enabled",
+              "OBJPATH", objectPath);
+        return;
+    }
+
+    debug(
+        "[Software] enabling update of {OBJPATH} (adding the update interface)",
+        "OBJPATH", objectPath);
+
+    updateIntf = std::make_unique<SoftwareUpdate>(ctx, objectPath.str.c_str(),
+                                                  *this, allowedApplyTimes);
+}