blob: 03dd17688d353ed4c7ab3774f2f8290b59a53c06 [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");
William A. Kennington III09568ff2018-05-11 00:03:12 -070070 try
71 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070072 auto reply = this->bus.call(method);
73 if (reply.is_method_error())
74 {
75 log<level::ERR>(
76 "Error in response message - could not get initial pgood");
77 goto fail;
78 }
79
80 try
81 {
82 reply.read(pgood);
83 }
84 catch (const SdBusError& e)
85 {
86 log<level::ERR>("Error in bus response - bad encoding of pgood",
87 entry("ERROR=%s", e.what()),
88 entry("REPLY_SIG=%s", reply.get_signature()));
89 goto fail;
90 }
91
92 if (pgood == 1)
93 {
94 log<level::INFO>("Initial Chassis State will be On",
95 entry("CHASSIS_CURRENT_POWER_STATE=%s",
96 convertForMessage(PowerState::On).c_str()));
97 server::Chassis::currentPowerState(PowerState::On);
98 server::Chassis::requestedPowerTransition(Transition::On);
99 return;
100 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700101 }
102 catch (const SdBusError& e)
103 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700104 // It's acceptable for the pgood state service to not be available
105 // since it will notify us of the pgood state when it comes up.
106 if (e.name() != nullptr &&
107 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
108 {
109 goto fail;
110 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600111
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700112 // Only log for unexpected error types.
113 log<level::ERR>("Error performing call to get pgood",
114 entry("ERROR=%s", e.what()));
115 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600116 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700117
118fail:
119 log<level::INFO>("Initial Chassis State will be Off",
120 entry("CHASSIS_CURRENT_POWER_STATE=%s",
121 convertForMessage(PowerState::Off).c_str()));
122 server::Chassis::currentPowerState(PowerState::Off);
123 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600124
125 return;
126}
127
Andrew Geisslerce80f242017-01-24 13:25:33 -0600128void Chassis::executeTransition(Transition tranReq)
129{
130 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
131
Andrew Geissler58a18012018-01-19 19:36:05 -0800132 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
133 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600134
135 method.append(sysdTarget);
136 method.append("replace");
137
138 this->bus.call_noreply(method);
139
140 return;
141}
142
Josh D. King697474c2017-03-02 11:15:55 -0600143bool Chassis::stateActive(const std::string& target)
144{
145 sdbusplus::message::variant<std::string> currentState;
146 sdbusplus::message::object_path unitTargetPath;
147
Andrew Geissler58a18012018-01-19 19:36:05 -0800148 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
149 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600150
151 method.append(target);
152 auto result = this->bus.call(method);
153
Andrew Geissler58a18012018-01-19 19:36:05 -0800154 // Check that the bus call didn't result in an error
155 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600156 {
157 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
158 entry(" %s", SYSTEMD_INTERFACE));
159 return false;
160 }
161
William A. Kennington III09568ff2018-05-11 00:03:12 -0700162 try
163 {
164 result.read(unitTargetPath);
165 }
166 catch (const SdBusError& e)
167 {
168 log<level::ERR>("Error in bus response - bad encoding for GetUnit",
169 entry("ERROR=%s", e.what()),
170 entry("REPLY_SIG=%s", result.get_signature()));
171 return false;
172 }
Josh D. King697474c2017-03-02 11:15:55 -0600173
Andrew Geissler58a18012018-01-19 19:36:05 -0800174 method = this->bus.new_method_call(
175 SYSTEMD_SERVICE,
176 static_cast<const std::string&>(unitTargetPath).c_str(),
177 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600178
179 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
180 result = this->bus.call(method);
181
Andrew Geissler58a18012018-01-19 19:36:05 -0800182 // Check that the bus call didn't result in an error
183 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600184 {
185 log<level::ERR>("Error in bus call - could not resolve Get for:",
186 entry(" %s", SYSTEMD_PROPERTY_IFACE));
187 return false;
188 }
189
190 result.read(currentState);
191
Andrew Geissler58a18012018-01-19 19:36:05 -0800192 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King697474c2017-03-02 11:15:55 -0600193 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800194 // False - not active
Josh D. King697474c2017-03-02 11:15:55 -0600195 return false;
196 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800197 // True - active
Josh D. King697474c2017-03-02 11:15:55 -0600198 return true;
Josh D. King697474c2017-03-02 11:15:55 -0600199}
200
Patrick Williams8f8ba392017-05-05 15:47:39 -0500201int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600202{
Andrew Geissler58a18012018-01-19 19:36:05 -0800203 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600204 sdbusplus::message::object_path newStateObjPath;
205 std::string newStateUnit{};
206 std::string newStateResult{};
207
Andrew Geissler58a18012018-01-19 19:36:05 -0800208 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700209 try
210 {
211 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
212 }
213 catch (const SdBusError& e)
214 {
215 log<level::ERR>("Error in state change - bad encoding",
216 entry("ERROR=%s", e.what()),
217 entry("REPLY_SIG=%s", msg.get_signature()));
218 return 0;
219 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600220
Andrew Geissler58a18012018-01-19 19:36:05 -0800221 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
222 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600223 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930224 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600225 this->currentPowerState(server::Chassis::PowerState::Off);
226 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800227 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
228 (newStateResult == "done") &&
229 (stateActive(CHASSIS_STATE_POWERON_TGT)))
230 {
231 log<level::INFO>("Received signal that power ON is complete");
232 this->currentPowerState(server::Chassis::PowerState::On);
233 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600234
235 return 0;
236}
237
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600238Chassis::Transition Chassis::requestedPowerTransition(Transition value)
239{
240
241 log<level::INFO>("Change to Chassis Requested Power State",
242 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
243 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600244 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600245 return server::Chassis::requestedPowerTransition(value);
246}
247
248Chassis::PowerState Chassis::currentPowerState(PowerState value)
249{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500250 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600251 log<level::INFO>("Change to Chassis Power State",
252 entry("CHASSIS_CURRENT_POWER_STATE=%s",
253 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500254
255 chassisPowerState = server::Chassis::currentPowerState(value);
256 if (chassisPowerState == PowerState::On)
257 {
258 timer->state(timer::ON);
259 }
260 else
261 {
262 timer->state(timer::OFF);
263 }
264 return chassisPowerState;
265}
266
267uint32_t Chassis::pOHCounter(uint32_t value)
268{
269 if (value != pOHCounter())
270 {
271 ChassisInherit::pOHCounter(value);
272 serialize();
273 }
274 return pOHCounter();
275}
276
277void Chassis::restorePOHCounter()
278{
279 uint32_t counter;
280 if (!deserialize(POH_COUNTER_PERSIST_PATH, counter))
281 {
282 // set to default value
283 pOHCounter(0);
284 }
285 else
286 {
287 pOHCounter(counter);
288 }
289}
290
291fs::path Chassis::serialize(const fs::path& path)
292{
293 std::ofstream os(path.c_str(), std::ios::binary);
294 cereal::JSONOutputArchive oarchive(os);
295 oarchive(pOHCounter());
296 return path;
297}
298
299bool Chassis::deserialize(const fs::path& path, uint32_t& pOHCounter)
300{
301 try
302 {
303 if (fs::exists(path))
304 {
305 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
306 cereal::JSONInputArchive iarchive(is);
307 iarchive(pOHCounter);
308 return true;
309 }
310 return false;
311 }
312 catch (cereal::Exception& e)
313 {
314 log<level::ERR>(e.what());
315 fs::remove(path);
316 return false;
317 }
318 catch (const fs::filesystem_error& e)
319 {
320 return false;
321 }
322
323 return false;
324}
325
326void Chassis::startPOHCounter()
327{
328 using namespace std::chrono_literals;
329 using namespace phosphor::logging;
330 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
331
332 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
333 fs::create_directories(dir);
334
335 sd_event* event = nullptr;
336 auto r = sd_event_default(&event);
337 if (r < 0)
338 {
339 log<level::ERR>("Error creating a default sd_event handler");
340 throw;
341 }
342
343 phosphor::state::manager::EventPtr eventP{event};
344 event = nullptr;
345
346 auto callback = [&]() {
347 if (ChassisInherit::currentPowerState() == PowerState::On)
348 {
349 pOHCounter(pOHCounter() + 1);
350 }
351 };
352
353 try
354 {
355 timer = std::make_unique<phosphor::state::manager::Timer>(
356 eventP, callback, std::chrono::seconds(POH::hour),
357 phosphor::state::manager::timer::ON);
358 bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
359 r = sd_event_loop(eventP.get());
360 if (r < 0)
361 {
362 log<level::ERR>("Error occurred during the sd_event_loop",
363 entry("RC=%d", r));
364 elog<InternalFailure>();
365 }
366 }
367 catch (InternalFailure& e)
368 {
369 phosphor::logging::commit<InternalFailure>();
370 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600371}
372
373} // namespace manager
374} // namespace state
375} // namepsace phosphor