xref: /openbmc/phosphor-watchdog/src/watchdog.cpp (revision 6ac6a347132c431b4ab05ffee0d1bd15a532fba9)
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