blob: c333032ae1a8e34e3d7501ad15349ed233d8decb [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
Andrew Geissler769a62f2019-12-06 13:36:08 -060021CEREAL_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 Geissler2f60aae2019-09-12 13:25:21 -050039using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060040
Andrew Geissler4f309e82017-05-24 15:18:01 -050041// host-shutdown notifies host of shutdown and that leads to host-stop being
42// called so initiate a host shutdown with the -shutdown target and consider the
43// host shut down when the -stop target is complete
44constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target";
Josh D. Kingca357922017-04-11 13:44:09 -050045constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
46constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Andrew Geissler969b2612018-03-29 10:16:51 -070047constexpr auto HOST_STATE_POWERON_MIN_TGT = "obmc-host-startmin@0.target";
Andrew Geisslera27a6e82017-07-27 16:44:43 -050048constexpr auto HOST_STATE_REBOOT_TGT = "obmc-host-reboot@0.target";
Andrew Geissler40dd6e72020-02-07 14:31:12 -060049constexpr auto HOST_STATE_WARM_REBOOT = "obmc-host-warm-reboot@0.target";
50constexpr auto HOST_STATE_FORCE_WARM_REBOOT =
51 "obmc-host-force-warm-reboot@0.target";
Andrew Geissler47b96122020-02-11 16:13:13 -060052constexpr auto HOST_STATE_DIAGNOSTIC_MODE =
53 "obmc-host-diagnostic-mode@0.target";
Andrew Geissler40dd6e72020-02-07 14:31:12 -060054
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050055constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060056
Josh D. King929ef702017-03-02 10:58:11 -060057constexpr auto ACTIVE_STATE = "active";
58constexpr auto ACTIVATING_STATE = "activating";
59
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060060/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080061const std::map<server::Host::Transition, std::string> SYSTEMD_TARGET_TABLE = {
62 {server::Host::Transition::Off, HOST_STATE_SOFT_POWEROFF_TGT},
63 {server::Host::Transition::On, HOST_STATE_POWERON_TGT},
Andrew Geissler40dd6e72020-02-07 14:31:12 -060064 {server::Host::Transition::Reboot, HOST_STATE_REBOOT_TGT},
65 {server::Host::Transition::GracefulWarmReboot, HOST_STATE_WARM_REBOOT},
66 {server::Host::Transition::ForceWarmReboot, HOST_STATE_FORCE_WARM_REBOOT}};
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060067
Andrew Geissler58a18012018-01-19 19:36:05 -080068constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
69constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060070constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
71
Josh D. King929ef702017-03-02 10:58:11 -060072constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
73constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
74
Andrew Geissler4da7e002017-01-24 15:21:40 -060075void Host::subscribeToSystemdSignals()
76{
Andrew Geissler58a18012018-01-19 19:36:05 -080077 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
78 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler2f60aae2019-09-12 13:25:21 -050079 try
80 {
81 this->bus.call_noreply(method);
82 }
83 catch (const SdBusError& e)
84 {
85 log<level::ERR>("Failed to subscribe to systemd signals",
86 entry("ERR=%s", e.what()));
87 elog<InternalFailure>();
88 }
Andrew Geissler4da7e002017-01-24 15:21:40 -060089 return;
90}
91
Andrew Geissleref3c1842016-12-01 12:33:09 -060092void Host::determineInitialState()
93{
Andrew Geissleref3c1842016-12-01 12:33:09 -060094
Andrew Geissler969b2612018-03-29 10:16:51 -070095 if (stateActive(HOST_STATE_POWERON_MIN_TGT))
Andrew Geissleref3c1842016-12-01 12:33:09 -060096 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060097 log<level::INFO>("Initial Host State will be Running",
98 entry("CURRENT_HOST_STATE=%s",
99 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600100 server::Host::currentHostState(HostState::Running);
101 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600102 }
103 else
104 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600105 log<level::INFO>("Initial Host State will be Off",
106 entry("CURRENT_HOST_STATE=%s",
107 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600108 server::Host::currentHostState(HostState::Off);
109 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600110 }
111
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500112 if (!deserialize(HOST_STATE_PERSIST_PATH))
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500113 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800114 // set to default value.
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500115 server::Host::requestedHostTransition(Transition::Off);
116 }
Andrew Geissleref3c1842016-12-01 12:33:09 -0600117
118 return;
119}
120
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600121void Host::executeTransition(Transition tranReq)
122{
123 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
124
Andrew Geissler58a18012018-01-19 19:36:05 -0800125 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
126 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600127
128 method.append(sysdUnit);
129 method.append("replace");
130
Andrew Geissler4da7e002017-01-24 15:21:40 -0600131 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600132
133 return;
134}
135
Josh D. King929ef702017-03-02 10:58:11 -0600136bool Host::stateActive(const std::string& target)
137{
138 sdbusplus::message::variant<std::string> currentState;
139 sdbusplus::message::object_path unitTargetPath;
140
Andrew Geissler58a18012018-01-19 19:36:05 -0800141 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
142 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King929ef702017-03-02 10:58:11 -0600143
144 method.append(target);
Josh D. King929ef702017-03-02 10:58:11 -0600145
Anthony Wilson32c532e2018-10-25 21:56:07 -0500146 try
Josh D. King929ef702017-03-02 10:58:11 -0600147 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500148 auto result = this->bus.call(method);
149 result.read(unitTargetPath);
150 }
151 catch (const SdBusError& e)
152 {
153 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
Josh D. King929ef702017-03-02 10:58:11 -0600154 return false;
155 }
156
Andrew Geissler58a18012018-01-19 19:36:05 -0800157 method = this->bus.new_method_call(
158 SYSTEMD_SERVICE,
159 static_cast<const std::string&>(unitTargetPath).c_str(),
160 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King929ef702017-03-02 10:58:11 -0600161
162 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King929ef702017-03-02 10:58:11 -0600163
Anthony Wilson32c532e2018-10-25 21:56:07 -0500164 try
Josh D. King929ef702017-03-02 10:58:11 -0600165 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500166 auto result = this->bus.call(method);
167 result.read(currentState);
168 }
169 catch (const SdBusError& e)
170 {
171 log<level::ERR>("Error in ActiveState Get",
172 entry("ERROR=%s", e.what()));
Josh D. King929ef702017-03-02 10:58:11 -0600173 return false;
174 }
175
Patrick Williams37413dc2020-05-13 11:29:54 -0500176 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800177 return currentStateStr == ACTIVE_STATE ||
178 currentStateStr == ACTIVATING_STATE;
Josh D. King929ef702017-03-02 10:58:11 -0600179}
180
Michael Tritz206a8332017-02-06 16:01:23 -0600181bool Host::isAutoReboot()
182{
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500183 using namespace settings;
Michael Tritz206a8332017-02-06 16:01:23 -0600184
Andrew Geissler58a18012018-01-19 19:36:05 -0800185 auto method = bus.new_method_call(
186 settings.service(settings.autoReboot, autoRebootIntf).c_str(),
187 settings.autoReboot.c_str(), "org.freedesktop.DBus.Properties", "Get");
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500188 method.append(autoRebootIntf, "AutoReboot");
Michael Tritz206a8332017-02-06 16:01:23 -0600189
Anthony Wilson32c532e2018-10-25 21:56:07 -0500190 try
Michael Tritz206a8332017-02-06 16:01:23 -0600191 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500192 auto reply = bus.call(method);
193
194 sdbusplus::message::variant<bool> result;
195 reply.read(result);
Patrick Williams37413dc2020-05-13 11:29:54 -0500196 auto autoReboot = std::get<bool>(result);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500197 auto rebootCounterParam = reboot::RebootAttempts::attemptsLeft();
198
199 if (autoReboot)
Saqib Khancbe08d12017-03-10 01:29:20 -0600200 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500201 if (rebootCounterParam > 0)
202 {
203 // Reduce BOOTCOUNT by 1
204 log<level::INFO>("Auto reboot enabled, rebooting");
205 return true;
206 }
207 else if (rebootCounterParam == 0)
208 {
209 // Reset reboot counter and go to quiesce state
210 log<level::INFO>("Auto reboot enabled. "
211 "HOST BOOTCOUNT already set to 0.");
212 attemptsLeft(BOOT_COUNT_MAX_ALLOWED);
213 return false;
214 }
215 else
216 {
217 log<level::INFO>("Auto reboot enabled. "
218 "HOST BOOTCOUNT has an invalid value.");
219 return false;
220 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500221 }
222 else
223 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500224 log<level::INFO>("Auto reboot disabled.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600225 return false;
226 }
Michael Tritz206a8332017-02-06 16:01:23 -0600227 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500228 catch (const SdBusError& e)
Saqib Khand5ac6352017-04-04 09:53:59 -0500229 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500230 log<level::ERR>("Error in AutoReboot Get", entry("ERROR=%s", e.what()));
Saqib Khand5ac6352017-04-04 09:53:59 -0500231 return false;
232 }
Michael Tritz206a8332017-02-06 16:01:23 -0600233}
234
Andrew Geisslere4039a82020-02-11 15:51:59 -0600235void Host::sysStateChangeJobRemoved(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600236{
Andrew Geissler58a18012018-01-19 19:36:05 -0800237 uint32_t newStateID{};
Andrew Geissler4da7e002017-01-24 15:21:40 -0600238 sdbusplus::message::object_path newStateObjPath;
239 std::string newStateUnit{};
240 std::string newStateResult{};
241
Andrew Geissler58a18012018-01-19 19:36:05 -0800242 // 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
Andrew Geissler58a18012018-01-19 19:36:05 -0800245 if ((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Andrew Geissler969b2612018-03-29 10:16:51 -0700246 (newStateResult == "done") &&
247 (!stateActive(HOST_STATE_POWERON_MIN_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 Geissleref621162016-12-08 12:56:21 -0600253 }
Andrew Geissler969b2612018-03-29 10:16:51 -0700254 else if ((newStateUnit == HOST_STATE_POWERON_MIN_TGT) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800255 (newStateResult == "done") &&
Andrew Geissler969b2612018-03-29 10:16:51 -0700256 (stateActive(HOST_STATE_POWERON_MIN_TGT)))
Andrew Geissler58a18012018-01-19 19:36:05 -0800257 {
258 log<level::INFO>("Received signal that host is running");
259 this->currentHostState(server::Host::HostState::Running);
260 }
261 else if ((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500262 (newStateResult == "done") &&
263 (stateActive(HOST_STATE_QUIESCE_TGT)))
Andrew Geissler58a18012018-01-19 19:36:05 -0800264 {
265 if (Host::isAutoReboot())
266 {
267 log<level::INFO>("Beginning reboot...");
268 Host::requestedHostTransition(server::Host::Transition::Reboot);
269 }
270 else
271 {
272 log<level::INFO>("Maintaining quiesce");
273 this->currentHostState(server::Host::HostState::Quiesced);
274 }
275 }
Andrew Geissleref621162016-12-08 12:56:21 -0600276}
277
Andrew Geissler47b96122020-02-11 16:13:13 -0600278void Host::sysStateChangeJobNew(sdbusplus::message::message& msg)
279{
280 uint32_t newStateID{};
281 sdbusplus::message::object_path newStateObjPath;
282 std::string newStateUnit{};
283
284 // Read the msg and populate each variable
285 msg.read(newStateID, newStateObjPath, newStateUnit);
286
287 if (newStateUnit == HOST_STATE_DIAGNOSTIC_MODE)
288 {
289 log<level::INFO>("Received signal that host is in diagnostice mode");
290 this->currentHostState(server::Host::HostState::DiagnosticMode);
291 }
292}
293
Andrew Geissler7b90a622017-08-08 11:41:08 -0500294uint32_t Host::decrementRebootCount()
295{
296 auto rebootCount = reboot::RebootAttempts::attemptsLeft();
Andrew Geissler58a18012018-01-19 19:36:05 -0800297 if (rebootCount > 0)
Andrew Geissler7b90a622017-08-08 11:41:08 -0500298 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800299 return (reboot::RebootAttempts::attemptsLeft(rebootCount - 1));
Andrew Geissler7b90a622017-08-08 11:41:08 -0500300 }
301 return rebootCount;
302}
303
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500304fs::path Host::serialize(const fs::path& dir)
305{
306 std::ofstream os(dir.c_str(), std::ios::binary);
307 cereal::JSONOutputArchive oarchive(os);
308 oarchive(*this);
309 return dir;
310}
311
312bool Host::deserialize(const fs::path& path)
313{
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500314 try
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500315 {
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500316 if (fs::exists(path))
317 {
318 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
319 cereal::JSONInputArchive iarchive(is);
320 iarchive(*this);
321 return true;
322 }
323 return false;
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500324 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800325 catch (cereal::Exception& e)
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500326 {
327 log<level::ERR>(e.what());
328 fs::remove(path);
329 return false;
330 }
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500331}
332
Andrew Geissleref3c1842016-12-01 12:33:09 -0600333Host::Transition Host::requestedHostTransition(Transition value)
334{
Andrew Geissler58a18012018-01-19 19:36:05 -0800335 log<level::INFO>("Host State transaction request",
336 entry("REQUESTED_HOST_TRANSITION=%s",
337 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600338
Andrew Geissler7b90a622017-08-08 11:41:08 -0500339 // If this is not a power off request then we need to
340 // decrement the reboot counter. This code should
341 // never prevent a power on, it should just decrement
342 // the count to 0. The quiesce handling is where the
343 // check of this count will occur
Andrew Geissler58a18012018-01-19 19:36:05 -0800344 if (value != server::Host::Transition::Off)
Andrew Geissler7b90a622017-08-08 11:41:08 -0500345 {
346 decrementRebootCount();
347 }
348
Andrew Geisslera27a6e82017-07-27 16:44:43 -0500349 executeTransition(value);
Andrew Geissler7b90a622017-08-08 11:41:08 -0500350
Andrew Geissler58a18012018-01-19 19:36:05 -0800351 auto retVal = server::Host::requestedHostTransition(value);
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500352 serialize();
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500353 return retVal;
Andrew Geissleref3c1842016-12-01 12:33:09 -0600354}
355
Dhruvaraj Subhashchandran4e6534f2017-09-19 06:13:20 -0500356Host::ProgressStages Host::bootProgress(ProgressStages value)
357{
358 auto retVal = bootprogress::Progress::bootProgress(value);
359 serialize();
360 return retVal;
361}
362
363Host::OSStatus Host::operatingSystemState(OSStatus value)
364{
365 auto retVal = osstatus::Status::operatingSystemState(value);
366 serialize();
367 return retVal;
368}
369
Andrew Geissleref3c1842016-12-01 12:33:09 -0600370Host::HostState Host::currentHostState(HostState value)
371{
Andrew Geissler58a18012018-01-19 19:36:05 -0800372 log<level::INFO>(
373 "Change to Host State",
374 entry("CURRENT_HOST_STATE=%s", convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600375 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600376}
377
Andrew Geissler36529022016-11-29 15:23:54 -0600378} // namespace manager
379} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700380} // namespace phosphor