blob: ab60030700daaa9afe2bddbe7f1d2ced11ecc542 [file] [log] [blame]
Andrew Geissler36529022016-11-29 15:23:54 -06001#include <iostream>
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -06002#include <map>
3#include <string>
Andrew Geissleref3c1842016-12-01 12:33:09 -06004#include <systemd/sd-bus.h>
Andrew Geissler033fc3b2017-08-30 15:11:44 -05005#include <cereal/cereal.hpp>
6#include <cereal/types/string.hpp>
7#include <cereal/types/vector.hpp>
8#include <cereal/types/tuple.hpp>
9#include <cereal/archives/json.hpp>
10#include <fstream>
Michael Tritz206a8332017-02-06 16:01:23 -060011#include <sdbusplus/server.hpp>
Saqib Khana8006a22017-02-14 11:37:08 -060012#include <phosphor-logging/log.hpp>
Deepak Kodihalli55f132b2017-07-25 07:36:06 -050013#include <phosphor-logging/elog-errors.hpp>
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050014#include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
Deepak Kodihalli55f132b2017-07-25 07:36:06 -050015#include <xyz/openbmc_project/Common/error.hpp>
Andrew Geissler36529022016-11-29 15:23:54 -060016#include "host_state_manager.hpp"
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050017#include "config.h"
18
Andrew Geissler36529022016-11-29 15:23:54 -060019
20namespace phosphor
21{
22namespace state
23{
24namespace manager
25{
26
Andrew Geissler7b90a622017-08-08 11:41:08 -050027// When you see server:: or reboot:: you know we're referencing our base class
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060028namespace server = sdbusplus::xyz::openbmc_project::State::server;
Andrew Geissler7b90a622017-08-08 11:41:08 -050029namespace reboot = sdbusplus::xyz::openbmc_project::Control::Boot::server;
Dhruvaraj Subhashchandrana3b8d7e2017-08-10 05:40:04 -050030namespace bootprogress = sdbusplus::xyz::openbmc_project::State::Boot::server;
31namespace osstatus =
32 sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060033using namespace phosphor::logging;
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050034namespace fs = std::experimental::filesystem;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060035
Andrew Geissler4f309e82017-05-24 15:18:01 -050036// host-shutdown notifies host of shutdown and that leads to host-stop being
37// called so initiate a host shutdown with the -shutdown target and consider the
38// host shut down when the -stop target is complete
39constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target";
Josh D. Kingca357922017-04-11 13:44:09 -050040constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
41constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Andrew Geisslera27a6e82017-07-27 16:44:43 -050042constexpr auto HOST_STATE_REBOOT_TGT = "obmc-host-reboot@0.target";
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050043constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060044
Josh D. King929ef702017-03-02 10:58:11 -060045constexpr auto ACTIVE_STATE = "active";
46constexpr auto ACTIVATING_STATE = "activating";
47
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060048/* Map a transition to it's systemd target */
49const std::map<server::Host::Transition,std::string> SYSTEMD_TARGET_TABLE =
50{
Andrew Geissler4f309e82017-05-24 15:18:01 -050051 {server::Host::Transition::Off, HOST_STATE_SOFT_POWEROFF_TGT},
Andrew Geisslera27a6e82017-07-27 16:44:43 -050052 {server::Host::Transition::On, HOST_STATE_POWERON_TGT},
53 {server::Host::Transition::Reboot, HOST_STATE_REBOOT_TGT}
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060054};
55
56constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
57constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
58constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
59
Michael Tritz206a8332017-02-06 16:01:23 -060060constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
Leonel Gonzalezf318d872017-03-16 13:47:49 -050061constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
Michael Tritz206a8332017-02-06 16:01:23 -060062constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
63
Josh D. King929ef702017-03-02 10:58:11 -060064constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
65constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
66
Andrew Geissleref621162016-12-08 12:56:21 -060067/* Map a system state to the HostState */
Andrew Geissleref621162016-12-08 12:56:21 -060068const std::map<std::string, server::Host::HostState> SYS_HOST_STATE_TABLE = {
69 {"HOST_BOOTING", server::Host::HostState::Running},
Saqib Khanadaa7212017-02-23 13:19:28 -060070 {"HOST_POWERED_OFF", server::Host::HostState::Off},
71 {"HOST_QUIESCED", server::Host::HostState::Quiesced}
Andrew Geissleref621162016-12-08 12:56:21 -060072};
73
Andrew Geissler4da7e002017-01-24 15:21:40 -060074void Host::subscribeToSystemdSignals()
75{
76 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
77 SYSTEMD_OBJ_PATH,
78 SYSTEMD_INTERFACE,
79 "Subscribe");
80 this->bus.call_noreply(method);
81
82 return;
83}
84
Andrew Geissleref3c1842016-12-01 12:33:09 -060085void Host::determineInitialState()
86{
Andrew Geissleref3c1842016-12-01 12:33:09 -060087
Josh D. King222014e2017-05-16 13:35:21 -050088 if(stateActive(HOST_STATE_POWERON_TGT))
Andrew Geissleref3c1842016-12-01 12:33:09 -060089 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060090 log<level::INFO>("Initial Host State will be Running",
91 entry("CURRENT_HOST_STATE=%s",
92 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060093 server::Host::currentHostState(HostState::Running);
94 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -060095 }
96 else
97 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060098 log<level::INFO>("Initial Host State will be Off",
99 entry("CURRENT_HOST_STATE=%s",
100 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600101 server::Host::currentHostState(HostState::Off);
102 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600103 }
104
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500105 if (!deserialize(HOST_STATE_PERSIST_PATH))
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500106 {
107 //set to default value.
108 server::Host::requestedHostTransition(Transition::Off);
109 }
Andrew Geissleref3c1842016-12-01 12:33:09 -0600110
111 return;
112}
113
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600114void Host::executeTransition(Transition tranReq)
115{
116 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
117
118 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
119 SYSTEMD_OBJ_PATH,
120 SYSTEMD_INTERFACE,
121 "StartUnit");
122
123 method.append(sysdUnit);
124 method.append("replace");
125
Andrew Geissler4da7e002017-01-24 15:21:40 -0600126 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600127
128 return;
129}
130
Josh D. King929ef702017-03-02 10:58:11 -0600131bool Host::stateActive(const std::string& target)
132{
133 sdbusplus::message::variant<std::string> currentState;
134 sdbusplus::message::object_path unitTargetPath;
135
136 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
137 SYSTEMD_OBJ_PATH,
138 SYSTEMD_INTERFACE,
139 "GetUnit");
140
141 method.append(target);
142 auto result = this->bus.call(method);
143
144 //Check that the bus call didn't result in an error
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500145 if (result.is_method_error())
Josh D. King929ef702017-03-02 10:58:11 -0600146 {
147 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
148 entry(" %s", SYSTEMD_INTERFACE));
149 return false;
150 }
151
152 result.read(unitTargetPath);
153
154 method = this->bus.new_method_call(SYSTEMD_SERVICE,
155 static_cast<const std::string&>
156 (unitTargetPath).c_str(),
157 SYSTEMD_PROPERTY_IFACE,
158 "Get");
159
160 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
161 result = this->bus.call(method);
162
163 //Check that the bus call didn't result in an error
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500164 if (result.is_method_error())
Josh D. King929ef702017-03-02 10:58:11 -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
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500173 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King929ef702017-03-02 10:58:11 -0600174 {
175 //False - not active
176 return false;
177 }
178 //True - active
179 return true;
180}
181
Michael Tritz206a8332017-02-06 16:01:23 -0600182bool Host::isAutoReboot()
183{
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500184 using namespace settings;
Michael Tritz206a8332017-02-06 16:01:23 -0600185
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500186 auto method =
187 bus.new_method_call(
188 settings.service(settings.autoReboot, autoRebootIntf).c_str(),
189 settings.autoReboot.c_str(),
190 "org.freedesktop.DBus.Properties",
191 "Get");
192 method.append(autoRebootIntf, "AutoReboot");
193 auto reply = bus.call(method);
Michael Tritz206a8332017-02-06 16:01:23 -0600194 if (reply.is_method_error())
195 {
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500196 log<level::ERR>("Error in AutoReboot Get");
Michael Tritz206a8332017-02-06 16:01:23 -0600197 return false;
198 }
199
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500200 sdbusplus::message::variant<bool> result;
201 reply.read(result);
202 auto autoReboot = result.get<bool>();
Andrew Geissler7b90a622017-08-08 11:41:08 -0500203 auto rebootCounterParam = reboot::RebootAttempts::attemptsLeft();
Saqib Khancbe08d12017-03-10 01:29:20 -0600204
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500205 if (autoReboot)
Michael Tritz206a8332017-02-06 16:01:23 -0600206 {
Dhruvaraj Subhashchandran2710e732017-06-19 06:43:22 -0500207 if (rebootCounterParam > 0)
Saqib Khancbe08d12017-03-10 01:29:20 -0600208 {
209 // Reduce BOOTCOUNT by 1
Andrew Geissler7b90a622017-08-08 11:41:08 -0500210 log<level::INFO>("Auto reboot enabled, rebooting");
Saqib Khancbe08d12017-03-10 01:29:20 -0600211 return true;
212 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500213 else if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600214 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600215 // Reset reboot counter and go to quiesce state
Saqib Khand5ac6352017-04-04 09:53:59 -0500216 log<level::INFO>("Auto reboot enabled. "
217 "HOST BOOTCOUNT already set to 0.");
Dhruvaraj Subhashchandran2710e732017-06-19 06:43:22 -0500218 attemptsLeft(BOOT_COUNT_MAX_ALLOWED);
Saqib Khand5ac6352017-04-04 09:53:59 -0500219 return false;
220 }
221 else
222 {
223 log<level::INFO>("Auto reboot enabled. "
224 "HOST BOOTCOUNT has an invalid value.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600225 return false;
226 }
Michael Tritz206a8332017-02-06 16:01:23 -0600227 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500228 else
229 {
230 log<level::INFO>("Auto reboot disabled.");
231 return false;
232 }
Michael Tritz206a8332017-02-06 16:01:23 -0600233}
234
Patrick Williamsd22706f2017-05-04 05:42:49 -0500235void Host::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600236{
237 uint32_t newStateID {};
238 sdbusplus::message::object_path newStateObjPath;
239 std::string newStateUnit{};
240 std::string newStateResult{};
241
Andrew Geissler4da7e002017-01-24 15:21:40 -0600242 //Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500243 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600244
245 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600246 (newStateResult == "done") &&
247 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600248 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930249 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600250 this->currentHostState(server::Host::HostState::Off);
Dhruvaraj Subhashchandrana3b8d7e2017-08-10 05:40:04 -0500251 this->bootProgress(bootprogress::Progress::ProgressStages::Unspecified);
252 this->operatingSystemState(osstatus::Status::OSStatus::Inactive);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600253
Andrew Geissleref621162016-12-08 12:56:21 -0600254 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600255 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600256 (newStateResult == "done") &&
257 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600258 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930259 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600260 this->currentHostState(server::Host::HostState::Running);
261 }
Michael Tritz206a8332017-02-06 16:01:23 -0600262 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500263 (newStateResult == "done") &&
264 (stateActive(HOST_STATE_QUIESCE_TGT)))
Michael Tritz206a8332017-02-06 16:01:23 -0600265 {
266 if (Host::isAutoReboot())
267 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500268 log<level::INFO>("Beginning reboot...");
Michael Tritz206a8332017-02-06 16:01:23 -0600269 Host::requestedHostTransition(server::Host::Transition::Reboot);
270 }
271 else
272 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500273 log<level::INFO>("Maintaining quiesce");
Saqib Khanadaa7212017-02-23 13:19:28 -0600274 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600275 }
276
277 }
Andrew Geissleref621162016-12-08 12:56:21 -0600278}
279
Andrew Geissler7b90a622017-08-08 11:41:08 -0500280uint32_t Host::decrementRebootCount()
281{
282 auto rebootCount = reboot::RebootAttempts::attemptsLeft();
283 if(rebootCount > 0)
284 {
285 return(reboot::RebootAttempts::attemptsLeft(rebootCount - 1));
286 }
287 return rebootCount;
288}
289
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500290fs::path Host::serialize(const fs::path& dir)
291{
292 std::ofstream os(dir.c_str(), std::ios::binary);
293 cereal::JSONOutputArchive oarchive(os);
294 oarchive(*this);
295 return dir;
296}
297
298bool Host::deserialize(const fs::path& path)
299{
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500300 try
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500301 {
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500302 if (fs::exists(path))
303 {
304 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
305 cereal::JSONInputArchive iarchive(is);
306 iarchive(*this);
307 return true;
308 }
309 return false;
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500310 }
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500311 catch(cereal::Exception& e)
312 {
313 log<level::ERR>(e.what());
314 fs::remove(path);
315 return false;
316 }
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500317}
318
Andrew Geissleref3c1842016-12-01 12:33:09 -0600319Host::Transition Host::requestedHostTransition(Transition value)
320{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600321 log<level::INFO>(
322 "Host State transaction request",
323 entry("REQUESTED_HOST_TRANSITION=%s",
324 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600325
Andrew Geissler7b90a622017-08-08 11:41:08 -0500326 // If this is not a power off request then we need to
327 // decrement the reboot counter. This code should
328 // never prevent a power on, it should just decrement
329 // the count to 0. The quiesce handling is where the
330 // check of this count will occur
331 if(value != server::Host::Transition::Off)
332 {
333 decrementRebootCount();
334 }
335
Andrew Geisslera27a6e82017-07-27 16:44:43 -0500336 executeTransition(value);
Andrew Geissler7b90a622017-08-08 11:41:08 -0500337
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500338 auto retVal = server::Host::requestedHostTransition(value);
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500339 serialize();
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500340 return retVal;
Andrew Geissleref3c1842016-12-01 12:33:09 -0600341}
342
Andrew Geissleref3c1842016-12-01 12:33:09 -0600343Host::HostState Host::currentHostState(HostState value)
344{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600345 log<level::INFO>("Change to Host State",
346 entry("CURRENT_HOST_STATE=%s",
347 convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600348 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600349}
350
Andrew Geissler36529022016-11-29 15:23:54 -0600351} // namespace manager
352} // namespace state
353} // namepsace phosphor