Implement a flag to enable fallback always
On some machines we want our watchdog running as long as the
phosphor-watchdog daemon is alive. This patch adds an option to enter
fallback mode any time the watchdog expires or is set to be disabled.
Change-Id: Ic96d2f15c761aeb4e25158c5bd861076cca6497d
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/argument.cpp b/argument.cpp
index c2d99ce..770db87 100644
--- a/argument.cpp
+++ b/argument.cpp
@@ -28,7 +28,7 @@
const std::vector<std::string> emptyArg;
const std::string ArgumentParser::trueString = "true"s;
-const char* ArgumentParser::optionStr = "p:s:t:a:f:i:ch";
+const char* ArgumentParser::optionStr = "p:s:t:a:f:i:ech";
const option ArgumentParser::options[] =
{
{ "path", required_argument, nullptr, 'p' },
@@ -37,6 +37,7 @@
{ "action_target", required_argument, nullptr, 'a' },
{ "fallback_action", required_argument, nullptr, 'f' },
{ "fallback_interval", required_argument, nullptr, 'i' },
+ { "fallback_always", no_argument, nullptr, 'e' },
{ "continue", no_argument, nullptr, 'c' },
{ "help", no_argument, nullptr, 'h' },
{ 0, 0, 0, 0},
@@ -111,6 +112,9 @@
"watchdog even when disabled via the dbus interface. "
"Waits for this interval before performing the fallback "
"action.\n";
+ std::cerr << " [--fallback_always] Enables the "
+ "watchdog even when disabled by the dbus interface. "
+ "This option is only valid with a fallback specified.\n";
std::cerr << " [--continue] Continue daemon "
"after watchdog timeout.\n";
}
diff --git a/mainapp.cpp b/mainapp.cpp
index 365fd50..de09ba3 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -172,9 +172,21 @@
fallback = Watchdog::Fallback{
.action = action,
.interval = interval,
+ .always = false,
};
}
+ auto fallbackAlwaysParam = (options)["fallback_always"];
+ if (!fallbackAlwaysParam.empty())
+ {
+ if (!fallback)
+ {
+ exitWithError("Specified the fallback should always be enabled but "
+ "no fallback provided.", argv);
+ }
+ fallback->always = true;
+ }
+
sd_event* event = nullptr;
auto r = sd_event_default(&event);
if (r < 0)
diff --git a/test/watchdog_test.cpp b/test/watchdog_test.cpp
index af7c3ed..34d1d24 100644
--- a/test/watchdog_test.cpp
+++ b/test/watchdog_test.cpp
@@ -273,6 +273,7 @@
Watchdog::Fallback fallback{
.action = Watchdog::Action::PowerOff,
.interval = static_cast<uint64_t>(fallbackIntervalMs),
+ .always = false,
};
std::map<Watchdog::Action, Watchdog::TargetName> emptyActionTargets;
wdog = std::make_unique<Watchdog>(bus, TEST_PATH, eventP,
@@ -305,3 +306,63 @@
EXPECT_FALSE(wdog->timerExpired());
EXPECT_TRUE(wdog->timerEnabled());
}
+
+/** @brief Make sure the watchdog is started and with a fallback without
+ * sending an enable
+ * Then enable the watchdog
+ * Wait through the initial trip and ensure the fallback is observed
+ * Make sure that fallback runs to completion and ensure the watchdog
+ * is in the fallback state again
+ */
+TEST_F(WdogTest, enableWdogWithFallbackAlways)
+{
+ auto primaryInterval = 5s;
+ auto primaryIntervalMs = milliseconds(primaryInterval).count();
+ auto fallbackInterval = primaryInterval * 2;
+ auto fallbackIntervalMs = milliseconds(fallbackInterval).count();
+
+ // We need to make a wdog with the right fallback options
+ // The interval is set to be noticeably different from the default
+ // so we can always tell the difference
+ Watchdog::Fallback fallback{
+ .action = Watchdog::Action::PowerOff,
+ .interval = static_cast<uint64_t>(fallbackIntervalMs),
+ .always = true,
+ };
+ std::map<Watchdog::Action, Watchdog::TargetName> emptyActionTargets;
+ wdog = std::make_unique<Watchdog>(bus, TEST_PATH, eventP,
+ std::move(emptyActionTargets), std::move(fallback));
+ EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs)));
+ EXPECT_FALSE(wdog->enabled());
+ auto remaining = milliseconds(wdog->timeRemaining());
+ EXPECT_GE(fallbackInterval, remaining);
+ EXPECT_LT(primaryInterval, remaining);
+ EXPECT_FALSE(wdog->timerExpired());
+ EXPECT_TRUE(wdog->timerEnabled());
+
+ // Enable and then verify
+ EXPECT_TRUE(wdog->enabled(true));
+ EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining()));
+
+ // Waiting default expiration
+ EXPECT_EQ(primaryInterval - 1s, waitForWatchdog(primaryInterval));
+
+ // We should now have entered the fallback once the primary expires
+ EXPECT_FALSE(wdog->enabled());
+ remaining = milliseconds(wdog->timeRemaining());
+ EXPECT_GE(fallbackInterval, remaining);
+ EXPECT_LT(primaryInterval, remaining);
+ EXPECT_FALSE(wdog->timerExpired());
+ EXPECT_TRUE(wdog->timerEnabled());
+
+ // Waiting fallback expiration
+ EXPECT_EQ(fallbackInterval - 1s, waitForWatchdog(fallbackInterval));
+
+ // We should now enter the fallback again
+ EXPECT_FALSE(wdog->enabled());
+ remaining = milliseconds(wdog->timeRemaining());
+ EXPECT_GE(fallbackInterval, remaining);
+ EXPECT_LT(primaryInterval, remaining);
+ EXPECT_FALSE(wdog->timerExpired());
+ EXPECT_TRUE(wdog->timerEnabled());
+}
diff --git a/watchdog.cpp b/watchdog.cpp
index b0c7b6f..2bfd9b7 100644
--- a/watchdog.cpp
+++ b/watchdog.cpp
@@ -134,7 +134,7 @@
{
// We only re-arm the watchdog if we were already enabled and have
// a possible fallback
- if (fallback && this->enabled())
+ if (fallback && (fallback->always || this->enabled()))
{
auto interval_ms = fallback->interval;
auto interval_us = duration_cast<microseconds>(milliseconds(interval_ms));
diff --git a/watchdog.hpp b/watchdog.hpp
index 60bad4e..7603689 100644
--- a/watchdog.hpp
+++ b/watchdog.hpp
@@ -37,6 +37,7 @@
struct Fallback {
Action action;
uint64_t interval;
+ bool always;
};
/** @brief Constructs the Watchdog object
@@ -60,7 +61,9 @@
fallback(std::move(fallback)),
timer(event, std::bind(&Watchdog::timeOutHandler, this))
{
- // Nothing
+ // We need to poke the enable mechanism to make sure that the timer
+ // enters the fallback state if the fallback is always enabled.
+ tryFallbackOrDisable();
}
/** @brief Since we are overriding the setter-enabled but not the