blob: 1a0d18827fc595d232cedcf689920633770b5f70 [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
3#include "host_state_manager.hpp"
4
5#include <systemd/sd-bus.h>
6
7#include <cereal/archives/json.hpp>
8#include <cereal/cereal.hpp>
9#include <cereal/types/string.hpp>
10#include <cereal/types/tuple.hpp>
11#include <cereal/types/vector.hpp>
12#include <phosphor-logging/elog-errors.hpp>
13#include <phosphor-logging/log.hpp>
14#include <sdbusplus/exception.hpp>
15#include <sdbusplus/server.hpp>
16#include <xyz/openbmc_project/Common/error.hpp>
17#include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
18
19#include <fstream>
Andrew Geissler36529022016-11-29 15:23:54 -060020#include <iostream>
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060021#include <map>
22#include <string>
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050023
Vishwanatha Subbanna0a838732017-10-05 12:43:19 +053024// Register class version with Cereal
Andrew Geissler769a62f2019-12-06 13:36:08 -060025CEREAL_CLASS_VERSION(phosphor::state::manager::Host, CLASS_VERSION)
Andrew Geissler36529022016-11-29 15:23:54 -060026
27namespace phosphor
28{
29namespace state
30{
31namespace manager
32{
33
Andrew Geissler7b90a622017-08-08 11:41:08 -050034// When you see server:: or reboot:: you know we're referencing our base class
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060035namespace server = sdbusplus::xyz::openbmc_project::State::server;
Andrew Geissler7b90a622017-08-08 11:41:08 -050036namespace reboot = sdbusplus::xyz::openbmc_project::Control::Boot::server;
Dhruvaraj Subhashchandrana3b8d7e2017-08-10 05:40:04 -050037namespace bootprogress = sdbusplus::xyz::openbmc_project::State::Boot::server;
38namespace osstatus =
39 sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060040using namespace phosphor::logging;
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050041namespace fs = std::experimental::filesystem;
Anthony Wilson32c532e2018-10-25 21:56:07 -050042using sdbusplus::exception::SdBusError;
Andrew Geissler2f60aae2019-09-12 13:25:21 -050043using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060044
Andrew Geissler4f309e82017-05-24 15:18:01 -050045// host-shutdown notifies host of shutdown and that leads to host-stop being
46// called so initiate a host shutdown with the -shutdown target and consider the
47// host shut down when the -stop target is complete
48constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target";
Josh D. Kingca357922017-04-11 13:44:09 -050049constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
50constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Andrew Geissler969b2612018-03-29 10:16:51 -070051constexpr auto HOST_STATE_POWERON_MIN_TGT = "obmc-host-startmin@0.target";
Andrew Geisslera27a6e82017-07-27 16:44:43 -050052constexpr auto HOST_STATE_REBOOT_TGT = "obmc-host-reboot@0.target";
Andrew Geissler40dd6e72020-02-07 14:31:12 -060053constexpr auto HOST_STATE_WARM_REBOOT = "obmc-host-warm-reboot@0.target";
54constexpr auto HOST_STATE_FORCE_WARM_REBOOT =
55 "obmc-host-force-warm-reboot@0.target";
Andrew Geissler47b96122020-02-11 16:13:13 -060056constexpr auto HOST_STATE_DIAGNOSTIC_MODE =
57 "obmc-host-diagnostic-mode@0.target";
Andrew Geissler40dd6e72020-02-07 14:31:12 -060058
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050059constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060060
Josh D. King929ef702017-03-02 10:58:11 -060061constexpr auto ACTIVE_STATE = "active";
62constexpr auto ACTIVATING_STATE = "activating";
63
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060064/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080065const std::map<server::Host::Transition, std::string> SYSTEMD_TARGET_TABLE = {
66 {server::Host::Transition::Off, HOST_STATE_SOFT_POWEROFF_TGT},
67 {server::Host::Transition::On, HOST_STATE_POWERON_TGT},
Andrew Geissler40dd6e72020-02-07 14:31:12 -060068 {server::Host::Transition::Reboot, HOST_STATE_REBOOT_TGT},
Andrew Geissler7fdad602020-06-22 13:46:16 -050069// Some systems do not support a warm reboot so just map the reboot
70// requests to our normal cold reboot in that case
71#if ENABLE_WARM_REBOOT
Andrew Geissler40dd6e72020-02-07 14:31:12 -060072 {server::Host::Transition::GracefulWarmReboot, HOST_STATE_WARM_REBOOT},
73 {server::Host::Transition::ForceWarmReboot, HOST_STATE_FORCE_WARM_REBOOT}};
Andrew Geissler7fdad602020-06-22 13:46:16 -050074#else
75 {server::Host::Transition::GracefulWarmReboot, HOST_STATE_REBOOT_TGT},
76 {server::Host::Transition::ForceWarmReboot, HOST_STATE_REBOOT_TGT}};
77#endif
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060078
Andrew Geissler58a18012018-01-19 19:36:05 -080079constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
80constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060081constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
82
Josh D. King929ef702017-03-02 10:58:11 -060083constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
84constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
85
Andrew Geissler4da7e002017-01-24 15:21:40 -060086void Host::subscribeToSystemdSignals()
87{
Andrew Geissler58a18012018-01-19 19:36:05 -080088 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
89 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler2f60aae2019-09-12 13:25:21 -050090 try
91 {
92 this->bus.call_noreply(method);
93 }
94 catch (const SdBusError& e)
95 {
96 log<level::ERR>("Failed to subscribe to systemd signals",
97 entry("ERR=%s", e.what()));
98 elog<InternalFailure>();
99 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600100 return;
101}
102
Andrew Geissleref3c1842016-12-01 12:33:09 -0600103void Host::determineInitialState()
104{
Andrew Geissleref3c1842016-12-01 12:33:09 -0600105
Andrew Geissler969b2612018-03-29 10:16:51 -0700106 if (stateActive(HOST_STATE_POWERON_MIN_TGT))
Andrew Geissleref3c1842016-12-01 12:33:09 -0600107 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600108 log<level::INFO>("Initial Host State will be Running",
109 entry("CURRENT_HOST_STATE=%s",
110 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600111 server::Host::currentHostState(HostState::Running);
112 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600113 }
114 else
115 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600116 log<level::INFO>("Initial Host State will be Off",
117 entry("CURRENT_HOST_STATE=%s",
118 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600119 server::Host::currentHostState(HostState::Off);
120 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600121 }
122
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500123 if (!deserialize(HOST_STATE_PERSIST_PATH))
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500124 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800125 // set to default value.
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500126 server::Host::requestedHostTransition(Transition::Off);
127 }
Andrew Geissleref3c1842016-12-01 12:33:09 -0600128
129 return;
130}
131
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600132void Host::executeTransition(Transition tranReq)
133{
134 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
135
Andrew Geissler58a18012018-01-19 19:36:05 -0800136 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
137 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600138
139 method.append(sysdUnit);
140 method.append("replace");
141
Andrew Geissler4da7e002017-01-24 15:21:40 -0600142 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600143
144 return;
145}
146
Josh D. King929ef702017-03-02 10:58:11 -0600147bool Host::stateActive(const std::string& target)
148{
Patrick Williams2975e262020-05-13 18:01:09 -0500149 std::variant<std::string> currentState;
Josh D. King929ef702017-03-02 10:58:11 -0600150 sdbusplus::message::object_path unitTargetPath;
151
Andrew Geissler58a18012018-01-19 19:36:05 -0800152 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
153 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King929ef702017-03-02 10:58:11 -0600154
155 method.append(target);
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(unitTargetPath);
161 }
162 catch (const SdBusError& e)
163 {
164 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
Josh D. King929ef702017-03-02 10:58:11 -0600165 return false;
166 }
167
Andrew Geissler58a18012018-01-19 19:36:05 -0800168 method = this->bus.new_method_call(
169 SYSTEMD_SERVICE,
170 static_cast<const std::string&>(unitTargetPath).c_str(),
171 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King929ef702017-03-02 10:58:11 -0600172
173 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King929ef702017-03-02 10:58:11 -0600174
Anthony Wilson32c532e2018-10-25 21:56:07 -0500175 try
Josh D. King929ef702017-03-02 10:58:11 -0600176 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500177 auto result = this->bus.call(method);
178 result.read(currentState);
179 }
180 catch (const SdBusError& e)
181 {
182 log<level::ERR>("Error in ActiveState Get",
183 entry("ERROR=%s", e.what()));
Josh D. King929ef702017-03-02 10:58:11 -0600184 return false;
185 }
186
Patrick Williams37413dc2020-05-13 11:29:54 -0500187 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800188 return currentStateStr == ACTIVE_STATE ||
189 currentStateStr == ACTIVATING_STATE;
Josh D. King929ef702017-03-02 10:58:11 -0600190}
191
Michael Tritz206a8332017-02-06 16:01:23 -0600192bool Host::isAutoReboot()
193{
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500194 using namespace settings;
Michael Tritz206a8332017-02-06 16:01:23 -0600195
Andrew Geissler58a18012018-01-19 19:36:05 -0800196 auto method = bus.new_method_call(
197 settings.service(settings.autoReboot, autoRebootIntf).c_str(),
198 settings.autoReboot.c_str(), "org.freedesktop.DBus.Properties", "Get");
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500199 method.append(autoRebootIntf, "AutoReboot");
Michael Tritz206a8332017-02-06 16:01:23 -0600200
Anthony Wilson32c532e2018-10-25 21:56:07 -0500201 try
Michael Tritz206a8332017-02-06 16:01:23 -0600202 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500203 auto reply = bus.call(method);
204
Patrick Williams2975e262020-05-13 18:01:09 -0500205 std::variant<bool> result;
Anthony Wilson32c532e2018-10-25 21:56:07 -0500206 reply.read(result);
Patrick Williams37413dc2020-05-13 11:29:54 -0500207 auto autoReboot = std::get<bool>(result);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500208 auto rebootCounterParam = reboot::RebootAttempts::attemptsLeft();
209
210 if (autoReboot)
Saqib Khancbe08d12017-03-10 01:29:20 -0600211 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500212 if (rebootCounterParam > 0)
213 {
214 // Reduce BOOTCOUNT by 1
215 log<level::INFO>("Auto reboot enabled, rebooting");
216 return true;
217 }
218 else if (rebootCounterParam == 0)
219 {
220 // Reset reboot counter and go to quiesce state
221 log<level::INFO>("Auto reboot enabled. "
222 "HOST BOOTCOUNT already set to 0.");
223 attemptsLeft(BOOT_COUNT_MAX_ALLOWED);
224 return false;
225 }
226 else
227 {
228 log<level::INFO>("Auto reboot enabled. "
229 "HOST BOOTCOUNT has an invalid value.");
230 return false;
231 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500232 }
233 else
234 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500235 log<level::INFO>("Auto reboot disabled.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600236 return false;
237 }
Michael Tritz206a8332017-02-06 16:01:23 -0600238 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500239 catch (const SdBusError& e)
Saqib Khand5ac6352017-04-04 09:53:59 -0500240 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500241 log<level::ERR>("Error in AutoReboot Get", entry("ERROR=%s", e.what()));
Saqib Khand5ac6352017-04-04 09:53:59 -0500242 return false;
243 }
Michael Tritz206a8332017-02-06 16:01:23 -0600244}
245
Andrew Geisslere4039a82020-02-11 15:51:59 -0600246void Host::sysStateChangeJobRemoved(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600247{
Andrew Geissler58a18012018-01-19 19:36:05 -0800248 uint32_t newStateID{};
Andrew Geissler4da7e002017-01-24 15:21:40 -0600249 sdbusplus::message::object_path newStateObjPath;
250 std::string newStateUnit{};
251 std::string newStateResult{};
252
Andrew Geissler58a18012018-01-19 19:36:05 -0800253 // Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500254 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600255
Andrew Geissler58a18012018-01-19 19:36:05 -0800256 if ((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Andrew Geissler969b2612018-03-29 10:16:51 -0700257 (newStateResult == "done") &&
258 (!stateActive(HOST_STATE_POWERON_MIN_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600259 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930260 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600261 this->currentHostState(server::Host::HostState::Off);
Dhruvaraj Subhashchandrana3b8d7e2017-08-10 05:40:04 -0500262 this->bootProgress(bootprogress::Progress::ProgressStages::Unspecified);
263 this->operatingSystemState(osstatus::Status::OSStatus::Inactive);
Andrew Geissleref621162016-12-08 12:56:21 -0600264 }
Andrew Geissler969b2612018-03-29 10:16:51 -0700265 else if ((newStateUnit == HOST_STATE_POWERON_MIN_TGT) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800266 (newStateResult == "done") &&
Andrew Geissler969b2612018-03-29 10:16:51 -0700267 (stateActive(HOST_STATE_POWERON_MIN_TGT)))
Andrew Geissler58a18012018-01-19 19:36:05 -0800268 {
269 log<level::INFO>("Received signal that host is running");
270 this->currentHostState(server::Host::HostState::Running);
271 }
272 else if ((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500273 (newStateResult == "done") &&
274 (stateActive(HOST_STATE_QUIESCE_TGT)))
Andrew Geissler58a18012018-01-19 19:36:05 -0800275 {
276 if (Host::isAutoReboot())
277 {
278 log<level::INFO>("Beginning reboot...");
279 Host::requestedHostTransition(server::Host::Transition::Reboot);
280 }
281 else
282 {
283 log<level::INFO>("Maintaining quiesce");
284 this->currentHostState(server::Host::HostState::Quiesced);
285 }
286 }
Andrew Geissleref621162016-12-08 12:56:21 -0600287}
288
Andrew Geissler47b96122020-02-11 16:13:13 -0600289void Host::sysStateChangeJobNew(sdbusplus::message::message& msg)
290{
291 uint32_t newStateID{};
292 sdbusplus::message::object_path newStateObjPath;
293 std::string newStateUnit{};
294
295 // Read the msg and populate each variable
296 msg.read(newStateID, newStateObjPath, newStateUnit);
297
298 if (newStateUnit == HOST_STATE_DIAGNOSTIC_MODE)
299 {
300 log<level::INFO>("Received signal that host is in diagnostice mode");
301 this->currentHostState(server::Host::HostState::DiagnosticMode);
302 }
303}
304
Andrew Geissler7b90a622017-08-08 11:41:08 -0500305uint32_t Host::decrementRebootCount()
306{
307 auto rebootCount = reboot::RebootAttempts::attemptsLeft();
Andrew Geissler58a18012018-01-19 19:36:05 -0800308 if (rebootCount > 0)
Andrew Geissler7b90a622017-08-08 11:41:08 -0500309 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800310 return (reboot::RebootAttempts::attemptsLeft(rebootCount - 1));
Andrew Geissler7b90a622017-08-08 11:41:08 -0500311 }
312 return rebootCount;
313}
314
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500315fs::path Host::serialize(const fs::path& dir)
316{
317 std::ofstream os(dir.c_str(), std::ios::binary);
318 cereal::JSONOutputArchive oarchive(os);
319 oarchive(*this);
320 return dir;
321}
322
323bool Host::deserialize(const fs::path& path)
324{
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500325 try
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500326 {
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500327 if (fs::exists(path))
328 {
329 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
330 cereal::JSONInputArchive iarchive(is);
331 iarchive(*this);
332 return true;
333 }
334 return false;
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500335 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800336 catch (cereal::Exception& e)
Jayanth Othayothe39f3792017-09-19 23:53:31 -0500337 {
338 log<level::ERR>(e.what());
339 fs::remove(path);
340 return false;
341 }
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500342}
343
Andrew Geissleref3c1842016-12-01 12:33:09 -0600344Host::Transition Host::requestedHostTransition(Transition value)
345{
Andrew Geissler58a18012018-01-19 19:36:05 -0800346 log<level::INFO>("Host State transaction request",
347 entry("REQUESTED_HOST_TRANSITION=%s",
348 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600349
Andrew Geissler7b90a622017-08-08 11:41:08 -0500350 // If this is not a power off request then we need to
351 // decrement the reboot counter. This code should
352 // never prevent a power on, it should just decrement
353 // the count to 0. The quiesce handling is where the
354 // check of this count will occur
Andrew Geissler58a18012018-01-19 19:36:05 -0800355 if (value != server::Host::Transition::Off)
Andrew Geissler7b90a622017-08-08 11:41:08 -0500356 {
357 decrementRebootCount();
358 }
359
Andrew Geisslera27a6e82017-07-27 16:44:43 -0500360 executeTransition(value);
Andrew Geissler7b90a622017-08-08 11:41:08 -0500361
Andrew Geissler58a18012018-01-19 19:36:05 -0800362 auto retVal = server::Host::requestedHostTransition(value);
Andrew Geissler033fc3b2017-08-30 15:11:44 -0500363 serialize();
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500364 return retVal;
Andrew Geissleref3c1842016-12-01 12:33:09 -0600365}
366
Dhruvaraj Subhashchandran4e6534f2017-09-19 06:13:20 -0500367Host::ProgressStages Host::bootProgress(ProgressStages value)
368{
369 auto retVal = bootprogress::Progress::bootProgress(value);
370 serialize();
371 return retVal;
372}
373
374Host::OSStatus Host::operatingSystemState(OSStatus value)
375{
376 auto retVal = osstatus::Status::operatingSystemState(value);
377 serialize();
378 return retVal;
379}
380
Andrew Geissleref3c1842016-12-01 12:33:09 -0600381Host::HostState Host::currentHostState(HostState value)
382{
Andrew Geissler58a18012018-01-19 19:36:05 -0800383 log<level::INFO>(
384 "Change to Host State",
385 entry("CURRENT_HOST_STATE=%s", convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600386 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600387}
388
Andrew Geissler36529022016-11-29 15:23:54 -0600389} // namespace manager
390} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700391} // namespace phosphor