blob: f333254528b6a1da103e069cb74038af8c0ddf76 [file] [log] [blame]
Andrew Geissler0029a5d2017-01-24 14:48:35 -06001#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -07002#include <sdbusplus/exception.hpp>
Saqib Khana8006a22017-02-14 11:37:08 -06003#include <phosphor-logging/log.hpp>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05004#include <phosphor-logging/elog-errors.hpp>
5#include "xyz/openbmc_project/Common/error.hpp"
Andrew Geisslera90a31a2016-12-13 16:16:28 -06006#include "chassis_state_manager.hpp"
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05007#include <cereal/archives/json.hpp>
8#include <fstream>
9#include "config.h"
10#include <experimental/filesystem>
11
12// Register class version with Cereal
13CEREAL_CLASS_VERSION(phosphor::state::manager::Chassis, CLASS_VERSION);
Andrew Geisslera90a31a2016-12-13 16:16:28 -060014
15namespace phosphor
16{
17namespace state
18{
19namespace manager
20{
21
22// When you see server:: you know we're referencing our base class
23namespace server = sdbusplus::xyz::openbmc_project::State::server;
24
25using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070026using sdbusplus::exception::SdBusError;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060027
Josh D. King6838ea92017-04-11 13:39:18 -050028constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080029constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
30 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050031constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060032
Josh D. King697474c2017-03-02 11:15:55 -060033constexpr auto ACTIVE_STATE = "active";
34constexpr auto ACTIVATING_STATE = "activating";
35
Andrew Geisslerce80f242017-01-24 13:25:33 -060036/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080037const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
38 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050039 // Use the hard off target to ensure we shutdown immediately
40 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080041 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060042
Andrew Geissler58a18012018-01-19 19:36:05 -080043constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
44constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060045constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
46
Josh D. King697474c2017-03-02 11:15:55 -060047constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
48constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
49
Andrew Geissler0029a5d2017-01-24 14:48:35 -060050void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060051{
Andrew Geissler58a18012018-01-19 19:36:05 -080052 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
53 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler0029a5d2017-01-24 14:48:35 -060054 this->bus.call_noreply(method);
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060055
Andrew Geissler0029a5d2017-01-24 14:48:35 -060056 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060057}
58
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060059// TODO - Will be rewritten once sdbusplus client bindings are in place
60// and persistent storage design is in place and sdbusplus
61// has read property function
62void Chassis::determineInitialState()
63{
64 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080065 auto method = this->bus.new_method_call(
66 "org.openbmc.control.Power", "/org/openbmc/control/power0",
67 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060068
69 method.append("org.openbmc.control.Power", "pgood");
70 auto reply = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -070071 if (reply.is_method_error())
72 {
73 log<level::ERR>("Error in bus call - could not get initial pgood");
74 goto fail;
75 }
76
77 try
78 {
79 reply.read(pgood);
80 }
81 catch (const SdBusError& e)
82 {
83 log<level::ERR>("Error in bus response - bad encoding of pgood",
84 entry("ERROR=%s", e.what()),
85 entry("REPLY_SIG=%s", reply.get_signature()));
86 goto fail;
87 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060088
Andrew Geissler58a18012018-01-19 19:36:05 -080089 if (pgood == 1)
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060090 {
91 log<level::INFO>("Initial Chassis State will be On",
92 entry("CHASSIS_CURRENT_POWER_STATE=%s",
93 convertForMessage(PowerState::On).c_str()));
94 server::Chassis::currentPowerState(PowerState::On);
95 server::Chassis::requestedPowerTransition(Transition::On);
William A. Kennington III09568ff2018-05-11 00:03:12 -070096 return;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060097 }
William A. Kennington III09568ff2018-05-11 00:03:12 -070098
99fail:
100 log<level::INFO>("Initial Chassis State will be Off",
101 entry("CHASSIS_CURRENT_POWER_STATE=%s",
102 convertForMessage(PowerState::Off).c_str()));
103 server::Chassis::currentPowerState(PowerState::Off);
104 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600105
106 return;
107}
108
Andrew Geisslerce80f242017-01-24 13:25:33 -0600109void Chassis::executeTransition(Transition tranReq)
110{
111 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
112
Andrew Geissler58a18012018-01-19 19:36:05 -0800113 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
114 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600115
116 method.append(sysdTarget);
117 method.append("replace");
118
119 this->bus.call_noreply(method);
120
121 return;
122}
123
Josh D. King697474c2017-03-02 11:15:55 -0600124bool Chassis::stateActive(const std::string& target)
125{
126 sdbusplus::message::variant<std::string> currentState;
127 sdbusplus::message::object_path unitTargetPath;
128
Andrew Geissler58a18012018-01-19 19:36:05 -0800129 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
130 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600131
132 method.append(target);
133 auto result = this->bus.call(method);
134
Andrew Geissler58a18012018-01-19 19:36:05 -0800135 // Check that the bus call didn't result in an error
136 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600137 {
138 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
139 entry(" %s", SYSTEMD_INTERFACE));
140 return false;
141 }
142
William A. Kennington III09568ff2018-05-11 00:03:12 -0700143 try
144 {
145 result.read(unitTargetPath);
146 }
147 catch (const SdBusError& e)
148 {
149 log<level::ERR>("Error in bus response - bad encoding for GetUnit",
150 entry("ERROR=%s", e.what()),
151 entry("REPLY_SIG=%s", result.get_signature()));
152 return false;
153 }
Josh D. King697474c2017-03-02 11:15:55 -0600154
Andrew Geissler58a18012018-01-19 19:36:05 -0800155 method = this->bus.new_method_call(
156 SYSTEMD_SERVICE,
157 static_cast<const std::string&>(unitTargetPath).c_str(),
158 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600159
160 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
161 result = this->bus.call(method);
162
Andrew Geissler58a18012018-01-19 19:36:05 -0800163 // Check that the bus call didn't result in an error
164 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600165 {
166 log<level::ERR>("Error in bus call - could not resolve Get for:",
167 entry(" %s", SYSTEMD_PROPERTY_IFACE));
168 return false;
169 }
170
171 result.read(currentState);
172
Andrew Geissler58a18012018-01-19 19:36:05 -0800173 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King697474c2017-03-02 11:15:55 -0600174 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800175 // False - not active
Josh D. King697474c2017-03-02 11:15:55 -0600176 return false;
177 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800178 // True - active
Josh D. King697474c2017-03-02 11:15:55 -0600179 return true;
Josh D. King697474c2017-03-02 11:15:55 -0600180}
181
Patrick Williams8f8ba392017-05-05 15:47:39 -0500182int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600183{
Andrew Geissler58a18012018-01-19 19:36:05 -0800184 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600185 sdbusplus::message::object_path newStateObjPath;
186 std::string newStateUnit{};
187 std::string newStateResult{};
188
Andrew Geissler58a18012018-01-19 19:36:05 -0800189 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700190 try
191 {
192 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
193 }
194 catch (const SdBusError& e)
195 {
196 log<level::ERR>("Error in state change - bad encoding",
197 entry("ERROR=%s", e.what()),
198 entry("REPLY_SIG=%s", msg.get_signature()));
199 return 0;
200 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600201
Andrew Geissler58a18012018-01-19 19:36:05 -0800202 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
203 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600204 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930205 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600206 this->currentPowerState(server::Chassis::PowerState::Off);
207 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800208 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
209 (newStateResult == "done") &&
210 (stateActive(CHASSIS_STATE_POWERON_TGT)))
211 {
212 log<level::INFO>("Received signal that power ON is complete");
213 this->currentPowerState(server::Chassis::PowerState::On);
214 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600215
216 return 0;
217}
218
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600219Chassis::Transition Chassis::requestedPowerTransition(Transition value)
220{
221
222 log<level::INFO>("Change to Chassis Requested Power State",
223 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
224 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600225 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600226 return server::Chassis::requestedPowerTransition(value);
227}
228
229Chassis::PowerState Chassis::currentPowerState(PowerState value)
230{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500231 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600232 log<level::INFO>("Change to Chassis Power State",
233 entry("CHASSIS_CURRENT_POWER_STATE=%s",
234 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500235
236 chassisPowerState = server::Chassis::currentPowerState(value);
237 if (chassisPowerState == PowerState::On)
238 {
239 timer->state(timer::ON);
240 }
241 else
242 {
243 timer->state(timer::OFF);
244 }
245 return chassisPowerState;
246}
247
248uint32_t Chassis::pOHCounter(uint32_t value)
249{
250 if (value != pOHCounter())
251 {
252 ChassisInherit::pOHCounter(value);
253 serialize();
254 }
255 return pOHCounter();
256}
257
258void Chassis::restorePOHCounter()
259{
260 uint32_t counter;
261 if (!deserialize(POH_COUNTER_PERSIST_PATH, counter))
262 {
263 // set to default value
264 pOHCounter(0);
265 }
266 else
267 {
268 pOHCounter(counter);
269 }
270}
271
272fs::path Chassis::serialize(const fs::path& path)
273{
274 std::ofstream os(path.c_str(), std::ios::binary);
275 cereal::JSONOutputArchive oarchive(os);
276 oarchive(pOHCounter());
277 return path;
278}
279
280bool Chassis::deserialize(const fs::path& path, uint32_t& pOHCounter)
281{
282 try
283 {
284 if (fs::exists(path))
285 {
286 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
287 cereal::JSONInputArchive iarchive(is);
288 iarchive(pOHCounter);
289 return true;
290 }
291 return false;
292 }
293 catch (cereal::Exception& e)
294 {
295 log<level::ERR>(e.what());
296 fs::remove(path);
297 return false;
298 }
299 catch (const fs::filesystem_error& e)
300 {
301 return false;
302 }
303
304 return false;
305}
306
307void Chassis::startPOHCounter()
308{
309 using namespace std::chrono_literals;
310 using namespace phosphor::logging;
311 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
312
313 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
314 fs::create_directories(dir);
315
316 sd_event* event = nullptr;
317 auto r = sd_event_default(&event);
318 if (r < 0)
319 {
320 log<level::ERR>("Error creating a default sd_event handler");
321 throw;
322 }
323
324 phosphor::state::manager::EventPtr eventP{event};
325 event = nullptr;
326
327 auto callback = [&]() {
328 if (ChassisInherit::currentPowerState() == PowerState::On)
329 {
330 pOHCounter(pOHCounter() + 1);
331 }
332 };
333
334 try
335 {
336 timer = std::make_unique<phosphor::state::manager::Timer>(
337 eventP, callback, std::chrono::seconds(POH::hour),
338 phosphor::state::manager::timer::ON);
339 bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
340 r = sd_event_loop(eventP.get());
341 if (r < 0)
342 {
343 log<level::ERR>("Error occurred during the sd_event_loop",
344 entry("RC=%d", r));
345 elog<InternalFailure>();
346 }
347 }
348 catch (InternalFailure& e)
349 {
350 phosphor::logging::commit<InternalFailure>();
351 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600352}
353
354} // namespace manager
355} // namespace state
356} // namepsace phosphor