blob: 9e7ec234c6bc41aed70a96cadbfa0f8935956135 [file] [log] [blame]
#include "watchdog.hpp"
#include <chrono>
#include <phosphor-logging/log.hpp>
namespace phosphor
{
namespace watchdog
{
using namespace std::chrono;
using namespace std::chrono_literals;
using namespace phosphor::logging;
// systemd service to kick start a target.
constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
void Watchdog::resetTimeRemaining(bool enableWatchdog)
{
timeRemaining(interval());
if (enableWatchdog)
{
enabled(true);
}
}
// Enable or disable watchdog
bool Watchdog::enabled(bool value)
{
if (!value)
{
// Make sure we accurately reflect our enabled state to the
// tryFallbackOrDisable() call
WatchdogInherits::enabled(value);
// Attempt to fallback or disable our timer if needed
tryFallbackOrDisable();
return false;
}
else if (!this->enabled())
{
// Start ONESHOT timer. Timer handles all in usec
auto usec = duration_cast<microseconds>(milliseconds(this->interval()));
// Update new expiration
timer.clearExpired();
timer.start(usec);
// Enable timer
timer.setEnabled<std::true_type>();
log<level::INFO>("watchdog: enabled and started",
entry("INTERVAL=%llu", this->interval()));
}
return WatchdogInherits::enabled(value);
}
// Get the remaining time before timer expires.
// If the timer is disabled, returns 0
uint64_t Watchdog::timeRemaining() const
{
uint64_t timeRemain = 0;
// timer may have already expired and disabled
if (timerEnabled())
{
// the one-shot timer does not expire yet
auto expiry = duration_cast<milliseconds>(timer.getRemaining());
// convert to msec per interface expectation.
auto timeNow = duration_cast<milliseconds>(Timer::getCurrentTime());
// Its possible that timer may have expired by now.
// So need to cross verify.
timeRemain = (expiry > timeNow) ? (expiry - timeNow).count() : 0;
}
return timeRemain;
}
// Reset the timer to a new expiration value
uint64_t Watchdog::timeRemaining(uint64_t value)
{
if (!timerEnabled())
{
// We don't need to update the timer because it is off
return 0;
}
if (!this->enabled())
{
// Having a timer but not displaying an enabled value means we
// are inside of the fallback
value = fallback->interval;
}
// Update new expiration
auto usec = duration_cast<microseconds>(milliseconds(value));
timer.start(usec);
// Update Base class data.
return WatchdogInherits::timeRemaining(value);
}
// Optional callback function on timer expiration
void Watchdog::timeOutHandler()
{
Action action = expireAction();
if (!this->enabled())
{
action = fallback->action;
}
auto target = actionTargets.find(action);
if (target == actionTargets.end())
{
log<level::INFO>("watchdog: Timed out with no target",
entry("ACTION=%s", convertForMessage(action).c_str()));
}
else
{
auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
SYSTEMD_INTERFACE, "StartUnit");
method.append(target->second);
method.append("replace");
log<level::INFO>("watchdog: Timed out",
entry("ACTION=%s", convertForMessage(action).c_str()),
entry("TARGET=%s", target->second.c_str()));
bus.call_noreply(method);
}
tryFallbackOrDisable();
}
void Watchdog::tryFallbackOrDisable()
{
// We only re-arm the watchdog if we were already enabled and have
// a possible fallback
if (fallback && (fallback->always || this->enabled()))
{
auto interval_ms = fallback->interval;
auto interval_us =
duration_cast<microseconds>(milliseconds(interval_ms));
timer.clearExpired();
timer.start(interval_us);
timer.setEnabled<std::true_type>();
log<level::INFO>("watchdog: falling back",
entry("INTERVAL=%llu", interval_ms));
}
else if (timerEnabled())
{
timer.setEnabled<std::false_type>();
log<level::INFO>("watchdog: disabled");
}
// Make sure we accurately reflect our enabled state to the
// dbus interface.
WatchdogInherits::enabled(false);
}
} // namespace watchdog
} // namespace phosphor