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