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_update.cpp b/common/src/software_update.cpp
new file mode 100644
index 0000000..26a4602
--- /dev/null
+++ b/common/src/software_update.cpp
@@ -0,0 +1,103 @@
+#include "software_update.hpp"
+
+#include "device.hpp"
+#include "software.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Software/Update/aserver.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+using Unavailable = sdbusplus::xyz::openbmc_project::Common::Error::Unavailable;
+
+using namespace phosphor::logging;
+using namespace phosphor::software::update;
+using namespace phosphor::software::device;
+using namespace phosphor::software;
+
+namespace SoftwareLogging = phosphor::logging::xyz::openbmc_project::software;
+namespace SoftwareErrors =
+    sdbusplus::error::xyz::openbmc_project::software::image;
+
+SoftwareUpdate::SoftwareUpdate(
+    sdbusplus::async::context& ctx, const char* path, Software& software,
+    const std::set<RequestedApplyTimes>& allowedApplyTimes) :
+    sdbusplus::aserver::xyz::openbmc_project::software::Update<SoftwareUpdate>(
+        ctx, path),
+    software(software), allowedApplyTimes(allowedApplyTimes)
+{}
+
+auto SoftwareUpdate::method_call(start_update_t /*unused*/, auto image,
+                                 auto applyTime)
+    -> sdbusplus::async::task<start_update_t::return_type>
+{
+    debug("Requesting Image update with {FD}", "FD", image.fd);
+
+    Device& device = software.parentDevice;
+
+    if (device.updateInProgress)
+    {
+        error("An update is already in progress, cannot update.");
+        report<Unavailable>();
+        co_return sdbusplus::message::object_path();
+    }
+
+    device.updateInProgress = true;
+
+    // check if the apply time is allowed by our device
+    if (!allowedApplyTimes.contains(applyTime))
+    {
+        error(
+            "the selected apply time {APPLYTIME} is not allowed by the device",
+            "APPLYTIME", applyTime);
+        device.updateInProgress = false;
+        report<Unavailable>();
+        co_return sdbusplus::message::object_path();
+    }
+
+    debug("started asynchronous update with fd {FD}", "FD", image.fd);
+
+    int imageDup = dup(image.fd);
+
+    if (imageDup < 0)
+    {
+        error("ERROR calling dup on fd: {ERR}", "ERR", strerror(errno));
+        device.updateInProgress = false;
+        co_return software.objectPath;
+    }
+
+    debug("starting async update with FD: {FD}\n", "FD", imageDup);
+
+    std::unique_ptr<Software> softwareInstance =
+        std::make_unique<Software>(ctx, device);
+
+    softwareInstance->setActivation(ActivationInterface::Activations::NotReady);
+
+    std::string newObjPath = softwareInstance->objectPath;
+
+    // NOLINTBEGIN(readability-static-accessed-through-instance)
+    ctx.spawn(
+        [](Device& device, int imageDup, RequestedApplyTimes applyTime,
+           std::unique_ptr<Software> swupdate) -> sdbusplus::async::task<> {
+            co_await device.startUpdateAsync(imageDup, applyTime,
+                                             std::move(swupdate));
+            device.updateInProgress = false;
+            close(imageDup);
+            co_return;
+        }(device, imageDup, applyTime, std::move(softwareInstance)));
+    // NOLINTEND
+
+    // We need the object path for the new software here.
+    // It must be the same as constructed during the update process.
+    // This is so that bmcweb and redfish clients can keep track of the update
+    // process.
+    co_return newObjPath;
+}
+
+auto SoftwareUpdate::get_property(allowed_apply_times_t /*unused*/) const
+{
+    return allowedApplyTimes;
+}