blob: a723d8f9634706107b3ae1679335bad3167134ae [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
Andrew Geisslera90a31a2016-12-13 16:16:28 -060012namespace phosphor
13{
14namespace state
15{
16namespace manager
17{
18
19// When you see server:: you know we're referencing our base class
20namespace server = sdbusplus::xyz::openbmc_project::State::server;
21
22using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070023using sdbusplus::exception::SdBusError;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060024
Josh D. King6838ea92017-04-11 13:39:18 -050025constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080026constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
27 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050028constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060029
Josh D. King697474c2017-03-02 11:15:55 -060030constexpr auto ACTIVE_STATE = "active";
31constexpr auto ACTIVATING_STATE = "activating";
32
Andrew Geisslerce80f242017-01-24 13:25:33 -060033/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080034const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
35 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050036 // Use the hard off target to ensure we shutdown immediately
37 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080038 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060039
Andrew Geissler58a18012018-01-19 19:36:05 -080040constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
41constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060042constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
43
Josh D. King697474c2017-03-02 11:15:55 -060044constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
45constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
46
Andrew Geissler0029a5d2017-01-24 14:48:35 -060047void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060048{
Andrew Geissler58a18012018-01-19 19:36:05 -080049 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
50 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler0029a5d2017-01-24 14:48:35 -060051 this->bus.call_noreply(method);
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060052
Andrew Geissler0029a5d2017-01-24 14:48:35 -060053 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060054}
55
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060056// TODO - Will be rewritten once sdbusplus client bindings are in place
57// and persistent storage design is in place and sdbusplus
58// has read property function
59void Chassis::determineInitialState()
60{
61 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080062 auto method = this->bus.new_method_call(
63 "org.openbmc.control.Power", "/org/openbmc/control/power0",
64 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060065
66 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070067 try
68 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070069 auto reply = this->bus.call(method);
70 if (reply.is_method_error())
71 {
72 log<level::ERR>(
73 "Error in response message - 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 }
88
89 if (pgood == 1)
90 {
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);
96 return;
97 }
William A. Kennington III09568ff2018-05-11 00:03:12 -070098 }
99 catch (const SdBusError& e)
100 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700101 // It's acceptable for the pgood state service to not be available
102 // since it will notify us of the pgood state when it comes up.
103 if (e.name() != nullptr &&
104 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
105 {
106 goto fail;
107 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600108
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700109 // Only log for unexpected error types.
110 log<level::ERR>("Error performing call to get pgood",
111 entry("ERROR=%s", e.what()));
112 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600113 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700114
115fail:
116 log<level::INFO>("Initial Chassis State will be Off",
117 entry("CHASSIS_CURRENT_POWER_STATE=%s",
118 convertForMessage(PowerState::Off).c_str()));
119 server::Chassis::currentPowerState(PowerState::Off);
120 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600121
122 return;
123}
124
Andrew Geisslerce80f242017-01-24 13:25:33 -0600125void Chassis::executeTransition(Transition tranReq)
126{
127 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
128
Andrew Geissler58a18012018-01-19 19:36:05 -0800129 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
130 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600131
132 method.append(sysdTarget);
133 method.append("replace");
134
135 this->bus.call_noreply(method);
136
137 return;
138}
139
Josh D. King697474c2017-03-02 11:15:55 -0600140bool Chassis::stateActive(const std::string& target)
141{
142 sdbusplus::message::variant<std::string> currentState;
143 sdbusplus::message::object_path unitTargetPath;
144
Andrew Geissler58a18012018-01-19 19:36:05 -0800145 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
146 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600147
148 method.append(target);
149 auto result = this->bus.call(method);
150
Andrew Geissler58a18012018-01-19 19:36:05 -0800151 // Check that the bus call didn't result in an error
152 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600153 {
154 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
155 entry(" %s", SYSTEMD_INTERFACE));
156 return false;
157 }
158
William A. Kennington III09568ff2018-05-11 00:03:12 -0700159 try
160 {
161 result.read(unitTargetPath);
162 }
163 catch (const SdBusError& e)
164 {
165 log<level::ERR>("Error in bus response - bad encoding for GetUnit",
166 entry("ERROR=%s", e.what()),
167 entry("REPLY_SIG=%s", result.get_signature()));
168 return false;
169 }
Josh D. King697474c2017-03-02 11:15:55 -0600170
Andrew Geissler58a18012018-01-19 19:36:05 -0800171 method = this->bus.new_method_call(
172 SYSTEMD_SERVICE,
173 static_cast<const std::string&>(unitTargetPath).c_str(),
174 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600175
176 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
177 result = this->bus.call(method);
178
Andrew Geissler58a18012018-01-19 19:36:05 -0800179 // Check that the bus call didn't result in an error
180 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600181 {
182 log<level::ERR>("Error in bus call - could not resolve Get for:",
183 entry(" %s", SYSTEMD_PROPERTY_IFACE));
184 return false;
185 }
186
187 result.read(currentState);
188
Andrew Geissler58a18012018-01-19 19:36:05 -0800189 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King697474c2017-03-02 11:15:55 -0600190 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800191 // False - not active
Josh D. King697474c2017-03-02 11:15:55 -0600192 return false;
193 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800194 // True - active
Josh D. King697474c2017-03-02 11:15:55 -0600195 return true;
Josh D. King697474c2017-03-02 11:15:55 -0600196}
197
Patrick Williams8f8ba392017-05-05 15:47:39 -0500198int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600199{
Andrew Geissler58a18012018-01-19 19:36:05 -0800200 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600201 sdbusplus::message::object_path newStateObjPath;
202 std::string newStateUnit{};
203 std::string newStateResult{};
204
Andrew Geissler58a18012018-01-19 19:36:05 -0800205 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700206 try
207 {
208 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
209 }
210 catch (const SdBusError& e)
211 {
212 log<level::ERR>("Error in state change - bad encoding",
213 entry("ERROR=%s", e.what()),
214 entry("REPLY_SIG=%s", msg.get_signature()));
215 return 0;
216 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600217
Andrew Geissler58a18012018-01-19 19:36:05 -0800218 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
219 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600220 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930221 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600222 this->currentPowerState(server::Chassis::PowerState::Off);
223 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800224 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
225 (newStateResult == "done") &&
226 (stateActive(CHASSIS_STATE_POWERON_TGT)))
227 {
228 log<level::INFO>("Received signal that power ON is complete");
229 this->currentPowerState(server::Chassis::PowerState::On);
230 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600231
232 return 0;
233}
234
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600235Chassis::Transition Chassis::requestedPowerTransition(Transition value)
236{
237
238 log<level::INFO>("Change to Chassis Requested Power State",
239 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
240 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600241 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600242 return server::Chassis::requestedPowerTransition(value);
243}
244
245Chassis::PowerState Chassis::currentPowerState(PowerState value)
246{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500247 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600248 log<level::INFO>("Change to Chassis Power State",
249 entry("CHASSIS_CURRENT_POWER_STATE=%s",
250 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500251
252 chassisPowerState = server::Chassis::currentPowerState(value);
253 if (chassisPowerState == PowerState::On)
254 {
255 timer->state(timer::ON);
256 }
257 else
258 {
259 timer->state(timer::OFF);
260 }
261 return chassisPowerState;
262}
263
264uint32_t Chassis::pOHCounter(uint32_t value)
265{
266 if (value != pOHCounter())
267 {
268 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500269 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500270 }
271 return pOHCounter();
272}
273
274void Chassis::restorePOHCounter()
275{
276 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500277 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500278 {
279 // set to default value
280 pOHCounter(0);
281 }
282 else
283 {
284 pOHCounter(counter);
285 }
286}
287
Matt Spinler81957842018-07-11 10:37:12 -0500288fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500289{
290 std::ofstream os(path.c_str(), std::ios::binary);
291 cereal::JSONOutputArchive oarchive(os);
292 oarchive(pOHCounter());
293 return path;
294}
295
Matt Spinler81957842018-07-11 10:37:12 -0500296bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500297{
298 try
299 {
300 if (fs::exists(path))
301 {
302 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
303 cereal::JSONInputArchive iarchive(is);
304 iarchive(pOHCounter);
305 return true;
306 }
307 return false;
308 }
309 catch (cereal::Exception& e)
310 {
311 log<level::ERR>(e.what());
312 fs::remove(path);
313 return false;
314 }
315 catch (const fs::filesystem_error& e)
316 {
317 return false;
318 }
319
320 return false;
321}
322
323void Chassis::startPOHCounter()
324{
325 using namespace std::chrono_literals;
326 using namespace phosphor::logging;
327 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
328
329 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
330 fs::create_directories(dir);
331
332 sd_event* event = nullptr;
333 auto r = sd_event_default(&event);
334 if (r < 0)
335 {
336 log<level::ERR>("Error creating a default sd_event handler");
337 throw;
338 }
339
340 phosphor::state::manager::EventPtr eventP{event};
341 event = nullptr;
342
343 auto callback = [&]() {
344 if (ChassisInherit::currentPowerState() == PowerState::On)
345 {
346 pOHCounter(pOHCounter() + 1);
347 }
348 };
349
350 try
351 {
352 timer = std::make_unique<phosphor::state::manager::Timer>(
353 eventP, callback, std::chrono::seconds(POH::hour),
354 phosphor::state::manager::timer::ON);
355 bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
356 r = sd_event_loop(eventP.get());
357 if (r < 0)
358 {
359 log<level::ERR>("Error occurred during the sd_event_loop",
360 entry("RC=%d", r));
361 elog<InternalFailure>();
362 }
363 }
364 catch (InternalFailure& e)
365 {
366 phosphor::logging::commit<InternalFailure>();
367 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600368}
369
370} // namespace manager
371} // namespace state
372} // namepsace phosphor