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/lg2.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         lg2::error("WatchdogService: Method error resetting time remaining, "
50                    "ENABLE_WATCHDOG: {ENABLE_WATCHDOG}, ERROR: {ERROR}",
51                    "ENABLE_WATCHDOG", enableWatchdog, "ERROR", e);
52         elog<InternalFailure>();
53     }
54 }
55 
56 WatchdogService::Properties WatchdogService::getProperties()
57 {
58     bool wasValid = wd_service.isValid(bus);
59     auto request = wd_service.newMethodCall(bus, prop_intf, "GetAll");
60     request.append(wd_intf);
61 
62     std::map<std::string, std::variant<bool, uint64_t, std::string>> properties;
63     try
64     {
65         auto response = bus.call(request);
66         response.read(properties);
67     }
68     catch (const std::exception& e)
69     {
70         wd_service.invalidate();
71         if (wasValid)
72         {
73             // Retry the request once in case the cached service was stale
74             return getProperties();
75         }
76         lg2::error("WatchdogService: Method error getting properties: {ERROR}",
77                    "ERROR", e);
78         elog<InternalFailure>();
79     }
80 
81     try
82     {
83         Properties wd_prop;
84         wd_prop.initialized = std::get<bool>(properties.at("Initialized"));
85         wd_prop.enabled = std::get<bool>(properties.at("Enabled"));
86         wd_prop.expireAction = Watchdog::convertActionFromString(
87             std::get<std::string>(properties.at("ExpireAction")));
88         wd_prop.timerUse = Watchdog::convertTimerUseFromString(
89             std::get<std::string>(properties.at("CurrentTimerUse")));
90         wd_prop.expiredTimerUse = Watchdog::convertTimerUseFromString(
91             std::get<std::string>(properties.at("ExpiredTimerUse")));
92 
93         wd_prop.interval = std::get<uint64_t>(properties.at("Interval"));
94         wd_prop.timeRemaining =
95             std::get<uint64_t>(properties.at("TimeRemaining"));
96         return wd_prop;
97     }
98     catch (const std::exception& e)
99     {
100         lg2::error("WatchdogService: Decode error in get properties: {ERROR}",
101                    "ERROR", e);
102         elog<InternalFailure>();
103     }
104 
105     // Needed instead of elog<InternalFailure>() since the compiler can't
106     // deduce the that elog<>() always throws
107     throw std::runtime_error(
108         "WatchdogService: Should not reach end of getProperties");
109 }
110 
111 template <typename T>
112 T WatchdogService::getProperty(const std::string& key)
113 {
114     bool wasValid = wd_service.isValid(bus);
115     auto request = wd_service.newMethodCall(bus, prop_intf, "Get");
116     request.append(wd_intf, key);
117     try
118     {
119         auto response = bus.call(request);
120         std::variant<T> value;
121         response.read(value);
122         return std::get<T>(value);
123     }
124     catch (const std::exception& e)
125     {
126         wd_service.invalidate();
127         if (wasValid)
128         {
129             // Retry the request once in case the cached service was stale
130             return getProperty<T>(key);
131         }
132         lg2::error("WatchdogService: Method error getting {PROPERTY}: {ERROR}",
133                    "PROPERTY", key, "ERROR", e);
134         elog<InternalFailure>();
135     }
136 
137     // Needed instead of elog<InternalFailure>() since the compiler can't
138     // deduce the that elog<>() always throws
139     throw std::runtime_error(
140         "WatchdogService: Should not reach end of getProperty");
141 }
142 
143 template <typename T>
144 void WatchdogService::setProperty(const std::string& key, const T& val)
145 {
146     bool wasValid = wd_service.isValid(bus);
147     auto request = wd_service.newMethodCall(bus, prop_intf, "Set");
148     request.append(wd_intf, key, std::variant<T>(val));
149     try
150     {
151         auto response = bus.call(request);
152     }
153     catch (const std::exception& e)
154     {
155         wd_service.invalidate();
156         if (wasValid)
157         {
158             // Retry the request once in case the cached service was stale
159             setProperty(key, val);
160             return;
161         }
162         lg2::error("WatchdogService: Method error setting {PROPERTY}: {ERROR}",
163                    "PROPERTY", key, "ERROR", e);
164         elog<InternalFailure>();
165     }
166 }
167 
168 bool WatchdogService::getInitialized()
169 {
170     return getProperty<bool>("Initialized");
171 }
172 
173 void WatchdogService::setInitialized(bool initialized)
174 {
175     setProperty("Initialized", initialized);
176 }
177 
178 void WatchdogService::setEnabled(bool enabled)
179 {
180     setProperty("Enabled", enabled);
181 }
182 
183 void WatchdogService::setLogTimeout(bool LogTimeout)
184 {
185     setProperty("LogTimeout", LogTimeout);
186 }
187 
188 void WatchdogService::setExpireAction(Action expireAction)
189 {
190     setProperty("ExpireAction", convertForMessage(expireAction));
191 }
192 
193 void WatchdogService::setTimerUse(TimerUse timerUse)
194 {
195     setProperty("CurrentTimerUse", convertForMessage(timerUse));
196 }
197 
198 void WatchdogService::setExpiredTimerUse(TimerUse timerUse)
199 {
200     setProperty("ExpiredTimerUse", convertForMessage(timerUse));
201 }
202 
203 void WatchdogService::setInterval(uint64_t interval)
204 {
205     setProperty("Interval", interval);
206 }
207