RBMC: Create initial RBMC application

Create a new phosphor-rbmc-manager application and service file to go
along with it, hidden behind the rbmc-state-management meson option.

As described in https://gerrit.openbmc.org/c/openbmc/docs/+/70233, this
application will manage redundancy related information for redundant BMC
systems.

This initial commit will just immediately set the role to Passive.  It
does introduce the framework to determine the role and also includes a
basic test case.  Future commits can then build on this.

It is setup to use the coroutine versions of sdbusplus's D-Bus
bindings.

The idea for the role_determination::run() function is to eventually
pass in all of the pieces of information it would need to determine the
role, like BMC position, sibling presence, previous role, etc.  That
way, test cases can be created to easily vary the inputs to test all of
the code paths.

Tested:
```
$ busctl introspect xyz.openbmc_project.State.BMC.Redundancy  /xyz/openbmc_project/state/bmc/redundancy -l
NAME                                     TYPE      SIGNATURE  RESULT/VALUE                                            FLAGS
org.freedesktop.DBus.Introspectable      interface -          -                                                       -
.Introspect                              method    -          s                                                       -
org.freedesktop.DBus.ObjectManager       interface -          -                                                       -
.GetManagedObjects                       method    -          a{oa{sa{sv}}}                                           -
.InterfacesAdded                         signal    oa{sa{sv}} -                                                       -
.InterfacesRemoved                       signal    oas        -                                                       -
org.freedesktop.DBus.Peer                interface -          -                                                       -
.GetMachineId                            method    -          s                                                       -
.Ping                                    method    -          -                                                       -
org.freedesktop.DBus.Properties          interface -          -                                                       -
.Get                                     method    ss         v                                                       -
.GetAll                                  method    s          a{sv}                                                   -
.Set                                     method    ssv        -                                                       -
.PropertiesChanged                       signal    sa{sv}as   -                                                       -
xyz.openbmc_project.State.BMC.Redundancy interface -          -                                                       -
.RedundancyEnabled                       property  b          false                                                   emits-change
.Role                                    property  s          "xyz.openbmc_project.State.BMC.Redundancy.Role.Passive" emits-change
```

Change-Id: Ie159d46ac5bd9cf47792d55ebd5a2e4a811017e8
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/meson.build b/meson.build
index 12daf35..83beef5 100644
--- a/meson.build
+++ b/meson.build
@@ -371,3 +371,7 @@
       )
   )
 endif
+
+if get_option('rbmc-state-management').allowed()
+  subdir('redundant-bmc')
+endif
diff --git a/redundant-bmc/manager.cpp b/redundant-bmc/manager.cpp
new file mode 100644
index 0000000..7ad79b3
--- /dev/null
+++ b/redundant-bmc/manager.cpp
@@ -0,0 +1,32 @@
+#include "manager.hpp"
+
+#include "role_determination.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+namespace bmc::redundancy
+{
+
+Manager::Manager(sdbusplus::async::context& ctx) :
+    ctx(ctx),
+    redundancyInterface(ctx.get_bus(), RedundancyInterface::instance_path)
+{
+    ctx.spawn(startup());
+}
+
+sdbusplus::async::task<> Manager::startup()
+{
+    determineRole();
+    co_return;
+}
+
+void Manager::determineRole()
+{
+    auto role = role_determination::run();
+
+    lg2::info("Role Determined: {ROLE}", "ROLE", role);
+
+    redundancyInterface.role(role);
+}
+
+} // namespace bmc::redundancy
diff --git a/redundant-bmc/manager.hpp b/redundant-bmc/manager.hpp
new file mode 100644
index 0000000..c40dc91
--- /dev/null
+++ b/redundant-bmc/manager.hpp
@@ -0,0 +1,50 @@
+#pragma once
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/State/BMC/Redundancy/server.hpp>
+
+namespace bmc::redundancy
+{
+
+using RedundancyIntf =
+    sdbusplus::xyz::openbmc_project::State::BMC::server::Redundancy;
+using RedundancyInterface = sdbusplus::server::object_t<RedundancyIntf>;
+
+class Manager
+{
+  public:
+    ~Manager() = default;
+    Manager(const Manager&) = delete;
+    Manager& operator=(const Manager&) = delete;
+    Manager(Manager&&) = delete;
+    Manager& operator=(Manager&&) = delete;
+
+    /**
+     * @brief Constructor
+     *
+     * @param cts - The async context object
+     */
+    explicit Manager(sdbusplus::async::context& ctx);
+
+  private:
+    /**
+     * @brief Kicks off the Manager startup as a coroutine
+     */
+    sdbusplus::async::task<> startup();
+
+    /**
+     * @brief Determines the BMC role, active or passive
+     */
+    void determineRole();
+
+    /**
+     * @brief The async context object
+     */
+    sdbusplus::async::context& ctx;
+
+    /**
+     * @brief The Redundancy D-Bus interface
+     */
+    RedundancyInterface redundancyInterface;
+};
+
+} // namespace bmc::redundancy
diff --git a/redundant-bmc/meson.build b/redundant-bmc/meson.build
new file mode 100644
index 0000000..27fe26a
--- /dev/null
+++ b/redundant-bmc/meson.build
@@ -0,0 +1,40 @@
+librbmc_sources = files(
+  'manager.cpp',
+  'role_determination.cpp',
+)
+
+librbmc_deps = [
+  sdbusplus,
+  phosphordbusinterfaces,
+  phosphorlogging,
+]
+
+librbmc_lib = static_library(
+  'rbmc',
+  librbmc_sources,
+  dependencies: [
+    librbmc_deps
+  ]
+)
+
+librbmc_dep = declare_dependency(
+  include_directories: include_directories('.'),
+  link_with: librbmc_lib,
+  dependencies: [
+    sdbusplus,
+    phosphordbusinterfaces,
+    phosphorlogging,
+  ]
+)
+
+executable('phosphor-rbmc-state-manager',
+           'rbmc_state_manager_main.cpp',
+            dependencies: [
+                librbmc_dep
+            ],
+    install: true
+)
+
+if get_option('tests').allowed()
+    subdir('test')
+endif
\ No newline at end of file
diff --git a/redundant-bmc/rbmc_state_manager_main.cpp b/redundant-bmc/rbmc_state_manager_main.cpp
new file mode 100644
index 0000000..b2196de
--- /dev/null
+++ b/redundant-bmc/rbmc_state_manager_main.cpp
@@ -0,0 +1,21 @@
+#include "manager.hpp"
+
+#include <sdbusplus/async.hpp>
+
+int main()
+{
+    sdbusplus::async::context ctx;
+    sdbusplus::server::manager_t objMgr{
+        ctx, bmc::redundancy::RedundancyInterface::instance_path};
+
+    bmc::redundancy::Manager manager{ctx};
+
+    ctx.spawn([](sdbusplus::async::context& ctx) -> sdbusplus::async::task<> {
+        ctx.request_name(bmc::redundancy::RedundancyInterface::default_service);
+        co_return;
+    }(ctx));
+
+    ctx.run();
+
+    return 0;
+}
diff --git a/redundant-bmc/role_determination.cpp b/redundant-bmc/role_determination.cpp
new file mode 100644
index 0000000..ef28c06
--- /dev/null
+++ b/redundant-bmc/role_determination.cpp
@@ -0,0 +1,14 @@
+#include "role_determination.hpp"
+
+namespace role_determination
+{
+using Redundancy =
+    sdbusplus::common::xyz::openbmc_project::state::bmc::Redundancy;
+
+Redundancy::Role run()
+{
+    using enum Redundancy::Role;
+    return Passive;
+}
+
+} // namespace role_determination
diff --git a/redundant-bmc/role_determination.hpp b/redundant-bmc/role_determination.hpp
new file mode 100644
index 0000000..69ae2c9
--- /dev/null
+++ b/redundant-bmc/role_determination.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <xyz/openbmc_project/State/BMC/Redundancy/common.hpp>
+
+namespace role_determination
+{
+
+/**
+ * @brief Determines if this BMC should claim the Active or Passive role.
+ *
+ * @return The role
+ */
+sdbusplus::common::xyz::openbmc_project::state::bmc::Redundancy::Role run();
+
+} // namespace role_determination
diff --git a/redundant-bmc/test/meson.build b/redundant-bmc/test/meson.build
new file mode 100644
index 0000000..60edfc6
--- /dev/null
+++ b/redundant-bmc/test/meson.build
@@ -0,0 +1,15 @@
+test('rbmc-state-manager-tests',
+  executable('rbmc-state-manager-tests',
+    'role_determination_test.cpp',
+    dependencies: [
+      gtest,
+      phosphordbusinterfaces,
+      sdbusplus,
+      phosphorlogging,
+    ],
+    link_with: [
+      librbmc_lib
+    ],
+    include_directories: '../'
+  )
+)
diff --git a/redundant-bmc/test/role_determination_test.cpp b/redundant-bmc/test/role_determination_test.cpp
new file mode 100644
index 0000000..1fab501
--- /dev/null
+++ b/redundant-bmc/test/role_determination_test.cpp
@@ -0,0 +1,13 @@
+#include "role_determination.hpp"
+
+#include <gtest/gtest.h>
+
+using Role =
+    sdbusplus::common::xyz::openbmc_project::state::bmc::Redundancy::Role;
+
+namespace rd = role_determination;
+
+TEST(RoleDeterminationTest, RoleDeterminationTest)
+{
+    EXPECT_EQ(rd::run(), Role::Passive);
+}
diff --git a/service_files/meson.build b/service_files/meson.build
index 0ba81ba..d372ddc 100644
--- a/service_files/meson.build
+++ b/service_files/meson.build
@@ -28,7 +28,8 @@
 
 if get_option('rbmc-state-management').allowed()
     unit_files += [
-        'phosphor-set-bmc-active-passive.service'
+        'phosphor-set-bmc-active-passive.service',
+        'xyz.openbmc_project.State.BMC.Redundancy.service'
     ]
 endif
 
diff --git a/service_files/xyz.openbmc_project.State.BMC.Redundancy.service b/service_files/xyz.openbmc_project.State.BMC.Redundancy.service
new file mode 100644
index 0000000..65910a3
--- /dev/null
+++ b/service_files/xyz.openbmc_project.State.BMC.Redundancy.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Phosphor Redundant BMC State Manager
+After=xyz.openbmc_project.State.BMC.service
+
+[Service]
+ExecStart=/usr/bin/phosphor-rbmc-state-manager
+Restart=always
+Type=dbus
+BusName=xyz.openbmc_project.State.BMC.Redundancy
+
+[Install]
+WantedBy=multi-user.target