Add base support for BMC code management

This is the starting code needed to build the application.

Change-Id: Ied9beee1ea4f12805622c17c99c01b89c3f74d50
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
new file mode 100755
index 0000000..daf5508
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,11 @@
+AM_DEFAULT_SOURCE_EXT = .cpp
+
+sbin_PROGRAMS = \
+	phosphor-version-software-manager
+
+phosphor_version_software_manager_SOURCES = \
+	version_software_manager.cpp \
+	xyz.openbmc_project.Software.Version.cpp \
+	version_software_manager_main.cpp
+
+phosphor_version_software_manager_LDFLAGS = $(SYSTEMD_LIBS)
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..9941c73
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,18 @@
+#!/bin/sh -xe
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+        config.guess config.h.in config.sub configure depcomp install-sh \
+        ltmain.sh missing *libtool test-driver"
+
+case $1 in
+    clean)
+        test -f Makefile && make maintainer-clean
+        for file in ${AUTOCONF_FILES}; do
+            find -name "$file" | xargs -r rm -rf
+        done
+        exit 0
+        ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
new file mode 100755
index 0000000..a7e9b9c
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,36 @@
+AC_PREREQ([2.69])
+AC_INIT([phosphor-bmc-code-mgmt], [1.0], [https://github.com/openbmc/phosphor-bmc-code-mgmt/issues])
+AC_LANG([C++])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_SILENT_RULES([yes])
+
+# Checks for programs
+AC_PROG_CXX
+AC_PROG_INSTALL #Checks/sets the install variable to be used
+AC_PROG_MAKE_SET
+
+# Check for libraries
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+
+# Checks for library functions
+LT_INIT # Required for systemd linking
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_CXX_COMPILE_STDCXX_14([noext])
+AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+
+AC_ARG_VAR(VERSION_BUSNAME, [The Dbus busname to own])
+AS_IF([test "x$VERSION_BUSNAME" == "x"], [VERSION_BUSNAME="xyz.openbmc_project.Software.BMC.Version"])
+AC_DEFINE_UNQUOTED([VERSION_BUSNAME], ["$VERSION_BUSNAME"], [The DBus busname to own])
+
+AC_ARG_VAR(SOFTWARE_OBJPATH, [The version software manager Dbus root])
+AS_IF([test "x$SOFTWARE_OBJPATH" == "x"], [SOFTWARE_OBJPATH="/xyz/openbmc_project/software"])
+AC_DEFINE_UNQUOTED([SOFTWARE_OBJPATH], ["$SOFTWARE_OBJPATH"], [The version software manager Dbus root])
+
+# Check for header files.
+AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
+AC_CHECK_HEADER(sdbusplus/server.hpp, ,[AC_MSG_ERROR([Could not find sdbusplus/server.hpp...openbmc/sdbusplus package required])])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/version_software_manager.cpp b/version_software_manager.cpp
new file mode 100644
index 0000000..7e49638
--- /dev/null
+++ b/version_software_manager.cpp
@@ -0,0 +1,57 @@
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <stdexcept>
+#include "version_software_manager.hpp"
+
+namespace phosphor
+{
+namespace software
+{
+namespace manager
+{
+
+const std::string Version::getVersion() const
+{
+    // Get version from /etc/os-release.
+    std::string versionKey = "VERSION_ID=";
+    std::string version{};
+    std::ifstream efile;
+    std::string line;
+    efile.open("/etc/os-release");
+
+    while (getline(efile, line))
+    {
+        if (line.substr(0, versionKey.size()).find(versionKey)
+            != std::string::npos)
+        {
+            // This line looks like VERSION_ID="v1.99.0-353-ga3b8a0a-dirty".
+            // So grab everything in quotes.
+            std::size_t pos = line.find_first_of('"') + 1;
+            version = line.substr(pos, line.find_last_of('"') - pos);
+            break;
+        }
+    }
+    efile.close();
+    return version;
+}
+
+const std::string Version::getId() const
+{
+    auto version = getVersion();
+    std::stringstream hexId;
+
+    if (version.empty())
+    {
+        throw std::runtime_error("Software version is empty");
+    }
+
+    // Only want 8 hex digits.
+    hexId << std::hex << ((std::hash<std::string> {}(version)) & 0xFFFFFFFF);
+    return hexId.str();
+}
+
+} // namespace manager
+} // namespace software
+} // namepsace phosphor
diff --git a/version_software_manager.hpp b/version_software_manager.hpp
new file mode 100644
index 0000000..45d9d82
--- /dev/null
+++ b/version_software_manager.hpp
@@ -0,0 +1,53 @@
+#pragma once
+#include <iostream>
+
+#include <sdbusplus/bus.hpp>
+#include "xyz/openbmc_project/Software/Version/server.hpp"
+
+namespace phosphor
+{
+namespace software
+{
+namespace manager
+{
+
+/** @class Version
+ *  @brief OpenBMC version software management implementation.
+ *  @details A concrete implementation for xyz.openbmc_project.Software.Version
+ *  DBus API.
+ */
+class Version : public sdbusplus::server::object::object<
+                sdbusplus::xyz::openbmc_project::Software::server::Version>
+{
+    public:
+        /** @brief Constructs Version Software Manager
+         *
+         * @param[in] bus       - The Dbus bus object
+         * @param[in] objPath   - The Dbus object path
+         */
+        Version(sdbusplus::bus::bus& bus,
+                const char* objPath) :
+                sdbusplus::server::object::object<
+                    sdbusplus::xyz::openbmc_project::Software::server::Version>
+                        (bus, (std::string{objPath} + '/' +
+                            getId()).c_str()) {};
+
+    private:
+        /**
+         * @brief Get the code version identifier.
+         *
+         * @return The version identifier.
+         **/
+        const std::string getVersion() const;
+
+        /**
+         * @brief Get the Software Version id.
+         *
+         * @return The id.
+         **/
+        const std::string getId() const;
+};
+
+} // namespace manager
+} // namespace software
+} // namespace phosphor
diff --git a/version_software_manager_main.cpp b/version_software_manager_main.cpp
new file mode 100644
index 0000000..bd41b63
--- /dev/null
+++ b/version_software_manager_main.cpp
@@ -0,0 +1,27 @@
+#include <iostream>
+#include <cstdlib>
+#include <exception>
+#include <sdbusplus/bus.hpp>
+#include "config.h"
+#include "version_software_manager.hpp"
+
+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::manager::Version manager(bus,
+            SOFTWARE_OBJPATH);
+
+    bus.request_name(VERSION_BUSNAME);
+
+    while (true)
+    {
+        bus.process_discard();
+        bus.wait();
+    }
+    return 0;
+}
diff --git a/xyz.openbmc_project.Software.Version.cpp b/xyz.openbmc_project.Software.Version.cpp
new file mode 100755
index 0000000..015447f
--- /dev/null
+++ b/xyz.openbmc_project.Software.Version.cpp
@@ -0,0 +1,237 @@
+#include <algorithm>
+#include <sdbusplus/server.hpp>
+#include <sdbusplus/exception.hpp>
+#include <xyz/openbmc_project/Software/Version/server.hpp>
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Software
+{
+namespace server
+{
+
+Version::Version(bus::bus& bus, const char* path)
+        : _xyz_openbmc_project_Software_Version_interface(
+                bus, path, _interface, _vtable, this)
+{
+}
+
+
+
+auto Version::version() const ->
+        std::string
+{
+    return _version;
+}
+
+int Version::_callback_get_Version(
+        sd_bus* bus, const char* path, const char* interface,
+        const char* property, sd_bus_message* reply, void* context,
+        sd_bus_error* error)
+{
+    using sdbusplus::server::binding::details::convertForMessage;
+
+    try
+    {
+        auto m = message::message(sd_bus_message_ref(reply));
+
+        auto o = static_cast<Version*>(context);
+        m.append(convertForMessage(o->version()));
+    }
+    catch(sdbusplus::internal_exception_t& e)
+    {
+        sd_bus_error_set_const(error, e.name(), e.description());
+        return -EINVAL;
+    }
+
+    return true;
+}
+
+auto Version::version(std::string value) ->
+        std::string
+{
+    if (_version != value)
+    {
+        _version = value;
+        _xyz_openbmc_project_Software_Version_interface.property_changed("Version");
+    }
+
+    return _version;
+}
+
+int Version::_callback_set_Version(
+        sd_bus* bus, const char* path, const char* interface,
+        const char* property, sd_bus_message* value, void* context,
+        sd_bus_error* error)
+{
+    try
+    {
+        auto m = message::message(sd_bus_message_ref(value));
+
+        auto o = static_cast<Version*>(context);
+
+        std::string v{};
+        m.read(v);
+        o->version(v);
+    }
+    catch(sdbusplus::internal_exception_t& e)
+    {
+        sd_bus_error_set_const(error, e.name(), e.description());
+        return -EINVAL;
+    }
+
+    return true;
+}
+
+namespace details
+{
+namespace Version
+{
+static const auto _property_Version =
+    utility::tuple_to_array(message::types::type_id<
+            std::string>());
+}
+}
+auto Version::purpose() const ->
+        VersionPurpose
+{
+    return _purpose;
+}
+
+int Version::_callback_get_Purpose(
+        sd_bus* bus, const char* path, const char* interface,
+        const char* property, sd_bus_message* reply, void* context,
+        sd_bus_error* error)
+{
+    using sdbusplus::server::binding::details::convertForMessage;
+
+    try
+    {
+        auto m = message::message(sd_bus_message_ref(reply));
+
+        auto o = static_cast<Version*>(context);
+        m.append(convertForMessage(o->purpose()));
+    }
+    catch(sdbusplus::internal_exception_t& e)
+    {
+        sd_bus_error_set_const(error, e.name(), e.description());
+        return -EINVAL;
+    }
+
+    return true;
+}
+
+auto Version::purpose(VersionPurpose value) ->
+        VersionPurpose
+{
+    if (_purpose != value)
+    {
+        _purpose = value;
+        _xyz_openbmc_project_Software_Version_interface.property_changed("Purpose");
+    }
+
+    return _purpose;
+}
+
+int Version::_callback_set_Purpose(
+        sd_bus* bus, const char* path, const char* interface,
+        const char* property, sd_bus_message* value, void* context,
+        sd_bus_error* error)
+{
+    try
+    {
+        auto m = message::message(sd_bus_message_ref(value));
+
+        auto o = static_cast<Version*>(context);
+
+        std::string v{};
+        m.read(v);
+        o->purpose(convertVersionPurposeFromString(v));
+    }
+    catch(sdbusplus::internal_exception_t& e)
+    {
+        sd_bus_error_set_const(error, e.name(), e.description());
+        return -EINVAL;
+    }
+
+    return true;
+}
+
+namespace details
+{
+namespace Version
+{
+static const auto _property_Purpose =
+    utility::tuple_to_array(message::types::type_id<
+            std::string>());
+}
+}
+
+
+namespace
+{
+/** String to enum mapping for Version::VersionPurpose */
+static const std::tuple<const char*, Version::VersionPurpose> mappingVersionVersionPurpose[] =
+        {
+            std::make_tuple( "xyz.openbmc_project.Software.Version.VersionPurpose.Unknown",                 Version::VersionPurpose::Unknown ),
+            std::make_tuple( "xyz.openbmc_project.Software.Version.VersionPurpose.Other",                 Version::VersionPurpose::Other ),
+            std::make_tuple( "xyz.openbmc_project.Software.Version.VersionPurpose.System",                 Version::VersionPurpose::System ),
+            std::make_tuple( "xyz.openbmc_project.Software.Version.VersionPurpose.BMC",                 Version::VersionPurpose::BMC ),
+            std::make_tuple( "xyz.openbmc_project.Software.Version.VersionPurpose.Host",                 Version::VersionPurpose::Host ),
+        };
+
+} // anonymous namespace
+
+auto Version::convertVersionPurposeFromString(std::string& s) ->
+        VersionPurpose
+{
+    auto i = std::find_if(
+            std::begin(mappingVersionVersionPurpose),
+            std::end(mappingVersionVersionPurpose),
+            [&s](auto& e){ return 0 == strcmp(s.c_str(), std::get<0>(e)); } );
+    if (std::end(mappingVersionVersionPurpose) == i)
+    {
+        throw sdbusplus::exception::InvalidEnumString();
+    }
+    else
+    {
+        return std::get<1>(*i);
+    }
+}
+
+std::string convertForMessage(Version::VersionPurpose v)
+{
+    auto i = std::find_if(
+            std::begin(mappingVersionVersionPurpose),
+            std::end(mappingVersionVersionPurpose),
+            [v](auto& e){ return v == std::get<1>(e); });
+    return std::get<0>(*i);
+}
+
+const vtable::vtable_t Version::_vtable[] = {
+    vtable::start(),
+    vtable::property("Version",
+                     details::Version::_property_Version
+                        .data(),
+                     _callback_get_Version,
+                     _callback_set_Version,
+                     vtable::property_::emits_change),
+    vtable::property("Purpose",
+                     details::Version::_property_Purpose
+                        .data(),
+                     _callback_get_Purpose,
+                     _callback_set_Purpose,
+                     vtable::property_::emits_change),
+    vtable::end()
+};
+
+} // namespace server
+} // namespace Software
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
diff --git a/xyz/openbmc_project/Software/Version/server.hpp b/xyz/openbmc_project/Software/Version/server.hpp
new file mode 100755
index 0000000..d92b48a
--- /dev/null
+++ b/xyz/openbmc_project/Software/Version/server.hpp
@@ -0,0 +1,115 @@
+#pragma once
+#include <tuple>
+#include <systemd/sd-bus.h>
+#include <sdbusplus/server.hpp>
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Software
+{
+namespace server
+{
+
+class Version
+{
+    public:
+        /* Define all of the basic class operations:
+         *     Not allowed:
+         *         - Default constructor to avoid nullptrs.
+         *         - Copy operations due to internal unique_ptr.
+         *         - Move operations due to 'this' being registered as the
+         *           'context' with sdbus.
+         *     Allowed:
+         *         - Destructor.
+         */
+        Version() = delete;
+        Version(const Version&) = delete;
+        Version& operator=(const Version&) = delete;
+        Version(Version&&) = delete;
+        Version& operator=(Version&&) = delete;
+        virtual ~Version() = default;
+
+        /** @brief Constructor to put object onto bus at a dbus path.
+         *  @param[in] bus - Bus to attach to.
+         *  @param[in] path - Path to attach at.
+         */
+        Version(bus::bus& bus, const char* path);
+
+        enum class VersionPurpose
+        {
+            Unknown,
+            Other,
+            System,
+            BMC,
+            Host,
+        };
+
+
+
+        /** Get value of Version */
+        virtual std::string version() const;
+        /** Set value of Version */
+        virtual std::string version(std::string value);
+        /** Get value of Purpose */
+        virtual VersionPurpose purpose() const;
+        /** Set value of Purpose */
+        virtual VersionPurpose purpose(VersionPurpose value);
+
+    /** @brief Convert a string to an appropriate enum value.
+     *  @param[in] s - The string to convert in the form of
+     *                 "xyz.openbmc_project.Software.Version.<value name>"
+     *  @return - The enum value.
+     */
+    static VersionPurpose convertVersionPurposeFromString(std::string& s);
+
+    private:
+
+        /** @brief sd-bus callback for get-property 'Version' */
+        static int _callback_get_Version(
+            sd_bus*, const char*, const char*, const char*,
+            sd_bus_message*, void*, sd_bus_error*);
+        /** @brief sd-bus callback for set-property 'Version' */
+        static int _callback_set_Version(
+            sd_bus*, const char*, const char*, const char*,
+            sd_bus_message*, void*, sd_bus_error*);
+
+        /** @brief sd-bus callback for get-property 'Purpose' */
+        static int _callback_get_Purpose(
+            sd_bus*, const char*, const char*, const char*,
+            sd_bus_message*, void*, sd_bus_error*);
+        /** @brief sd-bus callback for set-property 'Purpose' */
+        static int _callback_set_Purpose(
+            sd_bus*, const char*, const char*, const char*,
+            sd_bus_message*, void*, sd_bus_error*);
+
+
+        static constexpr auto _interface = "xyz.openbmc_project.Software.Version";
+        static const vtable::vtable_t _vtable[];
+        sdbusplus::server::interface::interface
+                _xyz_openbmc_project_Software_Version_interface;
+
+        std::string _version{};
+        VersionPurpose _purpose{};
+
+};
+
+/* Specialization of sdbusplus::server::bindings::details::convertForMessage
+ * for enum-type Version::VersionPurpose.
+ *
+ * This converts from the enum to a constant c-string representing the enum.
+ *
+ * @param[in] e - Enum value to convert.
+ * @return C-string representing the name for the enum value.
+ */
+std::string convertForMessage(Version::VersionPurpose e);
+
+} // namespace server
+} // namespace Software
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+