Add activation, item_updater and version

Implement part of functions of Activation, ItemUpdater and Version.

Tested: Upload a dummy PSU tarball, and verify the activation object is
        created with expected ExtendedVersion, and the object is deleted
        when Delete is invoked.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I7b9d29f46914ace93d27a715b32c80957e88a0aa
diff --git a/src/activation.cpp b/src/activation.cpp
index e69de29..8bfed0b 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -0,0 +1,27 @@
+#include "activation.hpp"
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+
+namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
+
+auto Activation::activation(Activations value) -> Activations
+{
+    // TODO
+    return softwareServer::Activation::activation(value);
+}
+
+auto Activation::requestedActivation(RequestedActivations value)
+    -> RequestedActivations
+{
+    // TODO
+    return softwareServer::Activation::requestedActivation(value);
+}
+
+} // namespace updater
+} // namespace software
+} // namespace phosphor
diff --git a/src/activation.hpp b/src/activation.hpp
new file mode 100644
index 0000000..6ba2b75
--- /dev/null
+++ b/src/activation.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "config.h"
+
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Software/Activation/server.hpp>
+#include <xyz/openbmc_project/Software/ExtendedVersion/server.hpp>
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+
+using ActivationInherit = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Software::server::ExtendedVersion,
+    sdbusplus::xyz::openbmc_project::Software::server::Activation>;
+
+/** @class Activation
+ *  @brief OpenBMC activation software management implementation.
+ *  @details A concrete implementation for
+ *  xyz.openbmc_project.Software.Activation DBus API.
+ */
+class Activation : public ActivationInherit
+{
+  public:
+    /** @brief Constructs Activation Software Manager
+     *
+     * @param[in] bus    - The Dbus bus object
+     * @param[in] path   - The Dbus object path
+     * @param[in] versionId  - The software version id
+     * @param[in] extVersion - The extended version
+     * @param[in] activationStatus - The status of Activation
+     */
+    Activation(sdbusplus::bus::bus& bus, const std::string& path,
+               const std::string& versionId, const std::string& extVersion,
+               sdbusplus::xyz::openbmc_project::Software::server::Activation::
+                   Activations activationStatus) :
+        ActivationInherit(bus, path.c_str(), true),
+        bus(bus), path(path), versionId(versionId)
+    {
+        // Set Properties.
+        extendedVersion(extVersion);
+        activation(activationStatus);
+
+        // Emit deferred signal.
+        emit_object_added();
+    }
+
+    /** @brief Overloaded Activation property setter function
+     *
+     * @param[in] value - One of Activation::Activations
+     *
+     * @return Success or exception thrown
+     */
+    Activations activation(Activations value) override;
+
+    /** @brief Activation */
+    using ActivationInherit::activation;
+
+    /** @brief Overloaded requestedActivation property setter function
+     *
+     * @param[in] value - One of Activation::RequestedActivations
+     *
+     * @return Success or exception thrown
+     */
+    RequestedActivations
+        requestedActivation(RequestedActivations value) override;
+
+    /** @brief Persistent sdbusplus DBus bus connection */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Persistent DBus object path */
+    std::string path;
+
+    /** @brief Version id */
+    std::string versionId;
+};
+
+} // namespace updater
+} // namespace software
+} // namespace phosphor
diff --git a/src/item_updater.cpp b/src/item_updater.cpp
new file mode 100644
index 0000000..9a08f2e
--- /dev/null
+++ b/src/item_updater.cpp
@@ -0,0 +1,173 @@
+#include "config.h"
+
+#include "item_updater.hpp"
+
+#include <filesystem>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+namespace server = sdbusplus::xyz::openbmc_project::Software::server;
+namespace fs = std::filesystem;
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+
+void ItemUpdater::createActivation(sdbusplus::message::message& m)
+{
+    using SVersion = server::Version;
+    using VersionPurpose = SVersion::VersionPurpose;
+    namespace msg = sdbusplus::message;
+    namespace variant_ns = msg::variant_ns;
+
+    sdbusplus::message::object_path objPath;
+    std::map<std::string, std::map<std::string, msg::variant<std::string>>>
+        interfaces;
+    m.read(objPath, interfaces);
+
+    std::string path(std::move(objPath));
+    std::string filePath;
+    auto purpose = VersionPurpose::Unknown;
+    std::string version;
+
+    for (const auto& [interfaceName, propertyMap] : interfaces)
+    {
+        if (interfaceName == VERSION_IFACE)
+        {
+            for (const auto& [propertyName, propertyValue] : propertyMap)
+            {
+                if (propertyName == "Purpose")
+                {
+                    // Only process the PSU images
+                    auto value = SVersion::convertVersionPurposeFromString(
+                        variant_ns::get<std::string>(propertyValue));
+
+                    if (value == VersionPurpose::PSU)
+                    {
+                        purpose = value;
+                    }
+                }
+                else if (propertyName == "Version")
+                {
+                    version = variant_ns::get<std::string>(propertyValue);
+                }
+            }
+        }
+        else if (interfaceName == FILEPATH_IFACE)
+        {
+            const auto& it = propertyMap.find("Path");
+            if (it != propertyMap.end())
+            {
+                filePath = variant_ns::get<std::string>(it->second);
+            }
+        }
+    }
+    if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
+    {
+        return;
+    }
+
+    // Version id is the last item in the path
+    auto pos = path.rfind("/");
+    if (pos == std::string::npos)
+    {
+        log<level::ERR>("No version id found in object path",
+                        entry("OBJPATH=%s", path.c_str()));
+        return;
+    }
+
+    auto versionId = path.substr(pos + 1);
+
+    if (activations.find(versionId) == activations.end())
+    {
+        // Determine the Activation state by processing the given image dir.
+        auto activationState = server::Activation::Activations::Ready;
+
+        fs::path manifestPath(filePath);
+        manifestPath /= MANIFEST_FILE;
+        std::string extendedVersion =
+            (Version::getValue(
+                 manifestPath.string(),
+                 std::map<std::string, std::string>{{"extended_version", ""}}))
+                .begin()
+                ->second;
+
+        auto activation = createActivationObject(
+            path, versionId, extendedVersion, activationState);
+        activations.emplace(versionId, std::move(activation));
+
+        auto versionPtr =
+            createVersionObject(path, versionId, version, purpose, filePath);
+        versions.emplace(versionId, std::move(versionPtr));
+    }
+    return;
+}
+
+void ItemUpdater::erase(std::string versionId)
+{
+    auto it = versions.find(versionId);
+    if (it == versions.end())
+    {
+        log<level::ERR>(("Error: Failed to find version " + versionId +
+                         " in item updater versions map."
+                         " Unable to remove.")
+                            .c_str());
+    }
+    else
+    {
+        versions.erase(versionId);
+    }
+
+    // Removing entry in activations map
+    auto ita = activations.find(versionId);
+    if (ita == activations.end())
+    {
+        log<level::ERR>(("Error: Failed to find version " + versionId +
+                         " in item updater activations map."
+                         " Unable to remove.")
+                            .c_str());
+    }
+    else
+    {
+        activations.erase(versionId);
+    }
+}
+
+void ItemUpdater::deleteAll()
+{
+    // TODO: when PSU's running firmware is implemented, delete all versions
+    // that are not the running firmware.
+}
+
+std::unique_ptr<Activation> ItemUpdater::createActivationObject(
+    const std::string& path, const std::string& versionId,
+    const std::string& extVersion,
+    sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
+        activationStatus)
+{
+    return std::make_unique<Activation>(bus, path, versionId, extVersion,
+                                        activationStatus);
+}
+
+std::unique_ptr<Version> ItemUpdater::createVersionObject(
+    const std::string& objPath, const std::string& versionId,
+    const std::string& versionString,
+    sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
+        versionPurpose,
+    const std::string& filePath)
+{
+    auto version = std::make_unique<Version>(
+        bus, objPath, versionId, versionString, versionPurpose, filePath,
+        std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
+    return version;
+}
+
+} // namespace updater
+} // namespace software
+} // namespace phosphor
diff --git a/src/item_updater.hpp b/src/item_updater.hpp
new file mode 100644
index 0000000..bc86af1
--- /dev/null
+++ b/src/item_updater.hpp
@@ -0,0 +1,97 @@
+#pragma once
+
+#include "config.h"
+
+#include "activation.hpp"
+#include "version.hpp"
+
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+
+class Version;
+
+using ItemUpdaterInherit = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>;
+namespace MatchRules = sdbusplus::bus::match::rules;
+
+/** @class ItemUpdater
+ *  @brief Manages the activation of the PSU version items.
+ */
+class ItemUpdater : public ItemUpdaterInherit
+{
+  public:
+    /** @brief Constructs ItemUpdater
+     *
+     * @param[in] bus    - The D-Bus bus object
+     * @param[in] path   - The D-Bus path
+     */
+    ItemUpdater(sdbusplus::bus::bus& bus, const std::string& path) :
+        ItemUpdaterInherit(bus, path.c_str()), bus(bus),
+        versionMatch(bus,
+                     MatchRules::interfacesAdded() +
+                         MatchRules::path(SOFTWARE_OBJPATH),
+                     std::bind(std::mem_fn(&ItemUpdater::createActivation),
+                               this, std::placeholders::_1))
+    {
+    }
+
+    /** @brief Deletes version
+     *
+     *  @param[in] versionId - Id of the version to delete
+     */
+    void erase(std::string versionId);
+
+    /**
+     * @brief Erases any non-active versions.
+     */
+    void deleteAll();
+
+  private:
+    /** @brief Callback function for Software.Version match.
+     *  @details Creates an Activation D-Bus object.
+     *
+     * @param[in]  msg       - Data associated with subscribed signal
+     */
+    void createActivation(sdbusplus::message::message& msg);
+
+    /** @brief Create Activation object */
+    std::unique_ptr<Activation> createActivationObject(
+        const std::string& path, const std::string& versionId,
+        const std::string& extVersion,
+        sdbusplus::xyz::openbmc_project::Software::server::Activation::
+            Activations activationStatus);
+
+    /** @brief Create Version object */
+    std::unique_ptr<Version>
+        createVersionObject(const std::string& objPath,
+                            const std::string& versionId,
+                            const std::string& versionString,
+                            sdbusplus::xyz::openbmc_project::Software::server::
+                                Version::VersionPurpose versionPurpose,
+                            const std::string& filePath);
+
+    /** @brief Persistent sdbusplus D-Bus bus connection. */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Persistent map of Activation D-Bus objects and their
+     * version id */
+    std::map<std::string, std::unique_ptr<Activation>> activations;
+
+    /** @brief Persistent map of Version D-Bus objects and their
+     * version id */
+    std::map<std::string, std::unique_ptr<Version>> versions;
+
+    /** @brief sdbusplus signal match for Software.Version */
+    sdbusplus::bus::match_t versionMatch;
+};
+
+} // namespace updater
+} // namespace software
+} // namespace phosphor
diff --git a/src/main.cpp b/src/main.cpp
index 905869d..fe9062e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,27 @@
-int main()
+#include "config.h"
+
+#include "item_updater.hpp"
+
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/manager.hpp>
+#include <system_error>
+
+int main(int /* argc */, char* /* argv */[])
 {
+    auto bus = sdbusplus::bus::new_default();
+
+    // Add sdbusplus ObjectManager.
+    sdbusplus::server::manager::manager objManager(bus, SOFTWARE_OBJPATH);
+
+    phosphor::software::updater::ItemUpdater updater(bus, SOFTWARE_OBJPATH);
+
+    bus.request_name(BUSNAME_UPDATER);
+
+    while (true)
+    {
+        bus.process_discard();
+        bus.wait();
+    }
     return 0;
 }
diff --git a/src/meson.build b/src/meson.build
index 85263c8..4a13228 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -2,11 +2,27 @@
 phosphor_logging = dependency('phosphor-logging')
 sdbusplus = dependency('sdbusplus')
 
+add_project_link_arguments(['-lstdc++fs'], language: 'cpp')
+
+cdata = configuration_data()
+cdata.set_quoted('SOFTWARE_OBJPATH', '/xyz/openbmc_project/software')
+cdata.set_quoted('VERSION_IFACE', 'xyz.openbmc_project.Software.Version')
+cdata.set_quoted('FILEPATH_IFACE', 'xyz.openbmc_project.Common.FilePath')
+cdata.set_quoted('BUSNAME_UPDATER', 'xyz.openbmc_project.Software.Psu.Updater')
+cdata.set_quoted('MANIFEST_FILE', 'MANIFEST')
+
+configure_file(output: 'config.h',
+  configuration: cdata,
+)
+configuration_inc = include_directories('.')
+
 executable(
   'phosphor-psu-code-manager',
   'activation.cpp',
+  'item_updater.cpp',
   'main.cpp',
-  implicit_include_directories: false,
+  'version.cpp',
+  include_directories: configuration_inc,
   dependencies: [
     phosphor_logging,
     phosphor_dbus_interfaces,
diff --git a/src/version.cpp b/src/version.cpp
new file mode 100644
index 0000000..7940d2e
--- /dev/null
+++ b/src/version.cpp
@@ -0,0 +1,81 @@
+#include "version.hpp"
+
+#include "item_updater.hpp"
+
+#include <fstream>
+#include <iostream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+
+std::map<std::string, std::string>
+    Version::getValue(const std::string& filePath,
+                      std::map<std::string, std::string> keys)
+{
+    if (filePath.empty())
+    {
+        log<level::ERR>("Error filePath is empty");
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("FilePath"),
+                              Argument::ARGUMENT_VALUE(filePath.c_str()));
+    }
+
+    std::ifstream efile;
+    std::string line;
+    efile.exceptions(std::ifstream::failbit | std::ifstream::badbit |
+                     std::ifstream::eofbit);
+
+    try
+    {
+        efile.open(filePath);
+        while (getline(efile, line))
+        {
+            for (auto& key : keys)
+            {
+                auto value = key.first + "=";
+                auto keySize = value.length();
+                if (line.compare(0, keySize, value) == 0)
+                {
+                    key.second = line.substr(keySize);
+                    break;
+                }
+            }
+        }
+        efile.close();
+    }
+    catch (const std::exception& e)
+    {
+        if (!efile.eof())
+        {
+            log<level::ERR>("Error in reading file");
+        }
+        efile.close();
+    }
+
+    return keys;
+}
+
+void Delete::delete_()
+{
+    if (version.eraseCallback)
+    {
+        version.eraseCallback(version.getVersionId());
+    }
+}
+
+} // namespace updater
+} // namespace software
+} // namespace phosphor
diff --git a/src/version.hpp b/src/version.hpp
new file mode 100644
index 0000000..102d26a
--- /dev/null
+++ b/src/version.hpp
@@ -0,0 +1,154 @@
+#pragma once
+
+#include "config.h"
+
+#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/Common/FilePath/server.hpp>
+#include <xyz/openbmc_project/Object/Delete/server.hpp>
+#include <xyz/openbmc_project/Software/Version/server.hpp>
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+
+using eraseFunc = std::function<void(std::string)>;
+
+using VersionInherit = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Software::server::Version,
+    sdbusplus::xyz::openbmc_project::Common::server::FilePath>;
+using DeleteInherit = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Object::server::Delete>;
+
+namespace sdbusRule = sdbusplus::bus::match::rules;
+
+class Version;
+
+/** @class Delete
+ *  @brief OpenBMC Delete implementation.
+ *  @details A concrete implementation for xyz.openbmc_project.Object.Delete
+ *  D-Bus API.
+ */
+class Delete : public DeleteInherit
+{
+  public:
+    /** @brief Constructs Delete.
+     *
+     *  @param[in] bus    - The D-Bus bus object
+     *  @param[in] path   - The D-Bus object path
+     *  @param[in] version - The Version object.
+     */
+    Delete(sdbusplus::bus::bus& bus, const std::string& path,
+           Version& version) :
+        DeleteInherit(bus, path.c_str(), true),
+        version(version), bus(bus), path(path)
+    {
+        std::vector<std::string> interfaces({interface});
+        bus.emit_interfaces_added(path.c_str(), interfaces);
+    }
+
+    ~Delete()
+    {
+        std::vector<std::string> interfaces({interface});
+        bus.emit_interfaces_removed(path.c_str(), interfaces);
+    }
+
+    /**
+     * @brief Delete the D-Bus object.
+     *        Overrides the default delete function by calling
+     *        Version class erase Method.
+     **/
+    void delete_() override;
+
+  private:
+    /** @brief The Version Object. */
+    Version& version;
+
+    static constexpr auto interface = "xyz.openbmc_project.Object.Delete";
+    sdbusplus::bus::bus& bus;
+    std::string path;
+};
+
+/** @class Version
+ *  @brief OpenBMC version software management implementation.
+ *  @details A concrete implementation for xyz.openbmc_project.Software.Version
+ *  D-Bus API.
+ */
+class Version : public VersionInherit
+{
+  public:
+    /** @brief Constructs Version Software Manager.
+     *
+     * @param[in] bus            - The D-Bus bus object
+     * @param[in] objPath        - The D-Bus object path
+     * @param[in] versionId      - The version Id
+     * @param[in] versionString  - The version string
+     * @param[in] versionPurpose - The version purpose
+     * @param[in] filePath       - The image filesystem path
+     * @param[in] callback       - The eraseFunc callback
+     */
+    Version(sdbusplus::bus::bus& bus, const std::string& objPath,
+            const std::string& versionId, const std::string& versionString,
+            VersionPurpose versionPurpose, const std::string& filePath,
+            eraseFunc callback) :
+        VersionInherit(bus, (objPath).c_str(), true),
+        eraseCallback(callback), bus(bus), objPath(objPath),
+        versionId(versionId), versionStr(versionString)
+    {
+        // Set properties.
+        purpose(versionPurpose);
+        version(versionString);
+        path(filePath);
+
+        deleteObject = std::make_unique<Delete>(bus, objPath, *this);
+
+        // Emit deferred signal.
+        emit_object_added();
+    }
+
+    /**
+     * @brief Return the version id
+     */
+    std::string getVersionId() const
+    {
+        return versionId;
+    }
+
+    /**
+     * @brief Read the manifest file to get the value of the key.
+     *
+     * @param[in] filePath - The path to the file which contains the value
+     *                       of keys.
+     * @param[in] keys     - A map of keys with empty values.
+     *
+     * @return The map of keys with filled values.
+     **/
+    static std::map<std::string, std::string>
+        getValue(const std::string& filePath,
+                 std::map<std::string, std::string> keys);
+
+    /** @brief Persistent Delete D-Bus object */
+    std::unique_ptr<Delete> deleteObject;
+
+    /** @brief The temUpdater's erase callback. */
+    eraseFunc eraseCallback;
+
+  private:
+    /** @brief Persistent sdbusplus DBus bus connection */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Persistent DBus object path */
+    std::string objPath;
+
+    /** @brief This Version's version Id */
+    const std::string versionId;
+
+    /** @brief This Version's version string */
+    const std::string versionStr;
+};
+
+} // namespace updater
+} // namespace software
+} // namespace phosphor