Fixed host count into 3(align with grpc-blob)

Test result: https://paste.googleplex.com/6300373870182400?raw

Google-Bug-Id: 433414984
Change-Id: I261e47cc00b1dc6b2031c4cdb98cb756aa6cccc1
Signed-off-by: Jack Huang <huangweichieh@google.com>
diff --git a/include/gen.hpp b/include/gen.hpp
index 8a90959..ccb4d1d 100644
--- a/include/gen.hpp
+++ b/include/gen.hpp
@@ -24,53 +24,17 @@
 namespace btm = boot_time_monitor;
 
 /**
- * @brief Generates the PSM (Platform State Manager) configuration for a
- * specific node.
+ * @brief Generates the PSM D-Bus configuration for a specific host.
  *
- * This function determines the correct D-Bus service names and object paths
- * for monitoring host state and OS status based on whether the system is
- * single-host or multi-host. For multi-host systems, it uses the node's
- * index within the provided list to construct unique identifiers.
+ * This function constructs the necessary D-Bus service names and object paths
+ * for the phosphor-state-manager (PSM) based on a host's numerical index.
+ * This configuration is used to monitor host state and OS status changes.
  *
- * @param nodeConfigs A vector containing the configurations of all nodes.
- * @param nodeConfig The configuration of the specific node for which to
- * generate the PSM config.
- * @return psm::PSMConfig The generated PSM configuration containing D-Bus
- *                        service names and object paths. Returns an empty
- *                        config if the node is not found or the list is empty.
+ * @param idx The zero-based index of the host.
+ * @return psm::PSMConfig The generated PSM configuration.
  */
-inline psm::PSMConfig
-    GenPSMConfig(const std::vector<btm::NodeConfig>& nodeConfigs,
-                 const btm::NodeConfig& nodeConfig)
+inline psm::PSMConfig GenPSMConfig(int idx)
 {
-    if (nodeConfigs.empty())
-    {
-        fmt::print("[{}] node list is empty!\n", __FUNCTION__);
-        return {};
-    }
-
-    // Singlehost rule
-    if (nodeConfigs.size() == 1)
-    {
-        return {
-            .hostStateDbusService = "xyz.openbmc_project.State.Host",
-            .hostStateDbusObjPath = "/xyz/openbmc_project/state/host0",
-            .osStateDbusService = "xyz.openbmc_project.State.OperatingSystem",
-            .osStateDbusObjPath = "/xyz/openbmc_project/state/os",
-        };
-    }
-
-    // Multihost platforms have a different PSM Dbus Service and Path rule.
-    // Create dBus path depends on its relative indices.
-    auto it = std::find(nodeConfigs.begin(), nodeConfigs.end(), nodeConfig);
-    if (it == nodeConfigs.end())
-    {
-        fmt::print("[{}] node config not found!\n", __FUNCTION__);
-        return {};
-    }
-
-    int64_t idx = std::distance(nodeConfigs.begin(), it) + 1;
-
     return {
         .hostStateDbusService = fmt::format("xyz.openbmc_project.State.Host{}",
                                             idx),
@@ -92,7 +56,7 @@
  * @return dbus::DbusConfig The generated D-Bus configuration containing the
  *                          object path.
  */
-inline dbus::DbusConfig GenDbusConfig(const NodeConfig& nodeConfig)
+inline dbus::DbusConfig GenDbusConfig(const btm::NodeConfig& nodeConfig)
 {
     return {.dbusObjPath = fmt::format("/xyz/openbmc_project/time/boot/{}",
                                        nodeConfig.node_name)};
diff --git a/include/psm_handler.hpp b/include/psm_handler.hpp
index d00d83b..aa4c2a0 100644
--- a/include/psm_handler.hpp
+++ b/include/psm_handler.hpp
@@ -3,7 +3,9 @@
 #include "boottime_api/boottime_api.h"
 #include "boottime_api/node_config.h"
 
+#include <boost/asio/steady_timer.hpp>
 #include <boost/container/flat_map.hpp>
+#include <sdbusplus/asio/connection.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/bus/match.hpp>
 #include <sdbusplus/message.hpp>
@@ -75,20 +77,24 @@
             std::shared_ptr<btm::api::IBoottimeApi> api);
 
   private:
-    /** @brief D-Bus match rule watcher for Host State property changes. */
-    std::unique_ptr<sdbusplus::bus::match::match> hostStateWatcher;
-    /** @brief D-Bus match rule watcher for OS Status property changes. */
-    std::unique_ptr<sdbusplus::bus::match::match> osStatusWatcher;
+    /** @brief Callback for hostStateWatcher */
+    void hostStateWatcherCallback(sdbusplus::message::message& message);
+    /** @brief Callback for oSStatusWatcher */
+    void oSStatusWatcherCallback(sdbusplus::message::message& message);
 
     /** @brief Stores the previously observed Host State to detect changes. */
     std::string mPreHostState;
     /** @brief Stores the previously observed OS State/Status to detect changes.
      */
-    std::string mPreOSState;
+    std::string mPreOSStatus;
     /** @brief Configuration of the node associated with this handler. */
     btm::NodeConfig mNodeConfig;
     /** @brief Shared pointer to the central boot time monitoring API. */
     std::shared_ptr<btm::api::IBoottimeApi> mApi;
+    /** @brief D-Bus match rule watcher for Host State property changes. */
+    std::unique_ptr<sdbusplus::bus::match::match> mHostStateWatcher;
+    /** @brief D-Bus match rule watcher for OS Status property changes. */
+    std::unique_ptr<sdbusplus::bus::match::match> mOSStatusWatcher;
 };
 } // namespace psm
 } // namespace boot_time_monitor
diff --git a/src/main.cpp b/src/main.cpp
index 33920b1..acea6a6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -46,22 +46,26 @@
  *
  */
 
+constexpr int64_t kDefaultHostCount = 3; // Align with grpc-blobs default value
+constexpr int64_t kDefaultBMCCount = 1;  // Default BMC count
+
 void print_usage(const char* prog_name)
 {
     fmt::print(stderr, "Usage: {} [options]\n", prog_name);
     fmt::print(stderr, "Options:\n");
     fmt::print(stderr,
                "  -h, --help             Show this help message and exit\n");
-    fmt::print(stderr,
-               "  --host_count <num>    Set number of hosts (default: 1)\n");
+    fmt::print(
+        stderr,
+        "  --host_count <num>    Set number of hosts (optional, queries DBus if not set)\n");
     fmt::print(stderr,
                "  --bmc_count <num>     Set number of BMCs (default: 1)\n");
 }
 
 int main(int argc, char* argv[])
 {
-    int64_t hostCount = 1; // Default host count
-    int64_t bmcCount = 1;  // Default BMC count
+    int64_t hostCount = kDefaultHostCount;
+    int64_t bmcCount = kDefaultBMCCount;
 
     const struct option long_options[] = {
         {"help", no_argument, nullptr, 'h'},
@@ -71,8 +75,8 @@
 
     int opt;
     int option_index = 0;
-    while ((opt = getopt_long(argc, argv, "h", long_options, &option_index)) !=
-           -1)
+    while ((opt = getopt_long(argc, argv, "ho:b:", long_options,
+                              &option_index)) != -1)
     {
         switch (opt)
         {
@@ -80,28 +84,59 @@
                 print_usage(argv[0]);
                 return 0;
             case 'o':
-                hostCount = std::strtol(optarg, nullptr, 10);
+                try
+                {
+                    hostCount = std::stoi(optarg);
+                    if (hostCount < 0)
+                    {
+                        fmt::print(
+                            stderr,
+                            "Warning: host_count should be non-negative. Using default {}.\n",
+                            kDefaultHostCount);
+                        hostCount = kDefaultHostCount;
+                    }
+                }
+                catch (const std::exception& e)
+                {
+                    fmt::print(
+                        stderr,
+                        "Error parsing host_count: {}. Using default {}.\n",
+                        e.what(), kDefaultHostCount);
+                    hostCount = kDefaultHostCount;
+                }
                 break;
             case 'b':
-                bmcCount = std::strtol(optarg, nullptr, 10);
+                try
+                {
+                    bmcCount = std::stoi(optarg);
+                    if (bmcCount <= 0)
+                    {
+                        fmt::print(
+                            stderr,
+                            "Warning: bmc_count must be positive. Using default {}.\n",
+                            kDefaultBMCCount);
+                        bmcCount = kDefaultBMCCount;
+                    }
+                    fmt::print(stdout, "Using provided bmc count: {}\n",
+                               bmcCount);
+                }
+                catch (const std::exception& e)
+                {
+                    fmt::print(
+                        stderr,
+                        "Error parsing bmc_count: {}. Using default {}.\n",
+                        e.what(), kDefaultBMCCount);
+                    bmcCount = kDefaultBMCCount;
+                }
                 break;
             default: /* '?' */
                 print_usage(argv[0]);
                 return 1;
         }
     }
-    if (hostCount < 0)
-    {
-        fmt::print(stderr, "hostCount({}) must be greater than zero.\n",
-                   hostCount);
-        return 1;
-    }
-    if (bmcCount < 0)
-    {
-        fmt::print(stderr, "bmcCount({}) must be greater than zero.\n",
-                   bmcCount);
-        return 1;
-    }
+
+    fmt::print(stdout, "Host count: {}\n", hostCount);
+    fmt::print(stdout, "BMC count: {}\n", bmcCount);
 
     boost::asio::io_service io;
     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
@@ -115,7 +150,7 @@
     std::vector<std::unique_ptr<btm::dbus::Handler>> hostDbusHandlers;
     std::vector<std::unique_ptr<btm::dbus::Handler>> bmcDbusHandlers;
 
-    std::vector<btm::psm::Handler> psmHandlers;
+    std::vector<std::unique_ptr<btm::psm::Handler>> psmHandlers;
 
     std::shared_ptr<btm::api::BoottimeApi> api =
         std::make_shared<btm::api::BoottimeApi>();
@@ -134,18 +169,30 @@
                                     std::string(btm::kBootTimeTagBMC));
     }
 
-    for (auto& nodeConfig : hostNodeConfigs)
+    hostDbusHandlers.reserve(hostCount);
+    psmHandlers.reserve(hostCount + 1); // extra psm handler for single host
+    for (int i = 0; i < hostCount; i++)
     {
+        auto& nodeConfig = hostNodeConfigs[i];
         absl::Status status = api->RegisterNode(nodeConfig);
         btm::log::LogIfError(status);
 
         hostDbusHandlers.emplace_back(std::make_unique<btm::dbus::Handler>(
             bus, nodeConfig, btm::gen::GenDbusConfig(nodeConfig), api));
-        psmHandlers.emplace_back(
-            bus, nodeConfig,
-            btm::gen::GenPSMConfig(hostNodeConfigs, nodeConfig), api);
+        if (i == 0)
+        {
+            // Register an extra PSM handler for host 0, which is a common
+            // convention for a single-host system. In a multi-host
+            // configuration where this D-Bus service might not exist.
+            psmHandlers.emplace_back(std::make_unique<btm::psm::Handler>(
+                bus, hostNodeConfigs[0], btm::gen::GenPSMConfig(0), api));
+        }
+        psmHandlers.emplace_back(std::make_unique<btm::psm::Handler>(
+            bus, nodeConfig, btm::gen::GenPSMConfig(i + 1),
+            api)); // PSMConfig using 1-based index.
     }
 
+    bmcDbusHandlers.reserve(bmcCount);
     for (auto& nodeConfig : bmcNodeConfigs)
     {
         absl::Status status = api->RegisterNode(nodeConfig);
diff --git a/src/psm_handler.cpp b/src/psm_handler.cpp
index f19b258..1ecac5f 100644
--- a/src/psm_handler.cpp
+++ b/src/psm_handler.cpp
@@ -29,25 +29,21 @@
     return std::string("OSStatus:") + std::string(status.substr(found + 1));
 }
 
-Handler::Handler(sdbusplus::bus::bus& bus, const btm::NodeConfig& nodeConfig,
-                 const btm::psm::PSMConfig& psmConfig,
-                 std::shared_ptr<btm::api::IBoottimeApi> api) :
-    mNodeConfig(nodeConfig), mApi(std::move(api))
+std::string inline queryHostState(sdbusplus::bus::bus& bus,
+                                  const btm::psm::PSMConfig& psmConfig)
 {
-    // Initialize mPreHostState
+    std::string hostState;
+    BasicVariantType result;
     auto method =
         bus.new_method_call(psmConfig.hostStateDbusService.c_str(), // Service
                             psmConfig.hostStateDbusObjPath.c_str(), // Path
                             "org.freedesktop.DBus.Properties",      // Iface
                             "Get");                                 // Function
     method.append("xyz.openbmc_project.State.Host", "CurrentHostState");
-    BasicVariantType result;
     try
     {
         bus.call(method).read(result);
-        mPreHostState = std::get<std::string>(result);
-        fmt::print("{} `CurrentHostState` is `{}`\n", mNodeConfig.node_name,
-                   mPreHostState);
+        hostState = std::get<std::string>(result);
     }
     catch (const sdbusplus::exception::SdBusError& e)
     {
@@ -59,37 +55,15 @@
             psmConfig.hostStateDbusService, psmConfig.hostStateDbusObjPath,
             e.what());
     }
+    return hostState;
+}
 
-    hostStateWatcher = std::make_unique<sdbusplus::bus::match::match>(
-        bus,
-        sdbusplus::bus::match::rules::propertiesChanged(
-            psmConfig.hostStateDbusObjPath, "xyz.openbmc_project.State.Host"),
-        [this](sdbusplus::message::message& message) {
-        std::string objectName;
-        boost::container::flat_map<
-            std::string,
-            std::variant<std::string, bool, int64_t, uint64_t, double>>
-            values;
-        message.read(objectName, values);
-
-        auto findState = values.find("CurrentHostState");
-        if (findState != values.end())
-        {
-            const std::string curHostState =
-                std::get<std::string>(findState->second);
-            fmt::print(
-                stderr,
-                "[hostStateWatcher] {} `CurrentHostState` has changed from {} to {}\n",
-                mNodeConfig.node_name, mPreHostState, curHostState);
-            absl::Status status = mApi->SetNodeCheckpoint(
-                mNodeConfig, translateHostStateName(curHostState), 0, 0);
-            btm::log::LogIfError(status);
-            mPreHostState = curHostState;
-        }
-    });
-
-    // Initialize mPreOSState
-    method =
+std::string inline queryOSStatus(sdbusplus::bus::bus& bus,
+                                 const btm::psm::PSMConfig& psmConfig)
+{
+    std::string oSState;
+    BasicVariantType result;
+    auto method =
         bus.new_method_call(psmConfig.osStateDbusService.c_str(), // Service
                             psmConfig.osStateDbusObjPath.c_str(), // Path
                             "org.freedesktop.DBus.Properties",    // Iface
@@ -99,9 +73,7 @@
     try
     {
         bus.call(method).read(result);
-        mPreOSState = std::get<std::string>(result);
-        fmt::print("{} `OperatingSystemState` is `{}`\n", mNodeConfig.node_name,
-                   mPreOSState);
+        oSState = std::get<std::string>(result);
     }
     catch (const sdbusplus::exception::SdBusError& e)
     {
@@ -113,36 +85,133 @@
             psmConfig.hostStateDbusService, psmConfig.hostStateDbusObjPath,
             e.what());
     }
+    return oSState;
+}
 
-    osStatusWatcher = std::make_unique<sdbusplus::bus::match::match>(
+std::string inline queryNameOwner(sdbusplus::bus::bus& bus,
+                                  const btm::psm::PSMConfig& psmConfig)
+{
+    std::string ownerUniqueName;
+    try
+    {
+        auto method =
+            bus.new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
+                                "org.freedesktop.DBus", "GetNameOwner");
+        method.append(std::string(psmConfig.osStateDbusService));
+        bus.call(method).read(ownerUniqueName);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        // This name isn't owned by anyone right now, so we can ignore this
+        // signal.
+    }
+    return ownerUniqueName;
+}
+
+Handler::Handler(sdbusplus::bus::bus& bus, const btm::NodeConfig& nodeConfig,
+                 const btm::psm::PSMConfig& psmConfig,
+                 std::shared_ptr<btm::api::IBoottimeApi> api) :
+    mNodeConfig(nodeConfig), mApi(std::move(api))
+{
+    mPreHostState = queryHostState(bus, psmConfig);
+    fmt::print("{} `CurrentHostState` is `{}`\n", mNodeConfig.node_name,
+               mPreHostState);
+    mHostStateWatcher = std::make_unique<sdbusplus::bus::match::match>(
+        bus,
+        sdbusplus::bus::match::rules::propertiesChanged(
+            psmConfig.hostStateDbusObjPath, "xyz.openbmc_project.State.Host"),
+        [this](sdbusplus::message::message& message) {
+        hostStateWatcherCallback(message);
+    });
+
+    mPreOSStatus = queryOSStatus(bus, psmConfig);
+    fmt::print("{} `OperatingSystemState` is `{}`\n", mNodeConfig.node_name,
+               mPreOSStatus);
+    mOSStatusWatcher = std::make_unique<sdbusplus::bus::match::match>(
         bus,
         sdbusplus::bus::match::rules::propertiesChanged(
             psmConfig.osStateDbusObjPath,
             "xyz.openbmc_project.State.OperatingSystem.Status"),
-        [this](sdbusplus::message::message& message) {
-        std::string objectName;
-        boost::container::flat_map<
-            std::string,
-            std::variant<std::string, bool, int64_t, uint64_t, double>>
-            values;
-        message.read(objectName, values);
-
-        auto findState = values.find("OperatingSystemState");
-        if (findState != values.end())
+        [this, &bus, psmConfig](sdbusplus::message::message& message) {
+        const std::string& signalSenderUniqueName = message.get_sender();
+        std::string ownerUniqueName = queryNameOwner(bus, psmConfig);
+        // Compare the signal's sender to the name's rightful owner.
+        if (signalSenderUniqueName != ownerUniqueName)
         {
-            const std::string curOSStatus =
-                std::get<std::string>(findState->second);
-
-            fmt::print(
-                stderr,
-                "[osStatusWatcher] {} `OperatingSystemState` has changed from {} to {}\n",
-                mNodeConfig.node_name, mPreOSState, curOSStatus);
-            absl::Status status = mApi->SetNodeCheckpoint(
-                mNodeConfig, translateOSStatus(curOSStatus), 0, 0);
-            btm::log::LogIfError(status);
-            mPreOSState = curOSStatus;
+            return; // The signal came from a different process. Ignore it.
         }
+        oSStatusWatcherCallback(message);
     });
 }
+
+void Handler::hostStateWatcherCallback(sdbusplus::message::message& message)
+{
+    fmt::print(stdout, "[mHostStateWatcher] Signal from {}\n",
+               mNodeConfig.node_name);
+    std::string objectName;
+    boost::container::flat_map<
+        std::string, std::variant<std::string, bool, int64_t, uint64_t, double>>
+        values;
+    message.read(objectName, values);
+
+    auto findState = values.find("CurrentHostState");
+    if (findState != values.end())
+    {
+        const std::string curHostState =
+            std::get<std::string>(findState->second);
+        if (curHostState == mPreHostState)
+        {
+            return;
+        }
+
+        fmt::print(
+            stdout,
+            "[mHostStateWatcher] {} `CurrentHostState` has changed from {} to {}\n",
+            mNodeConfig.node_name, mPreHostState, curHostState);
+        absl::Status status = mApi->SetNodeCheckpoint(
+            mNodeConfig, translateHostStateName(curHostState), 0, 0);
+        btm::log::LogIfError(status);
+        mPreHostState = curHostState;
+        fmt::print(
+            stdout,
+            "[mHostStateWatcher] {} `CurrentHostState` {} checkpoint has been recorded\n",
+            mNodeConfig.node_name, curHostState);
+    }
+}
+
+void Handler::oSStatusWatcherCallback(sdbusplus::message::message& message)
+{
+    fmt::print(stdout, "[mOSStatusWatcher] Signal from {}.\n",
+               mNodeConfig.node_name);
+    std::string objectName;
+    boost::container::flat_map<
+        std::string, std::variant<std::string, bool, int64_t, uint64_t, double>>
+        values;
+    message.read(objectName, values);
+
+    auto findState = values.find("OperatingSystemState");
+    if (findState != values.end())
+    {
+        const std::string curOSStatus =
+            std::get<std::string>(findState->second);
+        if (curOSStatus == mPreOSStatus)
+        {
+            return;
+        }
+
+        fmt::print(
+            stdout,
+            "[mOSStatusWatcher] {} `OperatingSystemState` has changed from {} to {}\n",
+            mNodeConfig.node_name, mPreOSStatus, curOSStatus);
+        absl::Status status = mApi->SetNodeCheckpoint(
+            mNodeConfig, translateOSStatus(curOSStatus), 0, 0);
+        btm::log::LogIfError(status);
+        mPreOSStatus = curOSStatus;
+        fmt::print(
+            stdout,
+            "[mOSStatusWatcher] {} `OperatingSystemState` {} checkpoint has been recorded\n",
+            mNodeConfig.node_name, curOSStatus);
+    }
+}
 } // namespace psm
 } // namespace boot_time_monitor
diff --git a/src/systemd_handler.cpp b/src/systemd_handler.cpp
index ccf5f53..1893002 100644
--- a/src/systemd_handler.cpp
+++ b/src/systemd_handler.cpp
@@ -235,6 +235,7 @@
     SystemdDurations durationMap = CalculateSystemdDuration(times);
 
 #ifdef NPCM7XX_OR_NEWER
+    fmt::print(stderr, "NPCM7XX_OR_NEWER applied");
     // NPCM7XX or newer Nuvoton BMC has a register that starts counting from
     // SoC power on. Also uptime starts counting when kernel is up. Thus we
     // can get (Firmware + Loader) time by `value[SEC_CNT_ADDR] - uptime`.