blob: d78a6f70af8b1d9a7bd2731f947c9f386f8a7b6a [file] [log] [blame]
Andrew Geissler0029a5d2017-01-24 14:48:35 -06001#include <sdbusplus/bus.hpp>
Saqib Khana8006a22017-02-14 11:37:08 -06002#include <phosphor-logging/log.hpp>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05003#include <phosphor-logging/elog-errors.hpp>
4#include "xyz/openbmc_project/Common/error.hpp"
Andrew Geisslera90a31a2016-12-13 16:16:28 -06005#include "chassis_state_manager.hpp"
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05006#include <cereal/archives/json.hpp>
7#include <fstream>
8#include "config.h"
9#include <experimental/filesystem>
10
11// Register class version with Cereal
12CEREAL_CLASS_VERSION(phosphor::state::manager::Chassis, CLASS_VERSION);
Andrew Geisslera90a31a2016-12-13 16:16:28 -060013
14namespace phosphor
15{
16namespace state
17{
18namespace manager
19{
20
21// When you see server:: you know we're referencing our base class
22namespace server = sdbusplus::xyz::openbmc_project::State::server;
23
24using namespace phosphor::logging;
25
Josh D. King6838ea92017-04-11 13:39:18 -050026constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080027constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
28 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050029constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060030
Josh D. King697474c2017-03-02 11:15:55 -060031constexpr auto ACTIVE_STATE = "active";
32constexpr auto ACTIVATING_STATE = "activating";
33
Andrew Geisslerce80f242017-01-24 13:25:33 -060034/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080035const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
36 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050037 // Use the hard off target to ensure we shutdown immediately
38 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080039 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060040
Andrew Geissler58a18012018-01-19 19:36:05 -080041constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
42constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060043constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
44
Josh D. King697474c2017-03-02 11:15:55 -060045constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
46constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
47
Andrew Geissler0029a5d2017-01-24 14:48:35 -060048void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060049{
Andrew Geissler58a18012018-01-19 19:36:05 -080050 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
51 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler0029a5d2017-01-24 14:48:35 -060052 this->bus.call_noreply(method);
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060053
Andrew Geissler0029a5d2017-01-24 14:48:35 -060054 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060055}
56
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060057// TODO - Will be rewritten once sdbusplus client bindings are in place
58// and persistent storage design is in place and sdbusplus
59// has read property function
60void Chassis::determineInitialState()
61{
62 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080063 auto method = this->bus.new_method_call(
64 "org.openbmc.control.Power", "/org/openbmc/control/power0",
65 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060066
67 method.append("org.openbmc.control.Power", "pgood");
68 auto reply = this->bus.call(method);
69 reply.read(pgood);
70
Andrew Geissler58a18012018-01-19 19:36:05 -080071 if (pgood == 1)
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060072 {
73 log<level::INFO>("Initial Chassis State will be On",
74 entry("CHASSIS_CURRENT_POWER_STATE=%s",
75 convertForMessage(PowerState::On).c_str()));
76 server::Chassis::currentPowerState(PowerState::On);
77 server::Chassis::requestedPowerTransition(Transition::On);
78 }
79 else
80 {
81 log<level::INFO>("Initial Chassis State will be Off",
82 entry("CHASSIS_CURRENT_POWER_STATE=%s",
83 convertForMessage(PowerState::Off).c_str()));
84 server::Chassis::currentPowerState(PowerState::Off);
85 server::Chassis::requestedPowerTransition(Transition::Off);
86 }
87
88 return;
89}
90
Andrew Geisslerce80f242017-01-24 13:25:33 -060091void Chassis::executeTransition(Transition tranReq)
92{
93 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
94
Andrew Geissler58a18012018-01-19 19:36:05 -080095 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
96 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -060097
98 method.append(sysdTarget);
99 method.append("replace");
100
101 this->bus.call_noreply(method);
102
103 return;
104}
105
Josh D. King697474c2017-03-02 11:15:55 -0600106bool Chassis::stateActive(const std::string& target)
107{
108 sdbusplus::message::variant<std::string> currentState;
109 sdbusplus::message::object_path unitTargetPath;
110
Andrew Geissler58a18012018-01-19 19:36:05 -0800111 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
112 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600113
114 method.append(target);
115 auto result = this->bus.call(method);
116
Andrew Geissler58a18012018-01-19 19:36:05 -0800117 // Check that the bus call didn't result in an error
118 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600119 {
120 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
121 entry(" %s", SYSTEMD_INTERFACE));
122 return false;
123 }
124
125 result.read(unitTargetPath);
126
Andrew Geissler58a18012018-01-19 19:36:05 -0800127 method = this->bus.new_method_call(
128 SYSTEMD_SERVICE,
129 static_cast<const std::string&>(unitTargetPath).c_str(),
130 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600131
132 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
133 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 Get for:",
139 entry(" %s", SYSTEMD_PROPERTY_IFACE));
140 return false;
141 }
142
143 result.read(currentState);
144
Andrew Geissler58a18012018-01-19 19:36:05 -0800145 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King697474c2017-03-02 11:15:55 -0600146 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800147 // False - not active
Josh D. King697474c2017-03-02 11:15:55 -0600148 return false;
149 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800150 // True - active
Josh D. King697474c2017-03-02 11:15:55 -0600151 return true;
Josh D. King697474c2017-03-02 11:15:55 -0600152}
153
Patrick Williams8f8ba392017-05-05 15:47:39 -0500154int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600155{
Andrew Geissler58a18012018-01-19 19:36:05 -0800156 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600157 sdbusplus::message::object_path newStateObjPath;
158 std::string newStateUnit{};
159 std::string newStateResult{};
160
Andrew Geissler58a18012018-01-19 19:36:05 -0800161 // Read the msg and populate each variable
Patrick Williams8f8ba392017-05-05 15:47:39 -0500162 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600163
Andrew Geissler58a18012018-01-19 19:36:05 -0800164 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
165 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600166 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930167 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600168 this->currentPowerState(server::Chassis::PowerState::Off);
169 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800170 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
171 (newStateResult == "done") &&
172 (stateActive(CHASSIS_STATE_POWERON_TGT)))
173 {
174 log<level::INFO>("Received signal that power ON is complete");
175 this->currentPowerState(server::Chassis::PowerState::On);
176 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600177
178 return 0;
179}
180
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600181Chassis::Transition Chassis::requestedPowerTransition(Transition value)
182{
183
184 log<level::INFO>("Change to Chassis Requested Power State",
185 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
186 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600187 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600188 return server::Chassis::requestedPowerTransition(value);
189}
190
191Chassis::PowerState Chassis::currentPowerState(PowerState value)
192{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500193 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600194 log<level::INFO>("Change to Chassis Power State",
195 entry("CHASSIS_CURRENT_POWER_STATE=%s",
196 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500197
198 chassisPowerState = server::Chassis::currentPowerState(value);
199 if (chassisPowerState == PowerState::On)
200 {
201 timer->state(timer::ON);
202 }
203 else
204 {
205 timer->state(timer::OFF);
206 }
207 return chassisPowerState;
208}
209
210uint32_t Chassis::pOHCounter(uint32_t value)
211{
212 if (value != pOHCounter())
213 {
214 ChassisInherit::pOHCounter(value);
215 serialize();
216 }
217 return pOHCounter();
218}
219
220void Chassis::restorePOHCounter()
221{
222 uint32_t counter;
223 if (!deserialize(POH_COUNTER_PERSIST_PATH, counter))
224 {
225 // set to default value
226 pOHCounter(0);
227 }
228 else
229 {
230 pOHCounter(counter);
231 }
232}
233
234fs::path Chassis::serialize(const fs::path& path)
235{
236 std::ofstream os(path.c_str(), std::ios::binary);
237 cereal::JSONOutputArchive oarchive(os);
238 oarchive(pOHCounter());
239 return path;
240}
241
242bool Chassis::deserialize(const fs::path& path, uint32_t& pOHCounter)
243{
244 try
245 {
246 if (fs::exists(path))
247 {
248 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
249 cereal::JSONInputArchive iarchive(is);
250 iarchive(pOHCounter);
251 return true;
252 }
253 return false;
254 }
255 catch (cereal::Exception& e)
256 {
257 log<level::ERR>(e.what());
258 fs::remove(path);
259 return false;
260 }
261 catch (const fs::filesystem_error& e)
262 {
263 return false;
264 }
265
266 return false;
267}
268
269void Chassis::startPOHCounter()
270{
271 using namespace std::chrono_literals;
272 using namespace phosphor::logging;
273 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
274
275 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
276 fs::create_directories(dir);
277
278 sd_event* event = nullptr;
279 auto r = sd_event_default(&event);
280 if (r < 0)
281 {
282 log<level::ERR>("Error creating a default sd_event handler");
283 throw;
284 }
285
286 phosphor::state::manager::EventPtr eventP{event};
287 event = nullptr;
288
289 auto callback = [&]() {
290 if (ChassisInherit::currentPowerState() == PowerState::On)
291 {
292 pOHCounter(pOHCounter() + 1);
293 }
294 };
295
296 try
297 {
298 timer = std::make_unique<phosphor::state::manager::Timer>(
299 eventP, callback, std::chrono::seconds(POH::hour),
300 phosphor::state::manager::timer::ON);
301 bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
302 r = sd_event_loop(eventP.get());
303 if (r < 0)
304 {
305 log<level::ERR>("Error occurred during the sd_event_loop",
306 entry("RC=%d", r));
307 elog<InternalFailure>();
308 }
309 }
310 catch (InternalFailure& e)
311 {
312 phosphor::logging::commit<InternalFailure>();
313 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600314}
315
316} // namespace manager
317} // namespace state
318} // namepsace phosphor