142506393SWilliam A. Kennington III #include "watchdog.hpp"
242506393SWilliam A. Kennington III
342506393SWilliam A. Kennington III #include <phosphor-logging/elog.hpp>
442506393SWilliam A. Kennington III #include <phosphor-logging/log.hpp>
542506393SWilliam A. Kennington III #include <sdbusplus/exception.hpp>
642506393SWilliam A. Kennington III #include <xyz/openbmc_project/Common/error.hpp>
742506393SWilliam A. Kennington III
8*6ac6a347SPatrick Williams #include <algorithm>
9*6ac6a347SPatrick Williams #include <chrono>
10*6ac6a347SPatrick Williams #include <string_view>
11*6ac6a347SPatrick Williams
1242506393SWilliam A. Kennington III namespace phosphor
1342506393SWilliam A. Kennington III {
1442506393SWilliam A. Kennington III namespace watchdog
1542506393SWilliam A. Kennington III {
1642506393SWilliam A. Kennington III using namespace std::chrono;
1742506393SWilliam A. Kennington III using namespace std::chrono_literals;
1842506393SWilliam A. Kennington III using namespace phosphor::logging;
1942506393SWilliam A. Kennington III
2042506393SWilliam A. Kennington III using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
2142506393SWilliam A. Kennington III
2242506393SWilliam A. Kennington III // systemd service to kick start a target.
2342506393SWilliam A. Kennington III constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
2442506393SWilliam A. Kennington III constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
2542506393SWilliam A. Kennington III constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
2642506393SWilliam A. Kennington III
resetTimeRemaining(bool enableWatchdog)2742506393SWilliam A. Kennington III void Watchdog::resetTimeRemaining(bool enableWatchdog)
2842506393SWilliam A. Kennington III {
2942506393SWilliam A. Kennington III timeRemaining(interval());
3042506393SWilliam A. Kennington III if (enableWatchdog)
3142506393SWilliam A. Kennington III {
3242506393SWilliam A. Kennington III enabled(true);
3342506393SWilliam A. Kennington III }
3442506393SWilliam A. Kennington III }
3542506393SWilliam A. Kennington III
3642506393SWilliam A. Kennington III // Enable or disable watchdog
enabled(bool value)3742506393SWilliam A. Kennington III bool Watchdog::enabled(bool value)
3842506393SWilliam A. Kennington III {
3942506393SWilliam A. Kennington III if (!value)
4042506393SWilliam A. Kennington III {
4142506393SWilliam A. Kennington III // Make sure we accurately reflect our enabled state to the
4242506393SWilliam A. Kennington III // tryFallbackOrDisable() call
4342506393SWilliam A. Kennington III WatchdogInherits::enabled(value);
4442506393SWilliam A. Kennington III
4542506393SWilliam A. Kennington III // Attempt to fallback or disable our timer if needed
4642506393SWilliam A. Kennington III tryFallbackOrDisable();
4742506393SWilliam A. Kennington III
4842506393SWilliam A. Kennington III return false;
4942506393SWilliam A. Kennington III }
5042506393SWilliam A. Kennington III else if (!this->enabled())
5142506393SWilliam A. Kennington III {
5242506393SWilliam A. Kennington III auto interval_ms = this->interval();
5342506393SWilliam A. Kennington III timer.restart(milliseconds(interval_ms));
5442506393SWilliam A. Kennington III log<level::INFO>("watchdog: enabled and started",
5542506393SWilliam A. Kennington III entry("INTERVAL=%llu", interval_ms));
5642506393SWilliam A. Kennington III }
5742506393SWilliam A. Kennington III
5842506393SWilliam A. Kennington III return WatchdogInherits::enabled(value);
5942506393SWilliam A. Kennington III }
6042506393SWilliam A. Kennington III
6142506393SWilliam A. Kennington III // Get the remaining time before timer expires.
6242506393SWilliam A. Kennington III // If the timer is disabled, returns 0
timeRemaining() const6342506393SWilliam A. Kennington III uint64_t Watchdog::timeRemaining() const
6442506393SWilliam A. Kennington III {
6542506393SWilliam A. Kennington III // timer may have already expired and disabled
6642506393SWilliam A. Kennington III if (!timerEnabled())
6742506393SWilliam A. Kennington III {
6842506393SWilliam A. Kennington III return 0;
6942506393SWilliam A. Kennington III }
7042506393SWilliam A. Kennington III
7142506393SWilliam A. Kennington III return duration_cast<milliseconds>(timer.getRemaining()).count();
7242506393SWilliam A. Kennington III }
7342506393SWilliam A. Kennington III
7442506393SWilliam A. Kennington III // Reset the timer to a new expiration value
timeRemaining(uint64_t value)7542506393SWilliam A. Kennington III uint64_t Watchdog::timeRemaining(uint64_t value)
7642506393SWilliam A. Kennington III {
7742506393SWilliam A. Kennington III if (!timerEnabled())
7842506393SWilliam A. Kennington III {
7942506393SWilliam A. Kennington III // We don't need to update the timer because it is off
8042506393SWilliam A. Kennington III return 0;
8142506393SWilliam A. Kennington III }
8242506393SWilliam A. Kennington III
8342506393SWilliam A. Kennington III if (this->enabled())
8442506393SWilliam A. Kennington III {
8542506393SWilliam A. Kennington III // Update interval to minInterval if applicable
8642506393SWilliam A. Kennington III value = std::max(value, minInterval);
8742506393SWilliam A. Kennington III }
8842506393SWilliam A. Kennington III else
8942506393SWilliam A. Kennington III {
9042506393SWilliam A. Kennington III // Having a timer but not displaying an enabled value means we
9142506393SWilliam A. Kennington III // are inside of the fallback
9242506393SWilliam A. Kennington III value = fallback->interval;
9342506393SWilliam A. Kennington III }
9442506393SWilliam A. Kennington III
9542506393SWilliam A. Kennington III // Update new expiration
9642506393SWilliam A. Kennington III timer.setRemaining(milliseconds(value));
9742506393SWilliam A. Kennington III
9842506393SWilliam A. Kennington III // Update Base class data.
9942506393SWilliam A. Kennington III return WatchdogInherits::timeRemaining(value);
10042506393SWilliam A. Kennington III }
10142506393SWilliam A. Kennington III
10242506393SWilliam A. Kennington III // Set value of Interval
interval(uint64_t value)10342506393SWilliam A. Kennington III uint64_t Watchdog::interval(uint64_t value)
10442506393SWilliam A. Kennington III {
10542506393SWilliam A. Kennington III return WatchdogInherits::interval(std::max(value, minInterval));
10642506393SWilliam A. Kennington III }
10742506393SWilliam A. Kennington III
10842506393SWilliam A. Kennington III // Optional callback function on timer expiration
timeOutHandler()10942506393SWilliam A. Kennington III void Watchdog::timeOutHandler()
11042506393SWilliam A. Kennington III {
11142506393SWilliam A. Kennington III Action action = expireAction();
11242506393SWilliam A. Kennington III if (!this->enabled())
11342506393SWilliam A. Kennington III {
11442506393SWilliam A. Kennington III action = fallback->action;
11542506393SWilliam A. Kennington III }
11642506393SWilliam A. Kennington III
11742506393SWilliam A. Kennington III expiredTimerUse(currentTimerUse());
11842506393SWilliam A. Kennington III
11942506393SWilliam A. Kennington III auto target = actionTargetMap.find(action);
12042506393SWilliam A. Kennington III if (target == actionTargetMap.end())
12142506393SWilliam A. Kennington III {
12242506393SWilliam A. Kennington III log<level::INFO>("watchdog: Timed out with no target",
12342506393SWilliam A. Kennington III entry("ACTION=%s", convertForMessage(action).c_str()),
12442506393SWilliam A. Kennington III entry("TIMER_USE=%s",
12542506393SWilliam A. Kennington III convertForMessage(expiredTimerUse()).c_str()));
12642506393SWilliam A. Kennington III }
12742506393SWilliam A. Kennington III else
12842506393SWilliam A. Kennington III {
12942506393SWilliam A. Kennington III log<level::INFO>(
13042506393SWilliam A. Kennington III "watchdog: Timed out",
13142506393SWilliam A. Kennington III entry("ACTION=%s", convertForMessage(action).c_str()),
13242506393SWilliam A. Kennington III entry("TIMER_USE=%s", convertForMessage(expiredTimerUse()).c_str()),
13342506393SWilliam A. Kennington III entry("TARGET=%s", target->second.c_str()));
13442506393SWilliam A. Kennington III
13542506393SWilliam A. Kennington III try
13642506393SWilliam A. Kennington III {
13742506393SWilliam A. Kennington III auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
13842506393SWilliam A. Kennington III SYSTEMD_INTERFACE, "StartUnit");
13942506393SWilliam A. Kennington III method.append(target->second);
14042506393SWilliam A. Kennington III method.append("replace");
14142506393SWilliam A. Kennington III
14242506393SWilliam A. Kennington III bus.call_noreply(method);
14342506393SWilliam A. Kennington III }
14473bd5277SPatrick Williams catch (const sdbusplus::exception_t& e)
14542506393SWilliam A. Kennington III {
14642506393SWilliam A. Kennington III log<level::ERR>("watchdog: Failed to start unit",
14742506393SWilliam A. Kennington III entry("TARGET=%s", target->second.c_str()),
14842506393SWilliam A. Kennington III entry("ERROR=%s", e.what()));
14942506393SWilliam A. Kennington III commit<InternalFailure>();
15042506393SWilliam A. Kennington III }
15142506393SWilliam A. Kennington III }
152fcc00392Sselvaganapathi try
153fcc00392Sselvaganapathi {
154fcc00392Sselvaganapathi auto signal = bus.new_signal(objPath.data(),
155fcc00392Sselvaganapathi "xyz.openbmc_project.Watchdog", "Timeout");
156fcc00392Sselvaganapathi signal.append(convertForMessage(action).c_str());
157fcc00392Sselvaganapathi signal.signal_send();
158fcc00392Sselvaganapathi }
159fcc00392Sselvaganapathi catch (const sdbusplus::exception_t& e)
160fcc00392Sselvaganapathi {
161fcc00392Sselvaganapathi log<level::ERR>("watchdog: failed to send timeout signal",
162fcc00392Sselvaganapathi entry("ERROR=%s", e.what()));
163fcc00392Sselvaganapathi }
16442506393SWilliam A. Kennington III
165d1b1e79bSWilliam A. Kennington III if (exitAfterTimeout)
166d1b1e79bSWilliam A. Kennington III {
167d1b1e79bSWilliam A. Kennington III timer.get_event().exit(0);
168d1b1e79bSWilliam A. Kennington III }
169d1b1e79bSWilliam A. Kennington III
17042506393SWilliam A. Kennington III tryFallbackOrDisable();
17142506393SWilliam A. Kennington III }
17242506393SWilliam A. Kennington III
tryFallbackOrDisable()17342506393SWilliam A. Kennington III void Watchdog::tryFallbackOrDisable()
17442506393SWilliam A. Kennington III {
17542506393SWilliam A. Kennington III // We only re-arm the watchdog if we were already enabled and have
17642506393SWilliam A. Kennington III // a possible fallback
17742506393SWilliam A. Kennington III if (fallback && (fallback->always || this->enabled()))
17842506393SWilliam A. Kennington III {
17942506393SWilliam A. Kennington III auto interval_ms = fallback->interval;
18042506393SWilliam A. Kennington III timer.restart(milliseconds(interval_ms));
18142506393SWilliam A. Kennington III log<level::INFO>("watchdog: falling back",
18242506393SWilliam A. Kennington III entry("INTERVAL=%llu", interval_ms));
18342506393SWilliam A. Kennington III }
18442506393SWilliam A. Kennington III else if (timerEnabled())
18542506393SWilliam A. Kennington III {
18642506393SWilliam A. Kennington III timer.setEnabled(false);
18742506393SWilliam A. Kennington III
18842506393SWilliam A. Kennington III log<level::INFO>("watchdog: disabled");
18942506393SWilliam A. Kennington III }
19042506393SWilliam A. Kennington III
19142506393SWilliam A. Kennington III // Make sure we accurately reflect our enabled state to the
19242506393SWilliam A. Kennington III // dbus interface.
19342506393SWilliam A. Kennington III WatchdogInherits::enabled(false);
19442506393SWilliam A. Kennington III }
19542506393SWilliam A. Kennington III
19642506393SWilliam A. Kennington III } // namespace watchdog
19742506393SWilliam A. Kennington III } // namespace phosphor
198