blob: 7d661dd2c55e2a195b90484b5cd93170a06dc13b [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>
Anthony Wilson32c532e2018-10-25 21:56:07 -050011#include <sdbusplus/exception.hpp>
Michael Tritz206a8332017-02-06 16:01:23 -060012#include <sdbusplus/server.hpp>
Saqib Khana8006a22017-02-14 11:37:08 -060013#include <phosphor-logging/log.hpp>
Deepak Kodihalli55f132b2017-07-25 07:36:06 -050014#include <phosphor-logging/elog-errors.hpp>
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050015#include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
Deepak Kodihalli55f132b2017-07-25 07:36:06 -050016#include <xyz/openbmc_project/Common/error.hpp>
Andrew Geissler36529022016-11-29 15:23:54 -060017#include "host_state_manager.hpp"
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050018#include "config.h"
19
Vishwanatha Subbanna0a838732017-10-05 12:43:19 +053020// Register class version with Cereal
21CEREAL_CLASS_VERSION(phosphor::state::manager::Host, CLASS_VERSION);
Andrew Geissler36529022016-11-29 15:23:54 -060022
23namespace phosphor
24{
25namespace state
26{
27namespace manager
28{
29
Andrew Geissler7b90a622017-08-08 11:41:08 -050030// When you see server:: or reboot:: you know we're referencing our base class
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060031namespace server = sdbusplus::xyz::openbmc_project::State::server;
Andrew Geissler7b90a622017-08-08 11:41:08 -050032namespace reboot = sdbusplus::xyz::openbmc_project::Control::Boot::server;
Dhruvaraj Subhashchandrana3b8d7e2017-08-10 05:40:04 -050033namespace bootprogress = sdbusplus::xyz::openbmc_project::State::Boot::server;
34namespace osstatus =
35 sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060036using namespace phosphor::logging;
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050037namespace fs = std::experimental::filesystem;
Anthony Wilson32c532e2018-10-25 21:56:07 -050038using sdbusplus::exception::SdBusError;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060039
Andrew Geissler4f309e82017-05-24 15:18:01 -050040// host-shutdown notifies host of shutdown and that leads to host-stop being
41// called so initiate a host shutdown with the -shutdown target and consider the
42// host shut down when the -stop target is complete
43constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target";
Josh D. Kingca357922017-04-11 13:44:09 -050044constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
45constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Andrew Geissler969b2612018-03-29 10:16:51 -070046constexpr auto HOST_STATE_POWERON_MIN_TGT = "obmc-host-startmin@0.target";
Andrew Geisslera27a6e82017-07-27 16:44:43 -050047constexpr auto HOST_STATE_REBOOT_TGT = "obmc-host-reboot@0.target";
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050048constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060049
Josh D. King929ef702017-03-02 10:58:11 -060050constexpr auto ACTIVE_STATE = "active";
51constexpr auto ACTIVATING_STATE = "activating";
52
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060053/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080054const std::map<server::Host::Transition, std::string> SYSTEMD_TARGET_TABLE = {
55 {server::Host::Transition::Off, HOST_STATE_SOFT_POWEROFF_TGT},
56 {server::Host::Transition::On, HOST_STATE_POWERON_TGT},
57 {server::Host::Transition::Reboot, HOST_STATE_REBOOT_TGT}};
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060058
Andrew Geissler58a18012018-01-19 19:36:05 -080059constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
60constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060061constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
62
Michael Tritz206a8332017-02-06 16:01:23 -060063constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
Leonel Gonzalezf318d872017-03-16 13:47:49 -050064constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
Michael Tritz206a8332017-02-06 16:01:23 -060065constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
66
Josh D. King929ef702017-03-02 10:58:11 -060067constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
68constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
69
Andrew Geissleref621162016-12-08 12:56:21 -060070/* Map a system state to the HostState */
Andrew Geissleref621162016-12-08 12:56:21 -060071const std::map<std::string, server::Host::HostState> SYS_HOST_STATE_TABLE = {
Andrew Geissler58a18012018-01-19 19:36:05 -080072 {"HOST_BOOTING", server::Host::HostState::Running},
73 {"HOST_POWERED_OFF", server::Host::HostState::Off},
74 {"HOST_QUIESCED", server::Host::HostState::Quiesced}};
Andrew Geissleref621162016-12-08 12:56:21 -060075
Andrew Geissler4da7e002017-01-24 15:21:40 -060076void Host::subscribeToSystemdSignals()
77{
Andrew Geissler58a18012018-01-19 19:36:05 -080078 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
79 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler4da7e002017-01-24 15:21:40 -060080 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
Andrew Geissler969b2612018-03-29 10:16:51 -070088 if (stateActive(HOST_STATE_POWERON_MIN_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 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800107 // set to default value.
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500108 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
Andrew Geissler58a18012018-01-19 19:36:05 -0800118 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
119 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600120
121 method.append(sysdUnit);
122 method.append("replace");
123
Andrew Geissler4da7e002017-01-24 15:21:40 -0600124 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600125
126 return;
127}
128
Josh D. King929ef702017-03-02 10:58:11 -0600129bool Host::stateActive(const std::string& target)
130{
131 sdbusplus::message::variant<std::string> currentState;
132 sdbusplus::message::object_path unitTargetPath;
133
Andrew Geissler58a18012018-01-19 19:36:05 -0800134 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
135 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King929ef702017-03-02 10:58:11 -0600136
137 method.append(target);
Josh D. King929ef702017-03-02 10:58:11 -0600138
Anthony Wilson32c532e2018-10-25 21:56:07 -0500139 try
Josh D. King929ef702017-03-02 10:58:11 -0600140 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500141 auto result = this->bus.call(method);
142 result.read(unitTargetPath);
143 }
144 catch (const SdBusError& e)
145 {
146 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
Josh D. King929ef702017-03-02 10:58:11 -0600147 return false;
148 }
149
Andrew Geissler58a18012018-01-19 19:36:05 -0800150 method = this->bus.new_method_call(
151 SYSTEMD_SERVICE,
152 static_cast<const std::string&>(unitTargetPath).c_str(),
153 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King929ef702017-03-02 10:58:11 -0600154
155 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King929ef702017-03-02 10:58:11 -0600156
Anthony Wilson32c532e2018-10-25 21:56:07 -0500157 try
Josh D. King929ef702017-03-02 10:58:11 -0600158 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500159 auto result = this->bus.call(method);
160 result.read(currentState);
161 }
162 catch (const SdBusError& e)
163 {
164 log<level::ERR>("Error in ActiveState Get",
165 entry("ERROR=%s", e.what()));
Josh D. King929ef702017-03-02 10:58:11 -0600166 return false;
167 }
168
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800169 const auto& currentStateStr =
170 sdbusplus::message::variant_ns::get<std::string>(currentState);
171 return currentStateStr == ACTIVE_STATE ||
172 currentStateStr == ACTIVATING_STATE;
Josh D. King929ef702017-03-02 10:58:11 -0600173}
174
Michael Tritz206a8332017-02-06 16:01:23 -0600175bool Host::isAutoReboot()
176{
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500177 using namespace settings;
Michael Tritz206a8332017-02-06 16:01:23 -0600178
Andrew Geissler58a18012018-01-19 19:36:05 -0800179 auto method = bus.new_method_call(
180 settings.service(settings.autoReboot, autoRebootIntf).c_str(),
181 settings.autoReboot.c_str(), "org.freedesktop.DBus.Properties", "Get");
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500182 method.append(autoRebootIntf, "AutoReboot");
Michael Tritz206a8332017-02-06 16:01:23 -0600183
Anthony Wilson32c532e2018-10-25 21:56:07 -0500184 try
Michael Tritz206a8332017-02-06 16:01:23 -0600185 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500186 auto reply = bus.call(method);
187
188 sdbusplus::message::variant<bool> result;
189 reply.read(result);
190 auto autoReboot = sdbusplus::message::variant_ns::get<bool>(result);
191 auto rebootCounterParam = reboot::RebootAttempts::attemptsLeft();
192
193 if (autoReboot)
Saqib Khancbe08d12017-03-10 01:29:20 -0600194 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500195 if (rebootCounterParam > 0)
196 {
197 // Reduce BOOTCOUNT by 1
198 log<level::INFO>("Auto reboot enabled, rebooting");
199 return true;
200 }
201 else if (rebootCounterParam == 0)
202 {
203 // Reset reboot counter and go to quiesce state
204 log<level::INFO>("Auto reboot enabled. "
205 "HOST BOOTCOUNT already set to 0.");
206 attemptsLeft(BOOT_COUNT_MAX_ALLOWED);
207 return false;
208 }
209 else
210 {
211 log<level::INFO>("Auto reboot enabled. "
212 "HOST BOOTCOUNT has an invalid value.");
213 return false;
214 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500215 }
216 else
217 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500218 log<level::INFO>("Auto reboot disabled.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600219 return false;
220 }
Michael Tritz206a8332017-02-06 16:01:23 -0600221 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500222 catch (const SdBusError& e)
Saqib Khand5ac6352017-04-04 09:53:59 -0500223 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500224 log<level::ERR>("Error in AutoReboot Get", entry("ERROR=%s", e.what()));
Saqib Khand5ac6352017-04-04 09:53:59 -0500225 return false;
226 }
Michael Tritz206a8332017-02-06 16:01:23 -0600227}
228
Patrick Williamsd22706f2017-05-04 05:42:49 -0500229void Host::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600230{
Andrew Geissler58a18012018-01-19 19:36:05 -0800231 uint32_t newStateID{};
Andrew Geissler4da7e002017-01-24 15:21:40 -0600232 sdbusplus::message::object_path newStateObjPath;
233 std::string newStateUnit{};
234 std::string newStateResult{};
235
Andrew Geissler58a18012018-01-19 19:36:05 -0800236 // Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500237 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600238
Andrew Geissler58a18012018-01-19 19:36:05 -0800239 if ((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Andrew Geissler969b2612018-03-29 10:16:51 -0700240 (newStateResult == "done") &&
241 (!stateActive(HOST_STATE_POWERON_MIN_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600242 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930243 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600244 this->currentHostState(server::Host::HostState::Off);
Dhruvaraj Subhashchandrana3b8d7e2017-08-10 05:40:04 -0500245 this->bootProgress(bootprogress::Progress::ProgressStages::Unspecified);
246 this->operatingSystemState(osstatus::Status::OSStatus::Inactive);
Andrew Geissleref621162016-12-08 12:56:21 -0600247 }
Andrew Geissler969b2612018-03-29 10:16:51 -0700248 else if ((newStateUnit == HOST_STATE_POWERON_MIN_TGT) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800249 (newStateResult == "done") &&
Andrew Geissler969b2612018-03-29 10:16:51 -0700250 (stateActive(HOST_STATE_POWERON_MIN_TGT)))
Andrew Geissler58a18012018-01-19 19:36:05 -0800251 {
252 log<level::INFO>("Received signal that host is running");
253 this->currentHostState(server::Host::HostState::Running);
254 }
255 else if ((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500256 (newStateResult == "done") &&
257 (stateActive(HOST_STATE_QUIESCE_TGT)))
Andrew Geissler58a18012018-01-19 19:36:05 -0800258 {
259 if (Host::isAutoReboot())
260 {
261 log<level::INFO>("Beginning reboot...");
262 Host::requestedHostTransition(server::Host::Transition::Reboot);
263 }
264 else
265 {
266 log<level::INFO>("Maintaining quiesce");
267 this->currentHostState(server::Host::HostState::Quiesced);
268 }
269 }
Andrew Geissleref621162016-12-08 12:56:21 -0600270}
271
Andrew Geissler7b90a622017-08-08 11:41:08 -0500272uint32_t Host::decrementRebootCount()
273{
274 auto rebootCount = reboot::RebootAttempts::attemptsLeft();
Andrew Geissler58a18012018-01-19 19:36:05 -0800275 if (rebootCount > 0)
Andrew Geissler7b90a622017-08-08 11:41:08 -0500276 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800277 return (reboot::RebootAttempts::attemptsLeft(rebootCount - 1));
Andrew Geissler7b90a622017-08-08 11:41:08 -0500278 }
279 return rebootCount;
280}
281
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500282fs::path Host::serialize(const fs::path& dir)
283{
284 std::ofstream os(dir.c_str(), std::ios::binary);
285 cereal::JSONOutputArchive oarchive(os);
286 oarchive(*this);
287 return dir;
288}
289
290bool Host::deserialize(const fs::path& path)
291{
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500292 try
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500293 {
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500294 if (fs::exists(path))
295 {
296 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
297 cereal::JSONInputArchive iarchive(is);
298 iarchive(*this);
299 return true;
300 }
301 return false;
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500302 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800303 catch (cereal::Exception& e)
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500304 {
305 log<level::ERR>(e.what());
306 fs::remove(path);
307 return false;
308 }
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500309}
310
Andrew Geissleref3c1842016-12-01 12:33:09 -0600311Host::Transition Host::requestedHostTransition(Transition value)
312{
Andrew Geissler58a18012018-01-19 19:36:05 -0800313 log<level::INFO>("Host State transaction request",
314 entry("REQUESTED_HOST_TRANSITION=%s",
315 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600316
Andrew Geissler7b90a622017-08-08 11:41:08 -0500317 // If this is not a power off request then we need to
318 // decrement the reboot counter. This code should
319 // never prevent a power on, it should just decrement
320 // the count to 0. The quiesce handling is where the
321 // check of this count will occur
Andrew Geissler58a18012018-01-19 19:36:05 -0800322 if (value != server::Host::Transition::Off)
Andrew Geissler7b90a622017-08-08 11:41:08 -0500323 {
324 decrementRebootCount();
325 }
326
Andrew Geisslera27a6e82017-07-27 16:44:43 -0500327 executeTransition(value);
Andrew Geissler7b90a622017-08-08 11:41:08 -0500328
Andrew Geissler58a18012018-01-19 19:36:05 -0800329 auto retVal = server::Host::requestedHostTransition(value);
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500330 serialize();
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500331 return retVal;
Andrew Geissleref3c1842016-12-01 12:33:09 -0600332}
333
Dhruvaraj Subhashchandran4e6534f2017-09-19 06:13:20 -0500334Host::ProgressStages Host::bootProgress(ProgressStages value)
335{
336 auto retVal = bootprogress::Progress::bootProgress(value);
337 serialize();
338 return retVal;
339}
340
341Host::OSStatus Host::operatingSystemState(OSStatus value)
342{
343 auto retVal = osstatus::Status::operatingSystemState(value);
344 serialize();
345 return retVal;
346}
347
Andrew Geissleref3c1842016-12-01 12:33:09 -0600348Host::HostState Host::currentHostState(HostState value)
349{
Andrew Geissler58a18012018-01-19 19:36:05 -0800350 log<level::INFO>(
351 "Change to Host State",
352 entry("CURRENT_HOST_STATE=%s", convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600353 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600354}
355
Andrew Geissler36529022016-11-29 15:23:54 -0600356} // namespace manager
357} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700358} // namespace phosphor