Attempt possible OSStatus interfaces

Newer dbus service will be all using hostX format instead of os.
To allow smooth transition, this commit allow trying all possible
interfaces.

Tested:
Manually verified on single host projects and one multi host
project.
https://paste.googleplex.com/4880312391434240?raw
https://paste.googleplex.com/4537823478415360?raw

Google-Bug-Id: 386135750
Change-Id: Ie83cfe50e75bd470b079dfcf4688223db68cea6f
Signed-off-by: Medicine Yeh <medicineyeh@google.com>
diff --git a/include/gen.hpp b/include/gen.hpp
index ccb4d1d..4332960 100644
--- a/include/gen.hpp
+++ b/include/gen.hpp
@@ -35,14 +35,25 @@
  */
 inline psm::PSMConfig GenPSMConfig(int idx)
 {
+    std::vector<btm::psm::DbusServiceLocation> possibleOsStatus = {
+        // New multi host nodes style.
+        {"xyz.openbmc_project.State.OperatingSystem",
+         fmt::format("/xyz/openbmc_project/state/host{}", idx)},
+        // Old multi host nodes style.
+        {fmt::format("xyz.openbmc_project.State.OperatingSystem{}", idx),
+         "/xyz/openbmc_project/state/os"},
+    };
+    if (idx == 0)
+    {
+        // Legacy style is only valid if idx == 0. Do not try this interface
+        // on other host nodes.
+        possibleOsStatus.push_back({"xyz.openbmc_project.State.OperatingSystem",
+                                    "/xyz/openbmc_project/state/os"});
+    }
     return {
-        .hostStateDbusService = fmt::format("xyz.openbmc_project.State.Host{}",
-                                            idx),
-        .hostStateDbusObjPath = fmt::format("/xyz/openbmc_project/state/host{}",
-                                            idx),
-        .osStateDbusService =
-            fmt::format("xyz.openbmc_project.State.OperatingSystem{}", idx),
-        .osStateDbusObjPath = "/xyz/openbmc_project/state/os",
+        .hostState = {fmt::format("xyz.openbmc_project.State.Host{}", idx),
+                      fmt::format("/xyz/openbmc_project/state/host{}", idx)},
+        .possibleOsStatus = possibleOsStatus,
     };
 }
 
diff --git a/include/psm_handler.hpp b/include/psm_handler.hpp
index aa4c2a0..fa31db9 100644
--- a/include/psm_handler.hpp
+++ b/include/psm_handler.hpp
@@ -24,6 +24,13 @@
 
 namespace btm = boot_time_monitor;
 
+// Define a structure to hold a path and interface pair.
+struct DbusServiceLocation
+{
+    std::string serviceName;
+    std::string objectPath;
+};
+
 /**
  * @brief Configuration for interacting with PSM D-Bus interfaces.
  *
@@ -32,18 +39,14 @@
  */
 struct PSMConfig
 {
-    /** @brief D-Bus service name for the Host State interface (e.g.,
-     * "xyz.openbmc_project.State.Host"). */
-    std::string hostStateDbusService;
-    /** @brief D-Bus object path for the Host State interface (e.g.,
+    /** @brief Host State D-Bus interface (e.g.,
+     * "xyz.openbmc_project.State.Host",
      * "/xyz/openbmc_project/state/host0"). */
-    std::string hostStateDbusObjPath;
-    /** @brief D-Bus service name for the OS State interface (e.g.,
-     * "xyz.openbmc_project.State.OperatingSystem"). */
-    std::string osStateDbusService;
-    /** @brief D-Bus object path for the OS State interface (e.g.,
+    DbusServiceLocation hostState;
+    /** @brief OS Status D-Bus (e.g.
+     * "xyz.openbmc_project.State.OperatingSystem"
      * "/xyz/openbmc_project/state/os"). */
-    std::string osStateDbusObjPath;
+    std::vector<DbusServiceLocation> possibleOsStatus;
 };
 
 /**
@@ -94,7 +97,7 @@
     /** @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;
+    std::vector<std::unique_ptr<sdbusplus::bus::match::match>> mOSStatusWatcher;
 };
 } // namespace psm
 } // namespace boot_time_monitor
diff --git a/src/psm_handler.cpp b/src/psm_handler.cpp
index 1ecac5f..a6405ca 100644
--- a/src/psm_handler.cpp
+++ b/src/psm_handler.cpp
@@ -35,10 +35,10 @@
     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
+        bus.new_method_call(psmConfig.hostState.serviceName.c_str(), // Service
+                            psmConfig.hostState.objectPath.c_str(),  // Path
+                            "org.freedesktop.DBus.Properties",       // Iface
+                            "Get");                                  // Function
     method.append("xyz.openbmc_project.State.Host", "CurrentHostState");
     try
     {
@@ -52,7 +52,7 @@
             "Failed to query `CurrentHostState` with busctl get-property {} {} "
             "xyz.openbmc_project.State.Host CurrentHostState"
             ". Got error: {}\n",
-            psmConfig.hostStateDbusService, psmConfig.hostStateDbusObjPath,
+            psmConfig.hostState.serviceName, psmConfig.hostState.objectPath,
             e.what());
     }
     return hostState;
@@ -61,35 +61,46 @@
 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
-                            "Get");                               // Function
-    method.append("xyz.openbmc_project.State.OperatingSystem.Status",
-                  "OperatingSystemState");
-    try
+    std::vector<std::string> errors;
+    for (const auto& service : psmConfig.possibleOsStatus)
     {
-        bus.call(method).read(result);
-        oSState = std::get<std::string>(result);
+        auto method =
+            bus.new_method_call(service.serviceName.c_str(),       // Service
+                                service.objectPath.c_str(),        // Path
+                                "org.freedesktop.DBus.Properties", // Iface
+                                "Get");                            // Function
+        method.append("xyz.openbmc_project.State.OperatingSystem.Status",
+                      "OperatingSystemState");
+        try
+        {
+            BasicVariantType result;
+            bus.call(method).read(result);
+            return std::get<std::string>(result);
+        }
+        catch (const sdbusplus::exception::SdBusError& e)
+        {
+            std::string errorMessage = fmt::format(
+                "busctl get-property {} {} "
+                "xyz.openbmc_project.State.OperatingSystem.Status OperatingSystemState."
+                "Got error: {}\n",
+                service.serviceName, service.objectPath, e.what());
+            errors.push_back(errorMessage);
+        }
     }
-    catch (const sdbusplus::exception::SdBusError& e)
+
+    // If we reach here, all attempts have failed. Print all errors.
+    fmt::print(
+        stderr,
+        "Failed to query OperatingSystemState, the attempts are as followed:\n");
+    for (const auto& error : errors)
     {
-        fmt::print(
-            stderr,
-            "Failed to query OperatingSystemState with busctl get-property {} {} "
-            "xyz.openbmc_project.State.OperatingSystem.Status OperatingSystemState."
-            "Got error: {}\n",
-            psmConfig.hostStateDbusService, psmConfig.hostStateDbusObjPath,
-            e.what());
+        fmt::print(stderr, "{}\n", error);
     }
-    return oSState;
+    return "";
 }
 
 std::string inline queryNameOwner(sdbusplus::bus::bus& bus,
-                                  const btm::psm::PSMConfig& psmConfig)
+                                  const btm::psm::DbusServiceLocation& service)
 {
     std::string ownerUniqueName;
     try
@@ -97,7 +108,7 @@
         auto method =
             bus.new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
                                 "org.freedesktop.DBus", "GetNameOwner");
-        method.append(std::string(psmConfig.osStateDbusService));
+        method.append(std::string(service.serviceName));
         bus.call(method).read(ownerUniqueName);
     }
     catch (const sdbusplus::exception::SdBusError& e)
@@ -119,7 +130,7 @@
     mHostStateWatcher = std::make_unique<sdbusplus::bus::match::match>(
         bus,
         sdbusplus::bus::match::rules::propertiesChanged(
-            psmConfig.hostStateDbusObjPath, "xyz.openbmc_project.State.Host"),
+            psmConfig.hostState.objectPath, "xyz.openbmc_project.State.Host"),
         [this](sdbusplus::message::message& message) {
         hostStateWatcherCallback(message);
     });
@@ -127,21 +138,25 @@
     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, &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)
-        {
-            return; // The signal came from a different process. Ignore it.
-        }
-        oSStatusWatcherCallback(message);
-    });
+    for (const auto& service : psmConfig.possibleOsStatus)
+    {
+        mOSStatusWatcher.emplace_back(
+            std::make_unique<sdbusplus::bus::match::match>(
+                bus,
+                sdbusplus::bus::match::rules::propertiesChanged(
+                    service.objectPath,
+                    "xyz.openbmc_project.State.OperatingSystem.Status"),
+                [this, &bus, service](sdbusplus::message::message& message) {
+            const std::string& signalSenderUniqueName = message.get_sender();
+            std::string ownerUniqueName = queryNameOwner(bus, service);
+            // Compare the signal's sender to the name's rightful owner.
+            if (signalSenderUniqueName != ownerUniqueName)
+            {
+                return; // The signal came from a different process. Ignore it.
+            }
+            oSStatusWatcherCallback(message);
+        }));
+    }
 }
 
 void Handler::hostStateWatcherCallback(sdbusplus::message::message& message)
@@ -159,6 +174,7 @@
     {
         const std::string curHostState =
             std::get<std::string>(findState->second);
+        // NOTE: Some BMC may emit the same status on change.
         if (curHostState == mPreHostState)
         {
             return;
@@ -194,6 +210,7 @@
     {
         const std::string curOSStatus =
             std::get<std::string>(findState->second);
+        // NOTE: Some BMC may emit the same status on change.
         if (curOSStatus == mPreOSStatus)
         {
             return;
@@ -213,5 +230,6 @@
             mNodeConfig.node_name, curOSStatus);
     }
 }
+
 } // namespace psm
 } // namespace boot_time_monitor