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