watchdog event: Add support for timeout watchdog event

Add sel log support for Watchdog timeout event. The timeout signal is
emitted by `phosphor-watchdog` what can be matched with

```
"type='signal',interface='xyz.openbmc_project.Watchdog',"
"member='Timeout'"
```

The `Member` is `Timeout` to indicate the timeout events with the first
argument be the Action taken.

Deleted the watchdog monitor for `Enabled` flag. Property changed on
`Enabled` does not indicate a timeout watchdog event.

Change-Id: I3cd09912648e7a38bbfe187c06d596bec654d313
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/include/watchdog_event_monitor.hpp b/include/watchdog_event_monitor.hpp
index fabd150..7dd9673 100644
--- a/include/watchdog_event_monitor.hpp
+++ b/include/watchdog_event_monitor.hpp
@@ -18,7 +18,7 @@
 #include <sel_logger.hpp>
 #include <sensorutils.hpp>
 
-#include <iostream>
+#include <string>
 #include <string_view>
 #include <variant>
 
@@ -53,226 +53,207 @@
 static constexpr const uint8_t wdtNologBit = (1 << 7);
 static constexpr int interruptTypeBits = 4;
 
-inline static sdbusplus::bus::match::match
-    startWatchdogEventMonitor(std::shared_ptr<sdbusplus::asio::connection> conn)
+inline static void sendWatchdogEventLog(
+    std::shared_ptr<sdbusplus::asio::connection> conn,
+    sdbusplus::message::message& msg, bool assert,
+    std::optional<std::string_view> expireAction = std::nullopt)
 {
-    auto watchdogEventMatcherCallback = [conn](
-                                            sdbusplus::message::message& msg) {
-        // This static set of std::pair<path, event> tracks asserted events to
-        // avoid duplicate logs or deasserts logged without an assert
-        static boost::container::flat_set<std::pair<std::string, std::string>>
-            assertedEvents;
+    // SEL event data is three bytes where 0xFF means unspecified
+    std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
 
-        // SEL event data is three bytes where 0xFF means unspecified
-        std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
+    sdbusplus::message::message getWatchdogStatus =
+        conn->new_method_call(msg.get_sender(), msg.get_path(),
+                              "org.freedesktop.DBus.Properties", "GetAll");
+    getWatchdogStatus.append("xyz.openbmc_project.State.Watchdog");
+    boost::container::flat_map<std::string,
+                               std::variant<std::string, uint64_t, bool>>
+        watchdogStatus;
 
-        // Get the event type and assertion details from the message
-        std::string watchdogInterface;
-        boost::container::flat_map<std::string, std::variant<bool>>
-            propertiesChanged;
+    try
+    {
+        sdbusplus::message::message getWatchdogStatusResp =
+            conn->call(getWatchdogStatus);
+        getWatchdogStatusResp.read(watchdogStatus);
+    }
+    catch (sdbusplus::exception_t&)
+    {
+        std::cerr << "error getting watchdog status from " << msg.get_path()
+                  << "\n";
+        return;
+    }
 
-        msg.read(watchdogInterface, propertiesChanged);
-
-        if (propertiesChanged.begin()->first != "Enabled")
-        {
-            return;
-        }
-
-        bool* pval = std::get_if<bool>(&propertiesChanged.begin()->second);
-
-        if (!pval)
-        {
-            std::cerr << "watchdog event direction has invalid type\n";
-            return;
-        }
-        bool assert = *pval;
-
-        sdbusplus::message::message getWatchdogStatus =
-            conn->new_method_call(msg.get_sender(), msg.get_path(),
-                                  "org.freedesktop.DBus.Properties", "GetAll");
-        getWatchdogStatus.append("xyz.openbmc_project.State.Watchdog");
-        boost::container::flat_map<std::string,
-                                   std::variant<std::string, uint64_t, bool>>
-            watchdogStatus;
-
-        try
-        {
-            sdbusplus::message::message getWatchdogStatusResp =
-                conn->call(getWatchdogStatus);
-            getWatchdogStatusResp.read(watchdogStatus);
-        }
-        catch (sdbusplus::exception_t&)
-        {
-            std::cerr << "error getting watchdog status from " << msg.get_path()
-                      << "\n";
-            return;
-        }
-
+    if (!expireAction)
+    {
         auto getExpireAction = watchdogStatus.find("ExpireAction");
-        std::string_view expireAction;
         if (getExpireAction != watchdogStatus.end())
         {
             expireAction = std::get<std::string>(getExpireAction->second);
-            expireAction.remove_prefix(std::min(
-                expireAction.find_last_of(".") + 1, expireAction.size()));
+            expireAction->remove_prefix(std::min(
+                expireAction->find_last_of(".") + 1, expireAction->size()));
         }
-        if (expireAction == "HardReset")
-        {
-            eventData[0] =
-                static_cast<uint8_t>(watchdogEventOffsets::hardReset);
-        }
-        else if (expireAction == "PowerOff")
-        {
-            eventData[0] =
-                static_cast<uint8_t>(watchdogEventOffsets::powerDown);
-        }
-        else if (expireAction == "PowerCycle")
-        {
-            eventData[0] =
-                static_cast<uint8_t>(watchdogEventOffsets::powerCycle);
-        }
-        else if (expireAction == "None")
-        {
-            eventData[0] = static_cast<uint8_t>(watchdogEventOffsets::noAction);
-        }
+    }
 
-        auto getPreTimeoutInterrupt =
-            watchdogStatus.find("PreTimeoutInterrupt");
-        std::string_view preTimeoutInterrupt;
-        if (getPreTimeoutInterrupt != watchdogStatus.end())
-        {
-            preTimeoutInterrupt =
-                std::get<std::string>(getPreTimeoutInterrupt->second);
-            preTimeoutInterrupt.remove_prefix(
-                std::min(preTimeoutInterrupt.find_last_of(".") + 1,
-                         preTimeoutInterrupt.size()));
-        }
-        if (preTimeoutInterrupt == "None")
-        {
-            eventData[1] &=
-                (static_cast<uint8_t>(watchdogInterruptTypeOffsets::none)
-                 << interruptTypeBits);
-        }
-        else if (preTimeoutInterrupt == "SMI")
-        {
-            eventData[1] &=
-                (static_cast<uint8_t>(watchdogInterruptTypeOffsets::SMI)
-                 << interruptTypeBits);
-        }
-        else if (preTimeoutInterrupt == "NMI")
-        {
-            eventData[1] &=
-                (static_cast<uint8_t>(watchdogInterruptTypeOffsets::NMI)
-                 << interruptTypeBits);
-        }
-        else if (preTimeoutInterrupt == "MI")
-        {
-            eventData[1] &= (static_cast<uint8_t>(
-                                 watchdogInterruptTypeOffsets::messageInterrupt)
-                             << interruptTypeBits);
-        }
+    if (*expireAction == "HardReset")
+    {
+        eventData[0] = static_cast<uint8_t>(watchdogEventOffsets::hardReset);
+    }
+    else if (*expireAction == "PowerOff")
+    {
+        eventData[0] = static_cast<uint8_t>(watchdogEventOffsets::powerDown);
+    }
+    else if (*expireAction == "PowerCycle")
+    {
+        eventData[0] = static_cast<uint8_t>(watchdogEventOffsets::powerCycle);
+    }
+    else if (*expireAction == "None")
+    {
+        eventData[0] = static_cast<uint8_t>(watchdogEventOffsets::noAction);
+    }
 
-        auto getCurrentTimerUse = watchdogStatus.find("CurrentTimerUse");
-        std::string_view currentTimerUse;
-        if (getCurrentTimerUse != watchdogStatus.end())
-        {
-            currentTimerUse = std::get<std::string>(getCurrentTimerUse->second);
-            currentTimerUse.remove_prefix(std::min(
-                currentTimerUse.find_last_of(".") + 1, currentTimerUse.size()));
-        }
-        if (currentTimerUse == "BIOSFRB2")
-        {
-            eventData[1] |=
-                static_cast<uint8_t>(watchdogTimerUseOffsets::BIOSFRB2);
-        }
-        else if (currentTimerUse == "BIOSPOST")
-        {
-            eventData[1] |=
-                static_cast<uint8_t>(watchdogTimerUseOffsets::BIOSPOST);
-        }
-        else if (currentTimerUse == "OSLoad")
-        {
-            eventData[1] |=
-                static_cast<uint8_t>(watchdogTimerUseOffsets::OSLoad);
-        }
-        else if (currentTimerUse == "SMSOS")
-        {
-            eventData[1] |=
-                static_cast<uint8_t>(watchdogTimerUseOffsets::SMSOS);
-        }
-        else if (currentTimerUse == "OEM")
-        {
-            eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::OEM);
-        }
-        else
-        {
-            eventData[1] |=
-                static_cast<uint8_t>(watchdogTimerUseOffsets::unspecified);
-        }
+    auto getPreTimeoutInterrupt = watchdogStatus.find("PreTimeoutInterrupt");
+    std::string_view preTimeoutInterrupt;
+    if (getPreTimeoutInterrupt != watchdogStatus.end())
+    {
+        preTimeoutInterrupt =
+            std::get<std::string>(getPreTimeoutInterrupt->second);
+        preTimeoutInterrupt.remove_prefix(
+            std::min(preTimeoutInterrupt.find_last_of(".") + 1,
+                     preTimeoutInterrupt.size()));
+    }
+    if (preTimeoutInterrupt == "None")
+    {
+        eventData[1] &=
+            (static_cast<uint8_t>(watchdogInterruptTypeOffsets::none)
+             << interruptTypeBits);
+    }
+    else if (preTimeoutInterrupt == "SMI")
+    {
+        eventData[1] &= (static_cast<uint8_t>(watchdogInterruptTypeOffsets::SMI)
+                         << interruptTypeBits);
+    }
+    else if (preTimeoutInterrupt == "NMI")
+    {
+        eventData[1] &= (static_cast<uint8_t>(watchdogInterruptTypeOffsets::NMI)
+                         << interruptTypeBits);
+    }
+    else if (preTimeoutInterrupt == "MI")
+    {
+        eventData[1] &= (static_cast<uint8_t>(
+                             watchdogInterruptTypeOffsets::messageInterrupt)
+                         << interruptTypeBits);
+    }
 
-        auto getWatchdogInterval = watchdogStatus.find("Interval");
-        uint64_t watchdogInterval = 0;
-        if (getWatchdogInterval != watchdogStatus.end())
-        {
-            watchdogInterval = std::get<uint64_t>(getWatchdogInterval->second);
-        }
+    auto getCurrentTimerUse = watchdogStatus.find("CurrentTimerUse");
+    std::string_view currentTimerUse;
+    if (getCurrentTimerUse != watchdogStatus.end())
+    {
+        currentTimerUse = std::get<std::string>(getCurrentTimerUse->second);
+        currentTimerUse.remove_prefix(std::min(
+            currentTimerUse.find_last_of(".") + 1, currentTimerUse.size()));
+    }
+    if (currentTimerUse == "BIOSFRB2")
+    {
+        eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::BIOSFRB2);
+    }
+    else if (currentTimerUse == "BIOSPOST")
+    {
+        eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::BIOSPOST);
+    }
+    else if (currentTimerUse == "OSLoad")
+    {
+        eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::OSLoad);
+    }
+    else if (currentTimerUse == "SMSOS")
+    {
+        eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::SMSOS);
+    }
+    else if (currentTimerUse == "OEM")
+    {
+        eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::OEM);
+    }
+    else
+    {
+        eventData[1] |=
+            static_cast<uint8_t>(watchdogTimerUseOffsets::unspecified);
+    }
 
-        // get watchdog status porperties
-        static bool wdt_nolog;
-        sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
-        uint8_t netFn = 0x06;
-        uint8_t lun = 0x00;
-        uint8_t cmd = 0x25;
-        std::vector<uint8_t> commandData;
-        std::map<std::string, std::variant<int>> options;
+    auto getWatchdogInterval = watchdogStatus.find("Interval");
+    uint64_t watchdogInterval = 0;
+    if (getWatchdogInterval != watchdogStatus.end())
+    {
+        watchdogInterval = std::get<uint64_t>(getWatchdogInterval->second);
+    }
 
-        auto ipmiCall = bus.new_method_call(
-            "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
-            "xyz.openbmc_project.Ipmi.Server", "execute");
-        ipmiCall.append(netFn, lun, cmd, commandData, options);
-        std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
-            rsp;
-        auto ipmiReply = bus.call(ipmiCall);
-        ipmiReply.read(rsp);
-        auto& [rnetFn, rlun, rcmd, cc, responseData] = rsp;
+    // get watchdog status properties
+    static bool wdt_nolog;
+    sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+    uint8_t netFn = 0x06;
+    uint8_t lun = 0x00;
+    uint8_t cmd = 0x25;
+    std::vector<uint8_t> commandData;
+    std::map<std::string, std::variant<int>> options;
 
-        std::string direction;
-        std::string eventMessageArgs;
-        if (assert)
-        {
-            direction = " enable ";
-            eventMessageArgs = "Enabled";
-            wdt_nolog = responseData[0] & wdtNologBit;
-        }
-        else
-        {
-            direction = " disable ";
-            eventMessageArgs = "Disabled";
-        }
+    auto ipmiCall = bus.new_method_call(
+        "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
+        "xyz.openbmc_project.Ipmi.Server", "execute");
+    ipmiCall.append(netFn, lun, cmd, commandData, options);
+    std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> rsp;
+    auto ipmiReply = bus.call(ipmiCall);
+    ipmiReply.read(rsp);
+    auto& [rnetFn, rlun, rcmd, cc, responseData] = rsp;
 
-        // Set Watchdog Timer byte1[7]-1b=don't log
-        if (!wdt_nolog)
-        {
-            // Construct a human-readable message of this event for the log
-            std::string journalMsg(
-                std::string(currentTimerUse) + std::string(direction) +
-                "watchdog countdown " +
-                std::to_string(watchdogInterval / 1000) + " seconds " +
-                std::string(expireAction) + " action");
+    std::string direction;
+    std::string eventMessageArgs;
+    if (assert)
+    {
+        direction = " enable ";
+        eventMessageArgs = "Enabled";
+        wdt_nolog = responseData[0] & wdtNologBit;
+    }
+    else
+    {
+        direction = " disable ";
+        eventMessageArgs = "Disabled";
+    }
 
-            std::string redfishMessageID = "OpenBMC.0.1.IPMIWatchdog";
+    // Set Watchdog Timer byte1[7]-1b=don't log
+    if (!wdt_nolog)
+    {
+        // Construct a human-readable message of this event for the log
+        std::string journalMsg(
+            std::string(currentTimerUse) + std::string(direction) +
+            "watchdog countdown " + std::to_string(watchdogInterval / 1000) +
+            " seconds " + std::string(*expireAction) + " action");
 
-            selAddSystemRecord(
-                journalMsg, std::string(msg.get_path()), eventData, assert,
-                selBMCGenID, "REDFISH_MESSAGE_ID=%s", redfishMessageID.c_str(),
-                "REDFISH_MESSAGE_ARGS=%s", eventMessageArgs.c_str(), NULL);
-        }
-    };
+        std::string redfishMessageID = "OpenBMC.0.1.IPMIWatchdog";
+
+        selAddSystemRecord(journalMsg, std::string(msg.get_path()), eventData,
+                           assert, selBMCGenID, "REDFISH_MESSAGE_ID=%s",
+                           redfishMessageID.c_str(), "REDFISH_MESSAGE_ARGS=%s",
+                           eventMessageArgs.c_str(), NULL);
+    }
+}
+
+inline static sdbusplus::bus::match::match
+    startWatchdogEventMonitor(std::shared_ptr<sdbusplus::asio::connection> conn)
+{
+    auto watchdogEventMatcherCallback =
+        [conn](sdbusplus::message::message& msg) {
+            std::string expiredAction;
+            msg.read(expiredAction);
+
+            std::string_view action = expiredAction;
+            action.remove_prefix(
+                std::min(action.find_last_of(".") + 1, action.size()));
+
+            sendWatchdogEventLog(conn, msg, true, action);
+        };
+
     sdbusplus::bus::match::match watchdogEventMatcher(
         static_cast<sdbusplus::bus::bus&>(*conn),
-        "type='signal',interface='org.freedesktop.DBus.Properties',member='"
-        "PropertiesChanged',arg0namespace='xyz.openbmc_project.State."
-        "Watchdog'",
+        "type='signal',interface='xyz.openbmc_project.Watchdog',"
+        "member='Timeout'",
         std::move(watchdogEventMatcherCallback));
+
     return watchdogEventMatcher;
 }