blob: fd1d05624c52fe966c4ebc736b373db6ae1c5f6c [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>
Andrew Geissler36529022016-11-29 15:23:54 -06007#include "host_state_manager.hpp"
8
9namespace phosphor
10{
11namespace state
12{
13namespace manager
14{
15
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060016// When you see server:: you know we're referencing our base class
17namespace server = sdbusplus::xyz::openbmc_project::State::server;
18
Andrew Geissler1e3bf942016-12-13 15:32:22 -060019using namespace phosphor::logging;
20
Andrew Geissler4f309e82017-05-24 15:18:01 -050021// host-shutdown notifies host of shutdown and that leads to host-stop being
22// called so initiate a host shutdown with the -shutdown target and consider the
23// host shut down when the -stop target is complete
24constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target";
Josh D. Kingca357922017-04-11 13:44:09 -050025constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
26constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050027constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060028
Josh D. King929ef702017-03-02 10:58:11 -060029constexpr auto ACTIVE_STATE = "active";
30constexpr auto ACTIVATING_STATE = "activating";
31
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060032/* Map a transition to it's systemd target */
33const std::map<server::Host::Transition,std::string> SYSTEMD_TARGET_TABLE =
34{
Andrew Geissler4f309e82017-05-24 15:18:01 -050035 {server::Host::Transition::Off, HOST_STATE_SOFT_POWEROFF_TGT},
Andrew Geissler4da7e002017-01-24 15:21:40 -060036 {server::Host::Transition::On, HOST_STATE_POWERON_TGT}
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060037};
38
39constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
40constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
41constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
42
Michael Tritz206a8332017-02-06 16:01:23 -060043constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
Leonel Gonzalezf318d872017-03-16 13:47:49 -050044constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
Michael Tritz206a8332017-02-06 16:01:23 -060045constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
46
Saqib Khancbe08d12017-03-10 01:29:20 -060047constexpr auto REBOOTCOUNTER_SERVICE("org.openbmc.Sensors");
48constexpr auto REBOOTCOUNTER_PATH("/org/openbmc/sensors/host/BootCount");
49constexpr auto REBOOTCOUNTER_INTERFACE("org.openbmc.SensorValue");
50
Josh D. King929ef702017-03-02 10:58:11 -060051constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
52constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
53
Andrew Geissler48696ce2017-05-22 14:41:58 -050054// TODO openbmc/openbmc#1646 - boot count needs to be defined in 1 place
55constexpr auto DEFAULT_BOOTCOUNT = 3;
Josh D. King929ef702017-03-02 10:58:11 -060056
Andrew Geissleref621162016-12-08 12:56:21 -060057/* Map a system state to the HostState */
Andrew Geissleref621162016-12-08 12:56:21 -060058const std::map<std::string, server::Host::HostState> SYS_HOST_STATE_TABLE = {
59 {"HOST_BOOTING", server::Host::HostState::Running},
Saqib Khanadaa7212017-02-23 13:19:28 -060060 {"HOST_POWERED_OFF", server::Host::HostState::Off},
61 {"HOST_QUIESCED", server::Host::HostState::Quiesced}
Andrew Geissleref621162016-12-08 12:56:21 -060062};
63
Andrew Geissler4da7e002017-01-24 15:21:40 -060064void Host::subscribeToSystemdSignals()
65{
66 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
67 SYSTEMD_OBJ_PATH,
68 SYSTEMD_INTERFACE,
69 "Subscribe");
70 this->bus.call_noreply(method);
71
72 return;
73}
74
Andrew Geissleref3c1842016-12-01 12:33:09 -060075void Host::determineInitialState()
76{
Andrew Geissleref3c1842016-12-01 12:33:09 -060077
Josh D. King222014e2017-05-16 13:35:21 -050078 if(stateActive(HOST_STATE_POWERON_TGT))
Andrew Geissleref3c1842016-12-01 12:33:09 -060079 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060080 log<level::INFO>("Initial Host State will be Running",
81 entry("CURRENT_HOST_STATE=%s",
82 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060083 server::Host::currentHostState(HostState::Running);
84 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -060085 }
86 else
87 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060088 log<level::INFO>("Initial Host State will be Off",
89 entry("CURRENT_HOST_STATE=%s",
90 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060091 server::Host::currentHostState(HostState::Off);
92 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -060093 }
94
95 // Set transition initially to Off
96 // TODO - Eventually need to restore this from persistent storage
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060097 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -060098
99 return;
100}
101
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600102void Host::executeTransition(Transition tranReq)
103{
104 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
105
106 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
107 SYSTEMD_OBJ_PATH,
108 SYSTEMD_INTERFACE,
109 "StartUnit");
110
111 method.append(sysdUnit);
112 method.append("replace");
113
Andrew Geissler4da7e002017-01-24 15:21:40 -0600114 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600115
116 return;
117}
118
Josh D. King929ef702017-03-02 10:58:11 -0600119bool Host::stateActive(const std::string& target)
120{
121 sdbusplus::message::variant<std::string> currentState;
122 sdbusplus::message::object_path unitTargetPath;
123
124 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
125 SYSTEMD_OBJ_PATH,
126 SYSTEMD_INTERFACE,
127 "GetUnit");
128
129 method.append(target);
130 auto result = this->bus.call(method);
131
132 //Check that the bus call didn't result in an error
133 if(result.is_method_error())
134 {
135 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
136 entry(" %s", SYSTEMD_INTERFACE));
137 return false;
138 }
139
140 result.read(unitTargetPath);
141
142 method = this->bus.new_method_call(SYSTEMD_SERVICE,
143 static_cast<const std::string&>
144 (unitTargetPath).c_str(),
145 SYSTEMD_PROPERTY_IFACE,
146 "Get");
147
148 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
149 result = this->bus.call(method);
150
151 //Check that the bus call didn't result in an error
152 if(result.is_method_error())
153 {
154 log<level::ERR>("Error in bus call - could not resolve Get for:",
155 entry(" %s", SYSTEMD_PROPERTY_IFACE));
156 return false;
157 }
158
159 result.read(currentState);
160
161 if(currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
162 {
163 //False - not active
164 return false;
165 }
166 //True - active
167 return true;
168}
169
Saqib Khand5ac6352017-04-04 09:53:59 -0500170void Host::setHostbootCount(int bootCount)
171{
172 auto method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
173 REBOOTCOUNTER_PATH,
174 REBOOTCOUNTER_INTERFACE,
175 "setValue");
176 sdbusplus::message::variant<int> newParam = bootCount;
177 method.append(newParam);
178 this->bus.call_noreply(method);
179}
Josh D. King929ef702017-03-02 10:58:11 -0600180
Michael Tritz206a8332017-02-06 16:01:23 -0600181bool Host::isAutoReboot()
182{
183 sdbusplus::message::variant<std::string> autoRebootParam;
184 std::string strParam;
185
186 std::string HOST_PATH("/org/openbmc/settings/host0");
187 std::string HOST_INTERFACE("org.openbmc.settings.Host");
188
189 auto mapper = this->bus.new_method_call(MAPPER_BUSNAME,
190 MAPPER_PATH,
191 MAPPER_INTERFACE,
192 "GetObject");
193
194 mapper.append(HOST_PATH, std::vector<std::string>({HOST_INTERFACE}));
195 auto mapperResponseMsg = this->bus.call(mapper);
196
197 if (mapperResponseMsg.is_method_error())
198 {
199 log<level::ERR>("Error in mapper call");
200 return false;
201 }
202
203 std::map<std::string, std::vector<std::string>> mapperResponse;
204 mapperResponseMsg.read(mapperResponse);
205 if (mapperResponse.empty())
206 {
207 log<level::ERR>("Error reading mapper response");
208 return false;
209 }
210
211 const auto& host = mapperResponse.begin()->first;
212
213 auto method = this->bus.new_method_call(host.c_str(),
214 HOST_PATH.c_str(),
215 "org.freedesktop.DBus.Properties",
216 "Get");
217
218 method.append(HOST_INTERFACE.c_str(), "auto_reboot");
219 auto reply = this->bus.call(method);
220
221 if (reply.is_method_error())
222 {
223 log<level::ERR>("Error in auto_reboot Get");
224 return false;
225 }
226
227 reply.read(autoRebootParam);
228 strParam =
229 sdbusplus::message::variant_ns::get<std::string>(autoRebootParam);
230
231 if (strParam.empty())
232 {
233 log<level::ERR>("Error reading auto_reboot response");
234 return false;
235 }
236
Saqib Khand5ac6352017-04-04 09:53:59 -0500237 sdbusplus::message::variant<int> rebootCounterParam = 0;
Saqib Khancbe08d12017-03-10 01:29:20 -0600238 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
239 REBOOTCOUNTER_PATH,
240 REBOOTCOUNTER_INTERFACE,
241 "getValue");
242 reply = this->bus.call(method);
243 if (reply.is_method_error())
244 {
245 log<level::ERR>("Error in BOOTCOUNT getValue");
246 return false;
247 }
248 reply.read(rebootCounterParam);
249
Michael Tritz206a8332017-02-06 16:01:23 -0600250 if (strParam == "yes")
251 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600252 if( rebootCounterParam > 0)
253 {
254 // Reduce BOOTCOUNT by 1
Saqib Khand5ac6352017-04-04 09:53:59 -0500255 log<level::INFO>("Auto reboot enabled. "
256 "Reducing HOST BOOTCOUNT by 1.");
257 Host::setHostbootCount((sdbusplus::message::variant_ns::
258 get<int>(rebootCounterParam)) - 1);
Saqib Khancbe08d12017-03-10 01:29:20 -0600259 return true;
260 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500261 else if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600262 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600263 // Reset reboot counter and go to quiesce state
Saqib Khand5ac6352017-04-04 09:53:59 -0500264 log<level::INFO>("Auto reboot enabled. "
265 "HOST BOOTCOUNT already set to 0.");
266 Host::setHostbootCount(DEFAULT_BOOTCOUNT);
267 return false;
268 }
269 else
270 {
271 log<level::INFO>("Auto reboot enabled. "
272 "HOST BOOTCOUNT has an invalid value.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600273 return false;
274 }
Michael Tritz206a8332017-02-06 16:01:23 -0600275 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500276 else
277 {
278 log<level::INFO>("Auto reboot disabled.");
279 return false;
280 }
Michael Tritz206a8332017-02-06 16:01:23 -0600281}
282
Patrick Williamsd22706f2017-05-04 05:42:49 -0500283void Host::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600284{
285 uint32_t newStateID {};
286 sdbusplus::message::object_path newStateObjPath;
287 std::string newStateUnit{};
288 std::string newStateResult{};
289
Andrew Geissler4da7e002017-01-24 15:21:40 -0600290 //Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500291 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600292
293 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600294 (newStateResult == "done") &&
295 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600296 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930297 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600298 this->currentHostState(server::Host::HostState::Off);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600299
300 // Check if we need to start a new transition (i.e. a Reboot)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600301 if(this->server::Host::requestedHostTransition() ==
302 Transition::Reboot)
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600303 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600304 log<level::DEBUG>("Reached intermediate state, going to next");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600305 this->executeTransition(server::Host::Transition::On);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600306 }
Andrew Geissleref621162016-12-08 12:56:21 -0600307 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600308 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600309 (newStateResult == "done") &&
310 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600311 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930312 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600313 this->currentHostState(server::Host::HostState::Running);
314 }
Michael Tritz206a8332017-02-06 16:01:23 -0600315 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500316 (newStateResult == "done") &&
317 (stateActive(HOST_STATE_QUIESCE_TGT)))
Michael Tritz206a8332017-02-06 16:01:23 -0600318 {
319 if (Host::isAutoReboot())
320 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500321 log<level::INFO>("Beginning reboot...");
Michael Tritz206a8332017-02-06 16:01:23 -0600322 Host::requestedHostTransition(server::Host::Transition::Reboot);
323 }
324 else
325 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500326 log<level::INFO>("Maintaining quiesce");
Saqib Khanadaa7212017-02-23 13:19:28 -0600327 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600328 }
329
330 }
Andrew Geissleref621162016-12-08 12:56:21 -0600331}
332
Andrew Geissleref3c1842016-12-01 12:33:09 -0600333Host::Transition Host::requestedHostTransition(Transition value)
334{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600335 log<level::INFO>(
336 "Host State transaction request",
337 entry("REQUESTED_HOST_TRANSITION=%s",
338 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600339
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600340 Transition tranReq = value;
341 if(value == server::Host::Transition::Reboot)
342 {
343 // On reboot requests we just need to do a off if we're on and
344 // vice versa. The handleSysStateChange() code above handles the
345 // second part of the reboot
346 if(this->server::Host::currentHostState() ==
347 server::Host::HostState::Off)
348 {
349 tranReq = server::Host::Transition::On;
350 }
351 else
352 {
353 tranReq = server::Host::Transition::Off;
354 }
355 }
356
357 executeTransition(tranReq);
Andrew Geissler3e3b84b2016-12-02 15:46:17 -0600358 return server::Host::requestedHostTransition(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600359}
360
Andrew Geissleref3c1842016-12-01 12:33:09 -0600361Host::HostState Host::currentHostState(HostState value)
362{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600363 log<level::INFO>("Change to Host State",
364 entry("CURRENT_HOST_STATE=%s",
365 convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600366 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600367}
368
Andrew Geissler36529022016-11-29 15:23:54 -0600369} // namespace manager
370} // namespace state
371} // namepsace phosphor