blob: 3e06d73aa3099daff090308cbc8d6c4986ef9e5b [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 Geissler4da7e002017-01-24 15:21:40 -060031 {server::Host::Transition::Off, HOST_STATE_POWEROFF_TGT},
32 {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
39constexpr auto SYSTEM_SERVICE = "org.openbmc.managers.System";
40constexpr auto SYSTEM_OBJ_PATH = "/org/openbmc/managers/System";
41constexpr auto SYSTEM_INTERFACE = SYSTEM_SERVICE;
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
Saqib Khand5ac6352017-04-04 09:53:59 -050054constexpr auto DEFAULT_BOOTCOUNT = 2;
Josh D. King929ef702017-03-02 10:58:11 -060055
Andrew Geissleref621162016-12-08 12:56:21 -060056/* Map a system state to the HostState */
Andrew Geissleref621162016-12-08 12:56:21 -060057const std::map<std::string, server::Host::HostState> SYS_HOST_STATE_TABLE = {
58 {"HOST_BOOTING", server::Host::HostState::Running},
Saqib Khanadaa7212017-02-23 13:19:28 -060059 {"HOST_POWERED_OFF", server::Host::HostState::Off},
60 {"HOST_QUIESCED", server::Host::HostState::Quiesced}
Andrew Geissleref621162016-12-08 12:56:21 -060061};
62
Andrew Geissler4da7e002017-01-24 15:21:40 -060063void Host::subscribeToSystemdSignals()
64{
65 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
66 SYSTEMD_OBJ_PATH,
67 SYSTEMD_INTERFACE,
68 "Subscribe");
69 this->bus.call_noreply(method);
70
71 return;
72}
73
Andrew Geissleref3c1842016-12-01 12:33:09 -060074// TODO - Will be rewritten once sdbusplus client bindings are in place
75// and persistent storage design is in place
76void Host::determineInitialState()
77{
78 std::string sysState;
79
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060080 auto method = this->bus.new_method_call(SYSTEM_SERVICE,
81 SYSTEM_OBJ_PATH,
82 SYSTEM_INTERFACE,
Andrew Geissleref3c1842016-12-01 12:33:09 -060083 "getSystemState");
84
85 auto reply = this->bus.call(method);
86
87 reply.read(sysState);
88
89 if(sysState == "HOST_BOOTED")
90 {
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
106 // Set transition initially to Off
107 // TODO - Eventually need to restore this from persistent storage
Andrew Geissler3e3b84b2016-12-02 15:46:17 -0600108 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600109
110 return;
111}
112
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600113void Host::executeTransition(Transition tranReq)
114{
115 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
116
117 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
118 SYSTEMD_OBJ_PATH,
119 SYSTEMD_INTERFACE,
120 "StartUnit");
121
122 method.append(sysdUnit);
123 method.append("replace");
124
Andrew Geissler4da7e002017-01-24 15:21:40 -0600125 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600126
127 return;
128}
129
Josh D. King929ef702017-03-02 10:58:11 -0600130bool Host::stateActive(const std::string& target)
131{
132 sdbusplus::message::variant<std::string> currentState;
133 sdbusplus::message::object_path unitTargetPath;
134
135 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
136 SYSTEMD_OBJ_PATH,
137 SYSTEMD_INTERFACE,
138 "GetUnit");
139
140 method.append(target);
141 auto result = this->bus.call(method);
142
143 //Check that the bus call didn't result in an error
144 if(result.is_method_error())
145 {
146 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
147 entry(" %s", SYSTEMD_INTERFACE));
148 return false;
149 }
150
151 result.read(unitTargetPath);
152
153 method = this->bus.new_method_call(SYSTEMD_SERVICE,
154 static_cast<const std::string&>
155 (unitTargetPath).c_str(),
156 SYSTEMD_PROPERTY_IFACE,
157 "Get");
158
159 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
160 result = this->bus.call(method);
161
162 //Check that the bus call didn't result in an error
163 if(result.is_method_error())
164 {
165 log<level::ERR>("Error in bus call - could not resolve Get for:",
166 entry(" %s", SYSTEMD_PROPERTY_IFACE));
167 return false;
168 }
169
170 result.read(currentState);
171
172 if(currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
173 {
174 //False - not active
175 return false;
176 }
177 //True - active
178 return true;
179}
180
Saqib Khand5ac6352017-04-04 09:53:59 -0500181void Host::setHostbootCount(int bootCount)
182{
183 auto method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
184 REBOOTCOUNTER_PATH,
185 REBOOTCOUNTER_INTERFACE,
186 "setValue");
187 sdbusplus::message::variant<int> newParam = bootCount;
188 method.append(newParam);
189 this->bus.call_noreply(method);
190}
Josh D. King929ef702017-03-02 10:58:11 -0600191
Michael Tritz206a8332017-02-06 16:01:23 -0600192bool Host::isAutoReboot()
193{
194 sdbusplus::message::variant<std::string> autoRebootParam;
195 std::string strParam;
196
197 std::string HOST_PATH("/org/openbmc/settings/host0");
198 std::string HOST_INTERFACE("org.openbmc.settings.Host");
199
200 auto mapper = this->bus.new_method_call(MAPPER_BUSNAME,
201 MAPPER_PATH,
202 MAPPER_INTERFACE,
203 "GetObject");
204
205 mapper.append(HOST_PATH, std::vector<std::string>({HOST_INTERFACE}));
206 auto mapperResponseMsg = this->bus.call(mapper);
207
208 if (mapperResponseMsg.is_method_error())
209 {
210 log<level::ERR>("Error in mapper call");
211 return false;
212 }
213
214 std::map<std::string, std::vector<std::string>> mapperResponse;
215 mapperResponseMsg.read(mapperResponse);
216 if (mapperResponse.empty())
217 {
218 log<level::ERR>("Error reading mapper response");
219 return false;
220 }
221
222 const auto& host = mapperResponse.begin()->first;
223
224 auto method = this->bus.new_method_call(host.c_str(),
225 HOST_PATH.c_str(),
226 "org.freedesktop.DBus.Properties",
227 "Get");
228
229 method.append(HOST_INTERFACE.c_str(), "auto_reboot");
230 auto reply = this->bus.call(method);
231
232 if (reply.is_method_error())
233 {
234 log<level::ERR>("Error in auto_reboot Get");
235 return false;
236 }
237
238 reply.read(autoRebootParam);
239 strParam =
240 sdbusplus::message::variant_ns::get<std::string>(autoRebootParam);
241
242 if (strParam.empty())
243 {
244 log<level::ERR>("Error reading auto_reboot response");
245 return false;
246 }
247
Saqib Khand5ac6352017-04-04 09:53:59 -0500248 sdbusplus::message::variant<int> rebootCounterParam = 0;
Saqib Khancbe08d12017-03-10 01:29:20 -0600249 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
250 REBOOTCOUNTER_PATH,
251 REBOOTCOUNTER_INTERFACE,
252 "getValue");
253 reply = this->bus.call(method);
254 if (reply.is_method_error())
255 {
256 log<level::ERR>("Error in BOOTCOUNT getValue");
257 return false;
258 }
259 reply.read(rebootCounterParam);
260
Michael Tritz206a8332017-02-06 16:01:23 -0600261 if (strParam == "yes")
262 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600263 if( rebootCounterParam > 0)
264 {
265 // Reduce BOOTCOUNT by 1
Saqib Khand5ac6352017-04-04 09:53:59 -0500266 log<level::INFO>("Auto reboot enabled. "
267 "Reducing HOST BOOTCOUNT by 1.");
268 Host::setHostbootCount((sdbusplus::message::variant_ns::
269 get<int>(rebootCounterParam)) - 1);
Saqib Khancbe08d12017-03-10 01:29:20 -0600270 return true;
271 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500272 else if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600273 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600274 // Reset reboot counter and go to quiesce state
Saqib Khand5ac6352017-04-04 09:53:59 -0500275 log<level::INFO>("Auto reboot enabled. "
276 "HOST BOOTCOUNT already set to 0.");
277 Host::setHostbootCount(DEFAULT_BOOTCOUNT);
278 return false;
279 }
280 else
281 {
282 log<level::INFO>("Auto reboot enabled. "
283 "HOST BOOTCOUNT has an invalid value.");
Saqib Khancbe08d12017-03-10 01:29:20 -0600284 return false;
285 }
Michael Tritz206a8332017-02-06 16:01:23 -0600286 }
Saqib Khand5ac6352017-04-04 09:53:59 -0500287 else
288 {
289 log<level::INFO>("Auto reboot disabled.");
290 return false;
291 }
Michael Tritz206a8332017-02-06 16:01:23 -0600292}
293
Andrew Geissler4da7e002017-01-24 15:21:40 -0600294int Host::sysStateChangeSignal(sd_bus_message *msg, void *userData,
295 sd_bus_error *retError)
Andrew Geissleref621162016-12-08 12:56:21 -0600296{
Andrew Geissler4da7e002017-01-24 15:21:40 -0600297 return static_cast<Host*>(userData)->sysStateChange(msg, retError);
298}
Andrew Geissleref621162016-12-08 12:56:21 -0600299
Andrew Geissler4da7e002017-01-24 15:21:40 -0600300int Host::sysStateChange(sd_bus_message* msg,
301 sd_bus_error* retError)
302{
303 uint32_t newStateID {};
304 sdbusplus::message::object_path newStateObjPath;
305 std::string newStateUnit{};
306 std::string newStateResult{};
307
308 auto sdPlusMsg = sdbusplus::message::message(msg);
309 //Read the msg and populate each variable
310 sdPlusMsg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
311
312 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600313 (newStateResult == "done") &&
314 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600315 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930316 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600317 this->currentHostState(server::Host::HostState::Off);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600318
319 // Check if we need to start a new transition (i.e. a Reboot)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600320 if(this->server::Host::requestedHostTransition() ==
321 Transition::Reboot)
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600322 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600323 log<level::DEBUG>("Reached intermediate state, going to next");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600324 this->executeTransition(server::Host::Transition::On);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600325 }
Andrew Geissleref621162016-12-08 12:56:21 -0600326 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600327 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600328 (newStateResult == "done") &&
329 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600330 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930331 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600332 this->currentHostState(server::Host::HostState::Running);
333 }
Michael Tritz206a8332017-02-06 16:01:23 -0600334 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
335 (newStateResult == "done"))
336 {
337 if (Host::isAutoReboot())
338 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500339 log<level::INFO>("Beginning reboot...");
Michael Tritz206a8332017-02-06 16:01:23 -0600340 Host::requestedHostTransition(server::Host::Transition::Reboot);
341 }
342 else
343 {
Saqib Khand5ac6352017-04-04 09:53:59 -0500344 log<level::INFO>("Maintaining quiesce");
Saqib Khanadaa7212017-02-23 13:19:28 -0600345 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600346 }
347
348 }
Andrew Geissleref621162016-12-08 12:56:21 -0600349
350 return 0;
351}
352
Andrew Geissleref3c1842016-12-01 12:33:09 -0600353Host::Transition Host::requestedHostTransition(Transition value)
354{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600355 log<level::INFO>(
356 "Host State transaction request",
357 entry("REQUESTED_HOST_TRANSITION=%s",
358 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600359
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600360 Transition tranReq = value;
361 if(value == server::Host::Transition::Reboot)
362 {
363 // On reboot requests we just need to do a off if we're on and
364 // vice versa. The handleSysStateChange() code above handles the
365 // second part of the reboot
366 if(this->server::Host::currentHostState() ==
367 server::Host::HostState::Off)
368 {
369 tranReq = server::Host::Transition::On;
370 }
371 else
372 {
373 tranReq = server::Host::Transition::Off;
374 }
375 }
376
377 executeTransition(tranReq);
Andrew Geissler3e3b84b2016-12-02 15:46:17 -0600378 return server::Host::requestedHostTransition(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600379}
380
Andrew Geissleref3c1842016-12-01 12:33:09 -0600381Host::HostState Host::currentHostState(HostState value)
382{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600383 log<level::INFO>("Change to Host State",
384 entry("CURRENT_HOST_STATE=%s",
385 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
391} // namepsace phosphor