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