1 #pragma once
2 
3 #include <sdbusplus/bus.hpp>
4 #include <sdbusplus/server/object.hpp>
5 #include <sdeventplus/event.hpp>
6 #include <sdeventplus/utility/timer.hpp>
7 #include <xyz/openbmc_project/State/Watchdog/server.hpp>
8 
9 #include <functional>
10 #include <optional>
11 #include <string_view>
12 #include <unordered_map>
13 #include <utility>
14 
15 namespace phosphor
16 {
17 namespace watchdog
18 {
19 
20 constexpr auto DEFAULT_MIN_INTERVAL_MS = 0;
21 namespace Base = sdbusplus::xyz::openbmc_project::State::server;
22 using WatchdogInherits = sdbusplus::server::object_t<Base::Watchdog>;
23 
24 /** @class Watchdog
25  *  @brief OpenBMC watchdog implementation.
26  *  @details A concrete implementation for the
27  *  xyz.openbmc_project.State.Watchdog DBus API.
28  */
29 class Watchdog : public WatchdogInherits
30 {
31   public:
32     Watchdog() = delete;
33     ~Watchdog() = default;
34     Watchdog(const Watchdog&) = delete;
35     Watchdog& operator=(const Watchdog&) = delete;
36     Watchdog(Watchdog&&) = delete;
37     Watchdog& operator=(Watchdog&&) = delete;
38 
39     /** @brief Type used to hold the name of a systemd target.
40      */
41     using TargetName = std::string;
42 
43     /** @brief Type used to store the mapping of a Watchdog timeout
44      *         action to a systemd target.
45      */
46     using ActionTargetMap = std::unordered_map<Action, TargetName>;
47 
48     /** @brief Type used to specify the parameters of a fallback watchdog
49      */
50     struct Fallback
51     {
52         Action action;
53         uint64_t interval;
54         bool always;
55     };
56 
57     /** @brief Constructs the Watchdog object
58      *
59      *  @param[in] bus              - DBus bus to attach to.
60      *  @param[in] objPath          - Object path to attach to.
61      *  @param[in] event            - reference to sdeventplus::Event loop
62      *  @param[in] actionTargets    - map of systemd targets called on timeout
63      *  @param[in] fallback         - fallback watchdog
64      *  @param[in] minInterval      - minimum intervale value allowed
65      *  @param[in] defaultInterval  - default interval to start with
66      *  @param[in] exitAfterTimeout - should the event loop be terminated
67      */
Watchdog(sdbusplus::bus_t & bus,const char * objPath,const sdeventplus::Event & event,ActionTargetMap && actionTargetMap={},std::optional<Fallback> && fallback=std::nullopt,uint64_t minInterval=DEFAULT_MIN_INTERVAL_MS,uint64_t defaultInterval=0,bool exitAfterTimeout=false)68     Watchdog(sdbusplus::bus_t& bus, const char* objPath,
69              const sdeventplus::Event& event,
70              ActionTargetMap&& actionTargetMap = {},
71              std::optional<Fallback>&& fallback = std::nullopt,
72              uint64_t minInterval = DEFAULT_MIN_INTERVAL_MS,
73              uint64_t defaultInterval = 0, bool exitAfterTimeout = false) :
74         WatchdogInherits(bus, objPath),
75         bus(bus), actionTargetMap(std::move(actionTargetMap)),
76         fallback(fallback), minInterval(minInterval),
77         timer(event, std::bind(&Watchdog::timeOutHandler, this)),
78         objPath(objPath), exitAfterTimeout(exitAfterTimeout)
79     {
80         // Use default if passed in otherwise just use default that comes
81         // with object
82         if (defaultInterval)
83         {
84             interval(defaultInterval);
85         }
86         else
87         {
88             interval(interval());
89         }
90         // We need to poke the enable mechanism to make sure that the timer
91         // enters the fallback state if the fallback is always enabled.
92         tryFallbackOrDisable();
93     }
94 
95     /** @brief Resets the TimeRemaining to the configured Interval
96      *         Optionally enables the watchdog.
97      *
98      *  @param[in] enableWatchdog - Should the call enable the watchdog
99      */
100     void resetTimeRemaining(bool enableWatchdog) override;
101 
102     /** @brief Since we are overriding the setter-enabled but not the
103      *         getter-enabled, we need to have this using in order to
104      *         allow passthrough usage of the getter-enabled.
105      */
106     using Base::Watchdog::enabled;
107 
108     /** @brief Enable or disable watchdog
109      *         If a watchdog state is changed from disable to enable,
110      *         the watchdog timer is set with the default expiration
111      *         interval and it starts counting down.
112      *         If a watchdog is already enabled, setting @value to true
113      *         has no effect.
114      *
115      *  @param[in] value - 'true' to enable. 'false' to disable
116      *
117      *  @return : applied value if success, previous value otherwise
118      */
119     bool enabled(bool value) override;
120 
121     /** @brief Gets the remaining time before watchdog expires.
122      *
123      *  @return 0 if watchdog is expired.
124      *          Remaining time in milliseconds otherwise.
125      */
126     uint64_t timeRemaining() const override;
127 
128     /** @brief Reset timer to expire after new timeout in milliseconds.
129      *
130      *  @param[in] value - the time in milliseconds after which
131      *                     the watchdog will expire
132      *
133      *  @return: updated timeout value if watchdog is enabled.
134      *           0 otherwise.
135      */
136     uint64_t timeRemaining(uint64_t value) override;
137 
138     /** @brief Get value of Interval
139      *
140      *
141      *  @return: current interval
142      *
143      */
144     using WatchdogInherits::interval;
145 
146     /** @brief Set value of Interval
147      *
148      *  @param[in] value - interval time to set
149      *
150      *  @return: interval that was set
151      *
152      */
153     uint64_t interval(uint64_t value) override;
154 
155     /** @brief Tells if the referenced timer is expired or not */
timerExpired() const156     inline auto timerExpired() const
157     {
158         return timer.hasExpired();
159     }
160 
161     /** @brief Tells if the timer is running or not */
timerEnabled() const162     inline bool timerEnabled() const
163     {
164         return timer.isEnabled();
165     }
166 
167   private:
168     /** @brief sdbusplus handle */
169     sdbusplus::bus_t& bus;
170 
171     /** @brief Map of systemd units to be started when the timer expires */
172     ActionTargetMap actionTargetMap;
173 
174     /** @brief Fallback timer options */
175     std::optional<Fallback> fallback;
176 
177     /** @brief Minimum watchdog interval value */
178     uint64_t minInterval;
179 
180     /** @brief Contained timer object */
181     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
182 
183     /** @brief Optional Callback handler on timer expirartion */
184     void timeOutHandler();
185 
186     /** @brief Attempt to enter the fallback watchdog or disables it */
187     void tryFallbackOrDisable();
188 
189     /** @brief Object path of the watchdog */
190     std::string_view objPath;
191 
192     /** @brief Do we terminate after exit */
193     bool exitAfterTimeout;
194 };
195 
196 } // namespace watchdog
197 } // namespace phosphor
198