1 #include "watchdog_service.hpp"
2 
3 #include <ipmid/api.hpp>
4 #include <phosphor-logging/elog-errors.hpp>
5 #include <phosphor-logging/elog.hpp>
6 #include <phosphor-logging/log.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <sdbusplus/message.hpp>
9 #include <xyz/openbmc_project/Common/error.hpp>
10 #include <xyz/openbmc_project/State/Watchdog/server.hpp>
11 
12 #include <exception>
13 #include <stdexcept>
14 #include <string>
15 
16 using phosphor::logging::elog;
17 using phosphor::logging::entry;
18 using phosphor::logging::level;
19 using phosphor::logging::log;
20 using sdbusplus::common::xyz::openbmc_project::state::convertForMessage;
21 using sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
22 using sdbusplus::server::xyz::openbmc_project::state::Watchdog;
23 
24 static constexpr char wd_path[] = "/xyz/openbmc_project/watchdog/host0";
25 static constexpr char wd_intf[] = "xyz.openbmc_project.State.Watchdog";
26 static constexpr char prop_intf[] = "org.freedesktop.DBus.Properties";
27 
28 ipmi::ServiceCache WatchdogService::wd_service(wd_intf, wd_path);
29 
30 WatchdogService::WatchdogService() : bus(ipmid_get_sd_bus_connection()) {}
31 
32 void WatchdogService::resetTimeRemaining(bool enableWatchdog)
33 {
34     bool wasValid = wd_service.isValid(bus);
35     auto request = wd_service.newMethodCall(bus, wd_intf, "ResetTimeRemaining");
36     request.append(enableWatchdog);
37     try
38     {
39         auto response = bus.call(request);
40     }
41     catch (const std::exception& e)
42     {
43         wd_service.invalidate();
44         if (wasValid)
45         {
46             // Retry the request once in case the cached service was stale
47             return resetTimeRemaining(enableWatchdog);
48         }
49         log<level::ERR>(
50             "WatchdogService: Method error resetting time remaining",
51             entry("ENABLE_WATCHDOG=%d", !!enableWatchdog),
52             entry("ERROR=%s", e.what()));
53         elog<InternalFailure>();
54     }
55 }
56 
57 WatchdogService::Properties WatchdogService::getProperties()
58 {
59     bool wasValid = wd_service.isValid(bus);
60     auto request = wd_service.newMethodCall(bus, prop_intf, "GetAll");
61     request.append(wd_intf);
62 
63     std::map<std::string, std::variant<bool, uint64_t, std::string>> properties;
64     try
65     {
66         auto response = bus.call(request);
67         response.read(properties);
68     }
69     catch (const std::exception& e)
70     {
71         wd_service.invalidate();
72         if (wasValid)
73         {
74             // Retry the request once in case the cached service was stale
75             return getProperties();
76         }
77         log<level::ERR>("WatchdogService: Method error getting properties",
78                         entry("ERROR=%s", e.what()));
79         elog<InternalFailure>();
80     }
81 
82     try
83     {
84         Properties wd_prop;
85         wd_prop.initialized = std::get<bool>(properties.at("Initialized"));
86         wd_prop.enabled = std::get<bool>(properties.at("Enabled"));
87         wd_prop.expireAction = Watchdog::convertActionFromString(
88             std::get<std::string>(properties.at("ExpireAction")));
89         wd_prop.timerUse = Watchdog::convertTimerUseFromString(
90             std::get<std::string>(properties.at("CurrentTimerUse")));
91         wd_prop.expiredTimerUse = Watchdog::convertTimerUseFromString(
92             std::get<std::string>(properties.at("ExpiredTimerUse")));
93 
94         wd_prop.interval = std::get<uint64_t>(properties.at("Interval"));
95         wd_prop.timeRemaining =
96             std::get<uint64_t>(properties.at("TimeRemaining"));
97         return wd_prop;
98     }
99     catch (const std::exception& e)
100     {
101         log<level::ERR>("WatchdogService: Decode error in get properties",
102                         entry("ERROR=%s", e.what()));
103         elog<InternalFailure>();
104     }
105 
106     // Needed instead of elog<InternalFailure>() since the compiler can't
107     // deduce the that elog<>() always throws
108     throw std::runtime_error(
109         "WatchdogService: Should not reach end of getProperties");
110 }
111 
112 template <typename T>
113 T WatchdogService::getProperty(const std::string& key)
114 {
115     bool wasValid = wd_service.isValid(bus);
116     auto request = wd_service.newMethodCall(bus, prop_intf, "Get");
117     request.append(wd_intf, key);
118     try
119     {
120         auto response = bus.call(request);
121         std::variant<T> value;
122         response.read(value);
123         return std::get<T>(value);
124     }
125     catch (const std::exception& e)
126     {
127         wd_service.invalidate();
128         if (wasValid)
129         {
130             // Retry the request once in case the cached service was stale
131             return getProperty<T>(key);
132         }
133         log<level::ERR>("WatchdogService: Method error getting property",
134                         entry("PROPERTY=%s", key.c_str()),
135                         entry("ERROR=%s", e.what()));
136         elog<InternalFailure>();
137     }
138 
139     // Needed instead of elog<InternalFailure>() since the compiler can't
140     // deduce the that elog<>() always throws
141     throw std::runtime_error(
142         "WatchdogService: Should not reach end of getProperty");
143 }
144 
145 template <typename T>
146 void WatchdogService::setProperty(const std::string& key, const T& val)
147 {
148     bool wasValid = wd_service.isValid(bus);
149     auto request = wd_service.newMethodCall(bus, prop_intf, "Set");
150     request.append(wd_intf, key, std::variant<T>(val));
151     try
152     {
153         auto response = bus.call(request);
154     }
155     catch (const std::exception& e)
156     {
157         wd_service.invalidate();
158         if (wasValid)
159         {
160             // Retry the request once in case the cached service was stale
161             setProperty(key, val);
162             return;
163         }
164         log<level::ERR>("WatchdogService: Method error setting property",
165                         entry("PROPERTY=%s", key.c_str()),
166                         entry("ERROR=%s", e.what()));
167         elog<InternalFailure>();
168     }
169 }
170 
171 bool WatchdogService::getInitialized()
172 {
173     return getProperty<bool>("Initialized");
174 }
175 
176 void WatchdogService::setInitialized(bool initialized)
177 {
178     setProperty("Initialized", initialized);
179 }
180 
181 void WatchdogService::setEnabled(bool enabled)
182 {
183     setProperty("Enabled", enabled);
184 }
185 
186 void WatchdogService::setLogTimeout(bool LogTimeout)
187 {
188     setProperty("LogTimeout", LogTimeout);
189 }
190 
191 void WatchdogService::setExpireAction(Action expireAction)
192 {
193     setProperty("ExpireAction", convertForMessage(expireAction));
194 }
195 
196 void WatchdogService::setTimerUse(TimerUse timerUse)
197 {
198     setProperty("CurrentTimerUse", convertForMessage(timerUse));
199 }
200 
201 void WatchdogService::setExpiredTimerUse(TimerUse timerUse)
202 {
203     setProperty("ExpiredTimerUse", convertForMessage(timerUse));
204 }
205 
206 void WatchdogService::setInterval(uint64_t interval)
207 {
208     setProperty("Interval", interval);
209 }
210