blob: da4291810a8ef17c3a3ed481958ccf18ac7645d5 [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{
258 sdbusplus::message::variant<std::string> autoRebootParam;
259 std::string strParam;
260
261 std::string HOST_PATH("/org/openbmc/settings/host0");
262 std::string HOST_INTERFACE("org.openbmc.settings.Host");
263
264 auto mapper = this->bus.new_method_call(MAPPER_BUSNAME,
265 MAPPER_PATH,
266 MAPPER_INTERFACE,
267 "GetObject");
268
269 mapper.append(HOST_PATH, std::vector<std::string>({HOST_INTERFACE}));
270 auto mapperResponseMsg = this->bus.call(mapper);
271
272 if (mapperResponseMsg.is_method_error())
273 {
274 log<level::ERR>("Error in mapper call");
275 return false;
276 }
277
278 std::map<std::string, std::vector<std::string>> mapperResponse;
279 mapperResponseMsg.read(mapperResponse);
280 if (mapperResponse.empty())
281 {
282 log<level::ERR>("Error reading mapper response");
283 return false;
284 }
285
286 const auto& host = mapperResponse.begin()->first;
287
288 auto method = this->bus.new_method_call(host.c_str(),
289 HOST_PATH.c_str(),
290 "org.freedesktop.DBus.Properties",
291 "Get");
292
293 method.append(HOST_INTERFACE.c_str(), "auto_reboot");
294 auto reply = this->bus.call(method);
295
296 if (reply.is_method_error())
297 {
298 log<level::ERR>("Error in auto_reboot Get");
299 return false;
300 }
301
302 reply.read(autoRebootParam);
303 strParam =
304 sdbusplus::message::variant_ns::get<std::string>(autoRebootParam);
305
306 if (strParam.empty())
307 {
308 log<level::ERR>("Error reading auto_reboot response");
309 return false;
310 }
311
Saqib Khand5ac6352017-04-04 09:53:59 -0500312 sdbusplus::message::variant<int> rebootCounterParam = 0;
Saqib Khancbe08d12017-03-10 01:29:20 -0600313 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
314 REBOOTCOUNTER_PATH,
315 REBOOTCOUNTER_INTERFACE,
316 "getValue");
317 reply = this->bus.call(method);
318 if (reply.is_method_error())
319 {
320 log<level::ERR>("Error in BOOTCOUNT getValue");
321 return false;
322 }
323 reply.read(rebootCounterParam);
324
Michael Tritz206a8332017-02-06 16:01:23 -0600325 if (strParam == "yes")
326 {
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500327 if ( rebootCounterParam > 0)
Saqib Khancbe08d12017-03-10 01:29:20 -0600328 {
329 // Reduce BOOTCOUNT by 1
Saqib Khand5ac6352017-04-04 09:53:59 -0500330 log<level::INFO>("Auto reboot enabled. "
331 "Reducing HOST BOOTCOUNT by 1.");
332 Host::setHostbootCount((sdbusplus::message::variant_ns::
333 get<int>(rebootCounterParam)) - 1);
Saqib Khancbe08d12017-03-10 01:29:20 -0600334 return true;
335 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500336 else if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600337 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600338 // Reset reboot counter and go to quiesce state
Saqib Khand5ac6352017-04-04 09:53:59 -0500339 log<level::INFO>("Auto reboot enabled. "
340 "HOST BOOTCOUNT already set to 0.");
341 Host::setHostbootCount(DEFAULT_BOOTCOUNT);
342 return false;
343 }
344 else
345 {
346 log<level::INFO>("Auto reboot enabled. "
347 "HOST BOOTCOUNT has an invalid value.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600348 return false;
349 }
Michael Tritz206a8332017-02-06 16:01:23 -0600350 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500351 else
352 {
353 log<level::INFO>("Auto reboot disabled.");
354 return false;
355 }
Michael Tritz206a8332017-02-06 16:01:23 -0600356}
357
Patrick Williamsd22706f2017-05-04 05:42:49 -0500358void Host::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600359{
360 uint32_t newStateID {};
361 sdbusplus::message::object_path newStateObjPath;
362 std::string newStateUnit{};
363 std::string newStateResult{};
364
Andrew Geissler4da7e002017-01-24 15:21:40 -0600365 //Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500366 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600367
368 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600369 (newStateResult == "done") &&
370 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600371 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930372 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600373 this->currentHostState(server::Host::HostState::Off);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600374
375 // Check if we need to start a new transition (i.e. a Reboot)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600376 if(this->server::Host::requestedHostTransition() ==
377 Transition::Reboot)
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600378 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600379 log<level::DEBUG>("Reached intermediate state, going to next");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600380 this->executeTransition(server::Host::Transition::On);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600381 }
Andrew Geissleref621162016-12-08 12:56:21 -0600382 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600383 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600384 (newStateResult == "done") &&
385 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600386 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930387 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600388 this->currentHostState(server::Host::HostState::Running);
389 }
Michael Tritz206a8332017-02-06 16:01:23 -0600390 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500391 (newStateResult == "done") &&
392 (stateActive(HOST_STATE_QUIESCE_TGT)))
Michael Tritz206a8332017-02-06 16:01:23 -0600393 {
394 if (Host::isAutoReboot())
395 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500396 log<level::INFO>("Beginning reboot...");
Michael Tritz206a8332017-02-06 16:01:23 -0600397 Host::requestedHostTransition(server::Host::Transition::Reboot);
398 }
399 else
400 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500401 log<level::INFO>("Maintaining quiesce");
Saqib Khanadaa7212017-02-23 13:19:28 -0600402 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600403 }
404
405 }
Andrew Geissleref621162016-12-08 12:56:21 -0600406}
407
Andrew Geissleref3c1842016-12-01 12:33:09 -0600408Host::Transition Host::requestedHostTransition(Transition value)
409{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600410 log<level::INFO>(
411 "Host State transaction request",
412 entry("REQUESTED_HOST_TRANSITION=%s",
413 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600414
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600415 Transition tranReq = value;
416 if(value == server::Host::Transition::Reboot)
417 {
418 // On reboot requests we just need to do a off if we're on and
419 // vice versa. The handleSysStateChange() code above handles the
420 // second part of the reboot
421 if(this->server::Host::currentHostState() ==
422 server::Host::HostState::Off)
423 {
424 tranReq = server::Host::Transition::On;
425 }
426 else
427 {
428 tranReq = server::Host::Transition::Off;
429 }
430 }
431
432 executeTransition(tranReq);
Dhruvaraj Subhashchandran3f475242017-07-12 00:44:27 -0500433 auto retVal = server::Host::requestedHostTransition(value);
434 serialize(*this);
435 return retVal;
Andrew Geissleref3c1842016-12-01 12:33:09 -0600436}
437
Andrew Geissleref3c1842016-12-01 12:33:09 -0600438Host::HostState Host::currentHostState(HostState value)
439{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600440 log<level::INFO>("Change to Host State",
441 entry("CURRENT_HOST_STATE=%s",
442 convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600443 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600444}
445
Andrew Geissler36529022016-11-29 15:23:54 -0600446} // namespace manager
447} // namespace state
448} // namepsace phosphor