blob: 54321d23fb9a8941d47412138fd866e5ab856246 [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>
Michael Tritz206a8332017-02-06 16:01:23 -06005#include <sdbusplus/server.hpp>
Saqib Khana8006a22017-02-14 11:37:08 -06006#include <phosphor-logging/log.hpp>
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -05007#include <experimental/filesystem>
8#include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
Andrew Geissler36529022016-11-29 15:23:54 -06009#include "host_state_manager.hpp"
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050010#include "host_state_serialize.hpp"
11#include "config.h"
12
Andrew Geissler36529022016-11-29 15:23:54 -060013
14namespace phosphor
15{
16namespace state
17{
18namespace manager
19{
20
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060021// When you see server:: you know we're referencing our base class
22namespace server = sdbusplus::xyz::openbmc_project::State::server;
23
Andrew Geissler1e3bf942016-12-13 15:32:22 -060024using namespace phosphor::logging;
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050025namespace fs = std::experimental::filesystem;
Andrew Geissler1e3bf942016-12-13 15:32:22 -060026
Andrew Geissler4f309e82017-05-24 15:18:01 -050027// host-shutdown notifies host of shutdown and that leads to host-stop being
28// called so initiate a host shutdown with the -shutdown target and consider the
29// host shut down when the -stop target is complete
30constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target";
Josh D. Kingca357922017-04-11 13:44:09 -050031constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
32constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050033constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060034
Josh D. King929ef702017-03-02 10:58:11 -060035constexpr auto ACTIVE_STATE = "active";
36constexpr auto ACTIVATING_STATE = "activating";
37
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060038/* Map a transition to it's systemd target */
39const std::map<server::Host::Transition,std::string> SYSTEMD_TARGET_TABLE =
40{
Andrew Geissler4f309e82017-05-24 15:18:01 -050041 {server::Host::Transition::Off, HOST_STATE_SOFT_POWEROFF_TGT},
Andrew Geissler4da7e002017-01-24 15:21:40 -060042 {server::Host::Transition::On, HOST_STATE_POWERON_TGT}
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060043};
44
45constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
46constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
47constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
48
Michael Tritz206a8332017-02-06 16:01:23 -060049constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
Leonel Gonzalezf318d872017-03-16 13:47:49 -050050constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
Michael Tritz206a8332017-02-06 16:01:23 -060051constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
52
Saqib Khancbe08d12017-03-10 01:29:20 -060053constexpr auto REBOOTCOUNTER_SERVICE("org.openbmc.Sensors");
54constexpr auto REBOOTCOUNTER_PATH("/org/openbmc/sensors/host/BootCount");
55constexpr auto REBOOTCOUNTER_INTERFACE("org.openbmc.SensorValue");
56
Josh D. King929ef702017-03-02 10:58:11 -060057constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
58constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
59
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -050060constexpr auto SETTINGS_INTERFACE =
61 "xyz.openbmc_project.Control.Power.RestorePolicy";
62constexpr auto SETTINGS_SERVICE_ROOT = "/";
63constexpr auto SETTINGS_HOST_STATE_RESTORE = "PowerRestorePolicy";
64
Andrew Geissler48696ce2017-05-22 14:41:58 -050065// TODO openbmc/openbmc#1646 - boot count needs to be defined in 1 place
66constexpr auto DEFAULT_BOOTCOUNT = 3;
Josh D. King929ef702017-03-02 10:58:11 -060067
Andrew Geissleref621162016-12-08 12:56:21 -060068/* Map a system state to the HostState */
Andrew Geissleref621162016-12-08 12:56:21 -060069const std::map<std::string, server::Host::HostState> SYS_HOST_STATE_TABLE = {
70 {"HOST_BOOTING", server::Host::HostState::Running},
Saqib Khanadaa7212017-02-23 13:19:28 -060071 {"HOST_POWERED_OFF", server::Host::HostState::Off},
72 {"HOST_QUIESCED", server::Host::HostState::Quiesced}
Andrew Geissleref621162016-12-08 12:56:21 -060073};
74
Andrew Geissler4da7e002017-01-24 15:21:40 -060075void Host::subscribeToSystemdSignals()
76{
77 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
78 SYSTEMD_OBJ_PATH,
79 SYSTEMD_INTERFACE,
80 "Subscribe");
81 this->bus.call_noreply(method);
82
83 return;
84}
85
Andrew Geissleref3c1842016-12-01 12:33:09 -060086void Host::determineInitialState()
87{
Andrew Geissleref3c1842016-12-01 12:33:09 -060088
Josh D. King222014e2017-05-16 13:35:21 -050089 if(stateActive(HOST_STATE_POWERON_TGT))
Andrew Geissleref3c1842016-12-01 12:33:09 -060090 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060091 log<level::INFO>("Initial Host State will be Running",
92 entry("CURRENT_HOST_STATE=%s",
93 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060094 server::Host::currentHostState(HostState::Running);
95 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -060096 }
97 else
98 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060099 log<level::INFO>("Initial Host State will be Off",
100 entry("CURRENT_HOST_STATE=%s",
101 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600102 server::Host::currentHostState(HostState::Off);
103 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600104 }
105
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500106 auto restore = getStateRestoreSetting();
107
108 if ((!restore) || (!deserialize(HOST_STATE_PERSIST_PATH,*this)))
109 {
110 //set to default value.
111 server::Host::requestedHostTransition(Transition::Off);
112 }
Andrew Geissleref3c1842016-12-01 12:33:09 -0600113
114 return;
115}
116
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500117bool Host::getStateRestoreSetting() const
118{
119 using namespace phosphor::logging;
120 auto depth = 0;
121 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
122 MAPPER_PATH,
123 MAPPER_INTERFACE,
124 "GetSubTree");
125 mapperCall.append(SETTINGS_SERVICE_ROOT);
126 mapperCall.append(depth);
127 mapperCall.append(std::vector<std::string>({SETTINGS_INTERFACE}));
128
129 auto mapperResponseMsg = bus.call(mapperCall);
130 if (mapperResponseMsg.is_method_error())
131 {
132 log<level::ERR>("Error in mapper call");
133 return false;
134 }
135
136 using MapperResponseType = std::map<std::string,
137 std::map<std::string, std::vector<std::string>>>;
138 MapperResponseType mapperResponse;
139 mapperResponseMsg.read(mapperResponse);
140 if (mapperResponse.empty())
141 {
142 log<level::ERR>("Invalid response from mapper");
143 return false;
144 }
145
146 auto& settingsPath = mapperResponse.begin()->first;
147 auto& service = mapperResponse.begin()->second.begin()->first;
148
149 auto cmdMsg = bus.new_method_call(service.c_str(),
150 settingsPath.c_str(),
151 SYSTEMD_PROPERTY_IFACE,
152 "Get");
153 cmdMsg.append(SETTINGS_INTERFACE);
154 cmdMsg.append(SETTINGS_HOST_STATE_RESTORE);
155
156 auto response = bus.call(cmdMsg);
157 if (response.is_method_error())
158 {
159 log<level::ERR>("Error in fetching host state restore settings");
160 return false;
161 }
162
163 sdbusplus::message::variant<std::string> result;
164 response.read(result);
165
166 using RestorePolicy = sdbusplus::xyz::openbmc_project::Control::
167 Power::server::RestorePolicy;
168
169 if (RestorePolicy::convertPolicyFromString(result.get<std::string>()) ==
170 RestorePolicy::Policy::Restore)
171 {
172 return true;
173 }
174 return false;
175}
176
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600177void Host::executeTransition(Transition tranReq)
178{
179 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
180
181 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
182 SYSTEMD_OBJ_PATH,
183 SYSTEMD_INTERFACE,
184 "StartUnit");
185
186 method.append(sysdUnit);
187 method.append("replace");
188
Andrew Geissler4da7e002017-01-24 15:21:40 -0600189 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600190
191 return;
192}
193
Josh D. King929ef702017-03-02 10:58:11 -0600194bool Host::stateActive(const std::string& target)
195{
196 sdbusplus::message::variant<std::string> currentState;
197 sdbusplus::message::object_path unitTargetPath;
198
199 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
200 SYSTEMD_OBJ_PATH,
201 SYSTEMD_INTERFACE,
202 "GetUnit");
203
204 method.append(target);
205 auto result = this->bus.call(method);
206
207 //Check that the bus call didn't result in an error
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500208 if (result.is_method_error())
Josh D. King929ef702017-03-02 10:58:11 -0600209 {
210 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
211 entry(" %s", SYSTEMD_INTERFACE));
212 return false;
213 }
214
215 result.read(unitTargetPath);
216
217 method = this->bus.new_method_call(SYSTEMD_SERVICE,
218 static_cast<const std::string&>
219 (unitTargetPath).c_str(),
220 SYSTEMD_PROPERTY_IFACE,
221 "Get");
222
223 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
224 result = this->bus.call(method);
225
226 //Check that the bus call didn't result in an error
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500227 if (result.is_method_error())
Josh D. King929ef702017-03-02 10:58:11 -0600228 {
229 log<level::ERR>("Error in bus call - could not resolve Get for:",
230 entry(" %s", SYSTEMD_PROPERTY_IFACE));
231 return false;
232 }
233
234 result.read(currentState);
235
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500236 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King929ef702017-03-02 10:58:11 -0600237 {
238 //False - not active
239 return false;
240 }
241 //True - active
242 return true;
243}
244
Saqib Khand5ac6352017-04-04 09:53:59 -0500245void Host::setHostbootCount(int bootCount)
246{
247 auto method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
248 REBOOTCOUNTER_PATH,
249 REBOOTCOUNTER_INTERFACE,
250 "setValue");
251 sdbusplus::message::variant<int> newParam = bootCount;
252 method.append(newParam);
253 this->bus.call_noreply(method);
254}
Josh D. King929ef702017-03-02 10:58:11 -0600255
Michael Tritz206a8332017-02-06 16:01:23 -0600256bool Host::isAutoReboot()
257{
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500258 using namespace settings;
Michael Tritz206a8332017-02-06 16:01:23 -0600259
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500260 auto method =
261 bus.new_method_call(
262 settings.service(settings.autoReboot, autoRebootIntf).c_str(),
263 settings.autoReboot.c_str(),
264 "org.freedesktop.DBus.Properties",
265 "Get");
266 method.append(autoRebootIntf, "AutoReboot");
267 auto reply = bus.call(method);
Michael Tritz206a8332017-02-06 16:01:23 -0600268 if (reply.is_method_error())
269 {
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500270 log<level::ERR>("Error in AutoReboot Get");
Michael Tritz206a8332017-02-06 16:01:23 -0600271 return false;
272 }
273
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500274 sdbusplus::message::variant<bool> result;
275 reply.read(result);
276 auto autoReboot = result.get<bool>();
Michael Tritz206a8332017-02-06 16:01:23 -0600277
Saqib Khand5ac6352017-04-04 09:53:59 -0500278 sdbusplus::message::variant<int> rebootCounterParam = 0;
Saqib Khancbe08d12017-03-10 01:29:20 -0600279 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
280 REBOOTCOUNTER_PATH,
281 REBOOTCOUNTER_INTERFACE,
282 "getValue");
283 reply = this->bus.call(method);
284 if (reply.is_method_error())
285 {
286 log<level::ERR>("Error in BOOTCOUNT getValue");
287 return false;
288 }
289 reply.read(rebootCounterParam);
290
Deepak Kodihalli3dd08a52017-07-25 07:34:44 -0500291 if (autoReboot)
Michael Tritz206a8332017-02-06 16:01:23 -0600292 {
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500293 if ( rebootCounterParam > 0)
Saqib Khancbe08d12017-03-10 01:29:20 -0600294 {
295 // Reduce BOOTCOUNT by 1
Saqib Khand5ac6352017-04-04 09:53:59 -0500296 log<level::INFO>("Auto reboot enabled. "
297 "Reducing HOST BOOTCOUNT by 1.");
298 Host::setHostbootCount((sdbusplus::message::variant_ns::
299 get<int>(rebootCounterParam)) - 1);
Saqib Khancbe08d12017-03-10 01:29:20 -0600300 return true;
301 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500302 else if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600303 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600304 // Reset reboot counter and go to quiesce state
Saqib Khand5ac6352017-04-04 09:53:59 -0500305 log<level::INFO>("Auto reboot enabled. "
306 "HOST BOOTCOUNT already set to 0.");
307 Host::setHostbootCount(DEFAULT_BOOTCOUNT);
308 return false;
309 }
310 else
311 {
312 log<level::INFO>("Auto reboot enabled. "
313 "HOST BOOTCOUNT has an invalid value.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600314 return false;
315 }
Michael Tritz206a8332017-02-06 16:01:23 -0600316 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500317 else
318 {
319 log<level::INFO>("Auto reboot disabled.");
320 return false;
321 }
Michael Tritz206a8332017-02-06 16:01:23 -0600322}
323
Patrick Williamsd22706f2017-05-04 05:42:49 -0500324void Host::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600325{
326 uint32_t newStateID {};
327 sdbusplus::message::object_path newStateObjPath;
328 std::string newStateUnit{};
329 std::string newStateResult{};
330
Andrew Geissler4da7e002017-01-24 15:21:40 -0600331 //Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500332 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600333
334 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600335 (newStateResult == "done") &&
336 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600337 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930338 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600339 this->currentHostState(server::Host::HostState::Off);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600340
341 // Check if we need to start a new transition (i.e. a Reboot)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600342 if(this->server::Host::requestedHostTransition() ==
343 Transition::Reboot)
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600344 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600345 log<level::DEBUG>("Reached intermediate state, going to next");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600346 this->executeTransition(server::Host::Transition::On);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600347 }
Andrew Geissleref621162016-12-08 12:56:21 -0600348 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600349 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600350 (newStateResult == "done") &&
351 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600352 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930353 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600354 this->currentHostState(server::Host::HostState::Running);
355 }
Michael Tritz206a8332017-02-06 16:01:23 -0600356 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500357 (newStateResult == "done") &&
358 (stateActive(HOST_STATE_QUIESCE_TGT)))
Michael Tritz206a8332017-02-06 16:01:23 -0600359 {
360 if (Host::isAutoReboot())
361 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500362 log<level::INFO>("Beginning reboot...");
Michael Tritz206a8332017-02-06 16:01:23 -0600363 Host::requestedHostTransition(server::Host::Transition::Reboot);
364 }
365 else
366 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500367 log<level::INFO>("Maintaining quiesce");
Saqib Khanadaa7212017-02-23 13:19:28 -0600368 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600369 }
370
371 }
Andrew Geissleref621162016-12-08 12:56:21 -0600372}
373
Andrew Geissleref3c1842016-12-01 12:33:09 -0600374Host::Transition Host::requestedHostTransition(Transition value)
375{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600376 log<level::INFO>(
377 "Host State transaction request",
378 entry("REQUESTED_HOST_TRANSITION=%s",
379 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600380
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600381 Transition tranReq = value;
382 if(value == server::Host::Transition::Reboot)
383 {
384 // On reboot requests we just need to do a off if we're on and
385 // vice versa. The handleSysStateChange() code above handles the
386 // second part of the reboot
387 if(this->server::Host::currentHostState() ==
388 server::Host::HostState::Off)
389 {
390 tranReq = server::Host::Transition::On;
391 }
392 else
393 {
394 tranReq = server::Host::Transition::Off;
395 }
396 }
397
398 executeTransition(tranReq);
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500399 auto retVal = server::Host::requestedHostTransition(value);
400 serialize(*this);
401 return retVal;
Andrew Geissleref3c1842016-12-01 12:33:09 -0600402}
403
Andrew Geissleref3c1842016-12-01 12:33:09 -0600404Host::HostState Host::currentHostState(HostState value)
405{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600406 log<level::INFO>("Change to Host State",
407 entry("CURRENT_HOST_STATE=%s",
408 convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600409 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600410}
411
Andrew Geissler36529022016-11-29 15:23:54 -0600412} // namespace manager
413} // namespace state
414} // namepsace phosphor