Add settings application

Implement settings d-bus interfaces.

Define a settings policy file (this commit checks in an example YAML
based policy), based on which code for a settings manager will be
generated via a python script.
This settings manager composes and places desired settings objects on
the bus.

The policy file can be supplied by a system specific bitbake recipe.

Resolves openbmc/openbmc#1487.

Change-Id: Ice0d3b319d9466824cef323a6915eb20ca5cae5c
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
new file mode 100755
index 0000000..65f95cb
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,33 @@
+AM_DEFAULT_SOURCE_EXT = .cpp
+
+BUILT_SOURCES = \
+	settings_manager.hpp
+CLEANFILES = \
+	settings_manager.hpp
+
+SETTINGS_GEN_SCRIPT ?= \
+	${abs_srcdir}/settings.py
+SETTINGS_GEN_SCRIPT_FILES ?= \
+	${abs_srcdir}/settings_example.yaml \
+	${abs_srcdir}/settings_manager.mako.hpp
+
+settings_manager.hpp: ${SETTINGS_GEN_SCRIPT} ${SETTINGS_GEN_SCRIPT_FILES}
+	$(AM_V_GEN)@SETTINGSGEN@
+
+# Build these headers, don't install them
+noinst_HEADERS = \
+	settings_manager.hpp
+
+sbin_PROGRAMS = \
+	phosphor-settings-manager
+
+phosphor_settings_manager_SOURCES = \
+	settings_main.cpp
+
+phosphor_settings_manager_CXXFLAGS = \
+	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
+	$(SDBUSPLUS_CFLAGS)
+
+phosphor_settings_manager_LDADD = \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+	$(SDBUSPLUS_LIBS)
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..f861496
--- /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"'
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..6b3191d
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,42 @@
+AC_PREREQ([2.69])
+AC_INIT([phosphor-settingsd], [1.0], [https://github.com/openbmc/phosphor-settingsd/issues])
+AC_LANG([C++])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_SILENT_RULES([yes])
+
+AC_PROG_CXX
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AM_PATH_PYTHON([2.7], [AC_SUBST([PYTHON], [echo "$PYTHON"])], [AC_MSG_ERROR(
+    [Could not find python-2.7 installed...python-2.7 is required])])
+
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],,\
+    AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."]))
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,
+    AC_MSG_ERROR(["Requires sdbusplus package."]))
+
+LT_INIT
+
+AX_CXX_COMPILE_STDCXX_14([noext])
+AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+
+AC_ARG_VAR(SETTINGS_BUSNAME, [The Dbus busname to own])
+AS_IF([test "x$SETTINGS_BUSNAME" == "x"],\
+    [SETTINGS_BUSNAME="xyz.openbmc_project.Settings"])
+AC_DEFINE_UNQUOTED([SETTINGS_BUSNAME], ["$SETTINGS_BUSNAME"],\
+    [The DBus busname to own])
+
+AC_ARG_VAR(SETTINGS_ROOT, [The settings Dbus root])
+AS_IF([test "x$SETTINGS_ROOT" == "x"],\
+    [SETTINGS_ROOT="/xyz/openbmc_project/settings"])
+AC_DEFINE_UNQUOTED([SETTINGS_ROOT], ["$SETTINGS_ROOT"],\
+    [The settings Dbus root])
+
+AS_IF([test "x$SETTINGS_YAML" == "x"], [SETTINGS_YAML="settings_example.yaml"])
+SETTINGSGEN="$PYTHON $srcdir/settings.py -i $SETTINGS_YAML"
+AC_SUBST(SETTINGSGEN)
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/settings.py b/settings.py
new file mode 100755
index 0000000..adee094
--- /dev/null
+++ b/settings.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+import os
+import yaml
+from mako.template import Template
+import argparse
+
+def main():
+    parser = argparse.ArgumentParser(
+        description="Settings YAML parser and code generator")
+
+    parser.add_argument(
+        '-i', '--settings_yaml', dest='settings_yaml',
+        default='settings_example.yaml', help='settings yaml file to parse')
+    args = parser.parse_args()
+
+    with open(os.path.join(script_dir, args.settings_yaml), 'r') as fd:
+        yamlDict = yaml.safe_load(fd)
+
+        # Render the mako template
+        template = os.path.join(script_dir, 'settings_manager.mako.hpp')
+        t = Template(filename=template)
+        with open('settings_manager.hpp', 'w') as fd:
+            fd.write(
+                t.render(
+                    settingsDict=yamlDict))
+
+
+if __name__ == '__main__':
+    script_dir = os.path.dirname(os.path.realpath(__file__))
+    main()
diff --git a/settings_example.yaml b/settings_example.yaml
new file mode 100644
index 0000000..d6e7f88
--- /dev/null
+++ b/settings_example.yaml
@@ -0,0 +1,9 @@
+/xyz/openbmc_project/control/host0/boot_mode:
+    Interface: xyz.openbmc_project.Control.Boot.Mode
+    Defaults:
+        BootMode: Mode::Modes::Safe
+
+/xyz/openbmc_project/control/host1/boot_mode:
+    Interface: xyz.openbmc_project.Control.Boot.Mode
+    Defaults:
+        BootMode: Mode::Modes::Regular
diff --git a/settings_main.cpp b/settings_main.cpp
new file mode 100644
index 0000000..22d6146
--- /dev/null
+++ b/settings_main.cpp
@@ -0,0 +1,24 @@
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/manager.hpp>
+#include "settings_manager.hpp"
+#include "config.h"
+
+int main(int argc, char *argv[])
+{
+    auto bus = sdbusplus::bus::new_default();
+
+    // Add sdbusplus ObjectManager for the settings root.
+    sdbusplus::server::manager::manager objManager(bus, SETTINGS_ROOT);
+
+    phosphor::settings::Manager mgr(bus);
+
+    bus.request_name(SETTINGS_BUSNAME);
+
+    while(true)
+    {
+        bus.process_discard();
+        bus.wait();
+    }
+
+    return 0;
+}
diff --git a/settings_manager.mako.hpp b/settings_manager.mako.hpp
new file mode 100644
index 0000000..2f97985
--- /dev/null
+++ b/settings_manager.mako.hpp
@@ -0,0 +1,105 @@
+## This file is a template.  The comment below is emitted
+## into the rendered file; feel free to edit this file.
+// WARNING: Generated header. Do not edit!
+
+<%
+objects = list(settingsDict.viewkeys())
+
+def get_setting_type(setting_intf):
+    setting = "sdbusplus::" + setting_intf.replace('.', '::')
+    i = setting.rfind('::')
+    setting = setting[:i] + '::server::' + setting[i+2:]
+    return setting
+%>\
+#pragma once
+
+% for object in objects:
+<%
+    includes = []
+    include = settingsDict[object]['Interface']
+    include = include.replace('.', '/')
+    include = include + "/server.hpp"
+    includes.append(include)
+%>\
+% endfor
+% for i in set(includes):
+#include "${i}"
+% endfor
+
+% for object in objects:
+<%
+    ns_list = []
+    ns = get_setting_type(settingsDict[object]['Interface'])
+    i = ns.rfind('::')
+    ns = ns[:i]
+    ns_list.append(ns)
+%>\
+% endfor
+% for n in set(ns_list):
+using namespace ${n};
+% endfor
+
+namespace phosphor
+{
+namespace settings
+{
+
+/** @class Manager
+ *
+ *  @brief Compose settings objects and put them on the bus.
+ */
+class Manager
+{
+    public:
+        Manager() = delete;
+        Manager(const Manager&) = delete;
+        Manager& operator=(const Manager&) = delete;
+        Manager(Manager&&) = delete;
+        Manager& operator=(Manager&&) = delete;
+        virtual ~Manager() = default;
+
+        /** @brief Constructor to put settings objects on to the bus.
+         *  @param[in] bus - Bus to attach to.
+         */
+        Manager(sdbusplus::bus::bus& bus)
+        {
+            settings =
+                std::make_tuple(
+% for index, object in enumerate(objects):
+<% type = get_setting_type(settingsDict[object]['Interface']) %>\
+                    std::make_unique<${type}>(
+                        bus,
+  % if index < len(settingsDict) - 1:
+                        "${object}"),
+  % else:
+                        "${object}"));
+  % endif
+% endfor
+
+% for index, object in enumerate(objects):
+  % if 'Defaults' in settingsDict[object].viewkeys():
+    % for property, value in settingsDict[object]['Defaults'].items():
+            std::get<${index}>(settings)->
+                setPropertyByName("${property}", ${value});
+    % endfor
+  % endif
+            bus.emit_object_added("${object}");
+
+% endfor
+        }
+
+    private:
+        /* @brief Composition of settings objects. */
+        std::tuple<
+% for index, object in enumerate(objects):
+<% type = get_setting_type(settingsDict[object]['Interface']) %>\
+  % if index < len(settingsDict) - 1:
+            std::unique_ptr<${type}>,
+  % else:
+            std::unique_ptr<${type}>> settings;
+  % endif
+% endfor
+};
+
+} // namespace settings
+} // namespace phosphor