blob: f33c8ade343112e7a75a63516ae6ad5e2eb44822 [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
Josh D. Kingca357922017-04-11 13:44:09 -050021constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target";
22constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target";
Josh D. Kingcc3fb5d2017-04-19 15:45:10 -050023constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
Andrew Geissler4da7e002017-01-24 15:21:40 -060024
Josh D. King929ef702017-03-02 10:58:11 -060025constexpr auto ACTIVE_STATE = "active";
26constexpr auto ACTIVATING_STATE = "activating";
27
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060028/* Map a transition to it's systemd target */
29const std::map<server::Host::Transition,std::string> SYSTEMD_TARGET_TABLE =
30{
Andrew Geissler134eb582017-05-24 13:36:08 -050031 {server::Host::Transition::Off, HOST_STATE_POWEROFF_TGT},
Andrew Geissler4da7e002017-01-24 15:21:40 -060032 {server::Host::Transition::On, HOST_STATE_POWERON_TGT}
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060033};
34
35constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
36constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
37constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
38
Michael Tritz206a8332017-02-06 16:01:23 -060039constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
Leonel Gonzalezf318d872017-03-16 13:47:49 -050040constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
Michael Tritz206a8332017-02-06 16:01:23 -060041constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
42
Saqib Khancbe08d12017-03-10 01:29:20 -060043constexpr auto REBOOTCOUNTER_SERVICE("org.openbmc.Sensors");
44constexpr auto REBOOTCOUNTER_PATH("/org/openbmc/sensors/host/BootCount");
45constexpr auto REBOOTCOUNTER_INTERFACE("org.openbmc.SensorValue");
46
Josh D. King929ef702017-03-02 10:58:11 -060047constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
48constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
49
Andrew Geissler48696ce2017-05-22 14:41:58 -050050// TODO openbmc/openbmc#1646 - boot count needs to be defined in 1 place
51constexpr auto DEFAULT_BOOTCOUNT = 3;
Josh D. King929ef702017-03-02 10:58:11 -060052
Andrew Geissleref621162016-12-08 12:56:21 -060053/* Map a system state to the HostState */
Andrew Geissleref621162016-12-08 12:56:21 -060054const std::map<std::string, server::Host::HostState> SYS_HOST_STATE_TABLE = {
55 {"HOST_BOOTING", server::Host::HostState::Running},
Saqib Khanadaa7212017-02-23 13:19:28 -060056 {"HOST_POWERED_OFF", server::Host::HostState::Off},
57 {"HOST_QUIESCED", server::Host::HostState::Quiesced}
Andrew Geissleref621162016-12-08 12:56:21 -060058};
59
Andrew Geissler4da7e002017-01-24 15:21:40 -060060void Host::subscribeToSystemdSignals()
61{
62 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
63 SYSTEMD_OBJ_PATH,
64 SYSTEMD_INTERFACE,
65 "Subscribe");
66 this->bus.call_noreply(method);
67
68 return;
69}
70
Andrew Geissleref3c1842016-12-01 12:33:09 -060071void Host::determineInitialState()
72{
Andrew Geissleref3c1842016-12-01 12:33:09 -060073
Josh D. King222014e2017-05-16 13:35:21 -050074 if(stateActive(HOST_STATE_POWERON_TGT))
Andrew Geissleref3c1842016-12-01 12:33:09 -060075 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060076 log<level::INFO>("Initial Host State will be Running",
77 entry("CURRENT_HOST_STATE=%s",
78 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060079 server::Host::currentHostState(HostState::Running);
80 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -060081 }
82 else
83 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060084 log<level::INFO>("Initial Host State will be Off",
85 entry("CURRENT_HOST_STATE=%s",
86 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060087 server::Host::currentHostState(HostState::Off);
88 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -060089 }
90
91 // Set transition initially to Off
92 // TODO - Eventually need to restore this from persistent storage
Andrew Geissler3e3b84b2016-12-02 15:46:17 -060093 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -060094
95 return;
96}
97
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060098void Host::executeTransition(Transition tranReq)
99{
100 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
101
102 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
103 SYSTEMD_OBJ_PATH,
104 SYSTEMD_INTERFACE,
105 "StartUnit");
106
107 method.append(sysdUnit);
108 method.append("replace");
109
Andrew Geissler4da7e002017-01-24 15:21:40 -0600110 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600111
112 return;
113}
114
Josh D. King929ef702017-03-02 10:58:11 -0600115bool Host::stateActive(const std::string& target)
116{
117 sdbusplus::message::variant<std::string> currentState;
118 sdbusplus::message::object_path unitTargetPath;
119
120 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
121 SYSTEMD_OBJ_PATH,
122 SYSTEMD_INTERFACE,
123 "GetUnit");
124
125 method.append(target);
126 auto result = this->bus.call(method);
127
128 //Check that the bus call didn't result in an error
129 if(result.is_method_error())
130 {
131 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
132 entry(" %s", SYSTEMD_INTERFACE));
133 return false;
134 }
135
136 result.read(unitTargetPath);
137
138 method = this->bus.new_method_call(SYSTEMD_SERVICE,
139 static_cast<const std::string&>
140 (unitTargetPath).c_str(),
141 SYSTEMD_PROPERTY_IFACE,
142 "Get");
143
144 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
145 result = this->bus.call(method);
146
147 //Check that the bus call didn't result in an error
148 if(result.is_method_error())
149 {
150 log<level::ERR>("Error in bus call - could not resolve Get for:",
151 entry(" %s", SYSTEMD_PROPERTY_IFACE));
152 return false;
153 }
154
155 result.read(currentState);
156
157 if(currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
158 {
159 //False - not active
160 return false;
161 }
162 //True - active
163 return true;
164}
165
Saqib Khand5ac6352017-04-04 09:53:59 -0500166void Host::setHostbootCount(int bootCount)
167{
168 auto method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
169 REBOOTCOUNTER_PATH,
170 REBOOTCOUNTER_INTERFACE,
171 "setValue");
172 sdbusplus::message::variant<int> newParam = bootCount;
173 method.append(newParam);
174 this->bus.call_noreply(method);
175}
Josh D. King929ef702017-03-02 10:58:11 -0600176
Michael Tritz206a8332017-02-06 16:01:23 -0600177bool Host::isAutoReboot()
178{
179 sdbusplus::message::variant<std::string> autoRebootParam;
180 std::string strParam;
181
182 std::string HOST_PATH("/org/openbmc/settings/host0");
183 std::string HOST_INTERFACE("org.openbmc.settings.Host");
184
185 auto mapper = this->bus.new_method_call(MAPPER_BUSNAME,
186 MAPPER_PATH,
187 MAPPER_INTERFACE,
188 "GetObject");
189
190 mapper.append(HOST_PATH, std::vector<std::string>({HOST_INTERFACE}));
191 auto mapperResponseMsg = this->bus.call(mapper);
192
193 if (mapperResponseMsg.is_method_error())
194 {
195 log<level::ERR>("Error in mapper call");
196 return false;
197 }
198
199 std::map<std::string, std::vector<std::string>> mapperResponse;
200 mapperResponseMsg.read(mapperResponse);
201 if (mapperResponse.empty())
202 {
203 log<level::ERR>("Error reading mapper response");
204 return false;
205 }
206
207 const auto& host = mapperResponse.begin()->first;
208
209 auto method = this->bus.new_method_call(host.c_str(),
210 HOST_PATH.c_str(),
211 "org.freedesktop.DBus.Properties",
212 "Get");
213
214 method.append(HOST_INTERFACE.c_str(), "auto_reboot");
215 auto reply = this->bus.call(method);
216
217 if (reply.is_method_error())
218 {
219 log<level::ERR>("Error in auto_reboot Get");
220 return false;
221 }
222
223 reply.read(autoRebootParam);
224 strParam =
225 sdbusplus::message::variant_ns::get<std::string>(autoRebootParam);
226
227 if (strParam.empty())
228 {
229 log<level::ERR>("Error reading auto_reboot response");
230 return false;
231 }
232
Saqib Khand5ac6352017-04-04 09:53:59 -0500233 sdbusplus::message::variant<int> rebootCounterParam = 0;
Saqib Khancbe08d12017-03-10 01:29:20 -0600234 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
235 REBOOTCOUNTER_PATH,
236 REBOOTCOUNTER_INTERFACE,
237 "getValue");
238 reply = this->bus.call(method);
239 if (reply.is_method_error())
240 {
241 log<level::ERR>("Error in BOOTCOUNT getValue");
242 return false;
243 }
244 reply.read(rebootCounterParam);
245
Michael Tritz206a8332017-02-06 16:01:23 -0600246 if (strParam == "yes")
247 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600248 if( rebootCounterParam > 0)
249 {
250 // Reduce BOOTCOUNT by 1
Saqib Khand5ac6352017-04-04 09:53:59 -0500251 log<level::INFO>("Auto reboot enabled. "
252 "Reducing HOST BOOTCOUNT by 1.");
253 Host::setHostbootCount((sdbusplus::message::variant_ns::
254 get<int>(rebootCounterParam)) - 1);
Saqib Khancbe08d12017-03-10 01:29:20 -0600255 return true;
256 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500257 else if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600258 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600259 // Reset reboot counter and go to quiesce state
Saqib Khand5ac6352017-04-04 09:53:59 -0500260 log<level::INFO>("Auto reboot enabled. "
261 "HOST BOOTCOUNT already set to 0.");
262 Host::setHostbootCount(DEFAULT_BOOTCOUNT);
263 return false;
264 }
265 else
266 {
267 log<level::INFO>("Auto reboot enabled. "
268 "HOST BOOTCOUNT has an invalid value.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600269 return false;
270 }
Michael Tritz206a8332017-02-06 16:01:23 -0600271 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500272 else
273 {
274 log<level::INFO>("Auto reboot disabled.");
275 return false;
276 }
Michael Tritz206a8332017-02-06 16:01:23 -0600277}
278
Patrick Williamsd22706f2017-05-04 05:42:49 -0500279void Host::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600280{
281 uint32_t newStateID {};
282 sdbusplus::message::object_path newStateObjPath;
283 std::string newStateUnit{};
284 std::string newStateResult{};
285
Andrew Geissler4da7e002017-01-24 15:21:40 -0600286 //Read the msg and populate each variable
Patrick Williamsd22706f2017-05-04 05:42:49 -0500287 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Andrew Geissler4da7e002017-01-24 15:21:40 -0600288
289 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600290 (newStateResult == "done") &&
291 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600292 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930293 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600294 this->currentHostState(server::Host::HostState::Off);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600295
296 // Check if we need to start a new transition (i.e. a Reboot)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600297 if(this->server::Host::requestedHostTransition() ==
298 Transition::Reboot)
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600299 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600300 log<level::DEBUG>("Reached intermediate state, going to next");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600301 this->executeTransition(server::Host::Transition::On);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600302 }
Andrew Geissleref621162016-12-08 12:56:21 -0600303 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600304 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600305 (newStateResult == "done") &&
306 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600307 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930308 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600309 this->currentHostState(server::Host::HostState::Running);
310 }
Michael Tritz206a8332017-02-06 16:01:23 -0600311 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
Josh D. King29d025d2017-04-27 12:40:22 -0500312 (newStateResult == "done") &&
313 (stateActive(HOST_STATE_QUIESCE_TGT)))
Michael Tritz206a8332017-02-06 16:01:23 -0600314 {
315 if (Host::isAutoReboot())
316 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500317 log<level::INFO>("Beginning reboot...");
Michael Tritz206a8332017-02-06 16:01:23 -0600318 Host::requestedHostTransition(server::Host::Transition::Reboot);
319 }
320 else
321 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500322 log<level::INFO>("Maintaining quiesce");
Saqib Khanadaa7212017-02-23 13:19:28 -0600323 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600324 }
325
326 }
Andrew Geissleref621162016-12-08 12:56:21 -0600327}
328
Andrew Geissleref3c1842016-12-01 12:33:09 -0600329Host::Transition Host::requestedHostTransition(Transition value)
330{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600331 log<level::INFO>(
332 "Host State transaction request",
333 entry("REQUESTED_HOST_TRANSITION=%s",
334 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600335
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600336 Transition tranReq = value;
337 if(value == server::Host::Transition::Reboot)
338 {
339 // On reboot requests we just need to do a off if we're on and
340 // vice versa. The handleSysStateChange() code above handles the
341 // second part of the reboot
342 if(this->server::Host::currentHostState() ==
343 server::Host::HostState::Off)
344 {
345 tranReq = server::Host::Transition::On;
346 }
347 else
348 {
349 tranReq = server::Host::Transition::Off;
350 }
351 }
352
353 executeTransition(tranReq);
Andrew Geissler3e3b84b2016-12-02 15:46:17 -0600354 return server::Host::requestedHostTransition(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600355}
356
Andrew Geissleref3c1842016-12-01 12:33:09 -0600357Host::HostState Host::currentHostState(HostState value)
358{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600359 log<level::INFO>("Change to Host State",
360 entry("CURRENT_HOST_STATE=%s",
361 convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600362 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600363}
364
Andrew Geissler36529022016-11-29 15:23:54 -0600365} // namespace manager
366} // namespace state
367} // namepsace phosphor