blob: 49fb910604c7b49d05adb97646fbfe28aa5bdb18 [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 Khancbe08d12017-03-10 01:29:20 -060054const sdbusplus::message::variant<int> DEFAULT_BOOTCOUNT = 2;
55
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 -060075// TODO - Will be rewritten once sdbusplus client bindings are in place
76// and persistent storage design is in place
77void Host::determineInitialState()
78{
79 std::string sysState;
80
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -060081 auto method = this->bus.new_method_call(SYSTEM_SERVICE,
82 SYSTEM_OBJ_PATH,
83 SYSTEM_INTERFACE,
Andrew Geissleref3c1842016-12-01 12:33:09 -060084 "getSystemState");
85
86 auto reply = this->bus.call(method);
87
88 reply.read(sysState);
89
90 if(sysState == "HOST_BOOTED")
91 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -060092 log<level::INFO>("Initial Host State will be Running",
93 entry("CURRENT_HOST_STATE=%s",
94 convertForMessage(HostState::Running).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -060095 server::Host::currentHostState(HostState::Running);
96 server::Host::requestedHostTransition(Transition::On);
Andrew Geissleref3c1842016-12-01 12:33:09 -060097 }
98 else
99 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600100 log<level::INFO>("Initial Host State will be Off",
101 entry("CURRENT_HOST_STATE=%s",
102 convertForMessage(HostState::Off).c_str()));
Andrew Geissler97924142017-01-24 16:10:18 -0600103 server::Host::currentHostState(HostState::Off);
104 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600105 }
106
107 // Set transition initially to Off
108 // TODO - Eventually need to restore this from persistent storage
Andrew Geissler3e3b84b2016-12-02 15:46:17 -0600109 server::Host::requestedHostTransition(Transition::Off);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600110
111 return;
112}
113
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600114void Host::executeTransition(Transition tranReq)
115{
116 auto sysdUnit = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
117
118 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
119 SYSTEMD_OBJ_PATH,
120 SYSTEMD_INTERFACE,
121 "StartUnit");
122
123 method.append(sysdUnit);
124 method.append("replace");
125
Andrew Geissler4da7e002017-01-24 15:21:40 -0600126 this->bus.call_noreply(method);
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600127
128 return;
129}
130
Josh D. King929ef702017-03-02 10:58:11 -0600131bool Host::stateActive(const std::string& target)
132{
133 sdbusplus::message::variant<std::string> currentState;
134 sdbusplus::message::object_path unitTargetPath;
135
136 auto method = this->bus.new_method_call(SYSTEMD_SERVICE,
137 SYSTEMD_OBJ_PATH,
138 SYSTEMD_INTERFACE,
139 "GetUnit");
140
141 method.append(target);
142 auto result = this->bus.call(method);
143
144 //Check that the bus call didn't result in an error
145 if(result.is_method_error())
146 {
147 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
148 entry(" %s", SYSTEMD_INTERFACE));
149 return false;
150 }
151
152 result.read(unitTargetPath);
153
154 method = this->bus.new_method_call(SYSTEMD_SERVICE,
155 static_cast<const std::string&>
156 (unitTargetPath).c_str(),
157 SYSTEMD_PROPERTY_IFACE,
158 "Get");
159
160 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
161 result = this->bus.call(method);
162
163 //Check that the bus call didn't result in an error
164 if(result.is_method_error())
165 {
166 log<level::ERR>("Error in bus call - could not resolve Get for:",
167 entry(" %s", SYSTEMD_PROPERTY_IFACE));
168 return false;
169 }
170
171 result.read(currentState);
172
173 if(currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
174 {
175 //False - not active
176 return false;
177 }
178 //True - active
179 return true;
180}
181
182
Michael Tritz206a8332017-02-06 16:01:23 -0600183bool Host::isAutoReboot()
184{
185 sdbusplus::message::variant<std::string> autoRebootParam;
186 std::string strParam;
187
188 std::string HOST_PATH("/org/openbmc/settings/host0");
189 std::string HOST_INTERFACE("org.openbmc.settings.Host");
190
191 auto mapper = this->bus.new_method_call(MAPPER_BUSNAME,
192 MAPPER_PATH,
193 MAPPER_INTERFACE,
194 "GetObject");
195
196 mapper.append(HOST_PATH, std::vector<std::string>({HOST_INTERFACE}));
197 auto mapperResponseMsg = this->bus.call(mapper);
198
199 if (mapperResponseMsg.is_method_error())
200 {
201 log<level::ERR>("Error in mapper call");
202 return false;
203 }
204
205 std::map<std::string, std::vector<std::string>> mapperResponse;
206 mapperResponseMsg.read(mapperResponse);
207 if (mapperResponse.empty())
208 {
209 log<level::ERR>("Error reading mapper response");
210 return false;
211 }
212
213 const auto& host = mapperResponse.begin()->first;
214
215 auto method = this->bus.new_method_call(host.c_str(),
216 HOST_PATH.c_str(),
217 "org.freedesktop.DBus.Properties",
218 "Get");
219
220 method.append(HOST_INTERFACE.c_str(), "auto_reboot");
221 auto reply = this->bus.call(method);
222
223 if (reply.is_method_error())
224 {
225 log<level::ERR>("Error in auto_reboot Get");
226 return false;
227 }
228
229 reply.read(autoRebootParam);
230 strParam =
231 sdbusplus::message::variant_ns::get<std::string>(autoRebootParam);
232
233 if (strParam.empty())
234 {
235 log<level::ERR>("Error reading auto_reboot response");
236 return false;
237 }
238
Saqib Khancbe08d12017-03-10 01:29:20 -0600239 sdbusplus::message::variant<int> rebootCounterParam;
240 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
241 REBOOTCOUNTER_PATH,
242 REBOOTCOUNTER_INTERFACE,
243 "getValue");
244 reply = this->bus.call(method);
245 if (reply.is_method_error())
246 {
247 log<level::ERR>("Error in BOOTCOUNT getValue");
248 return false;
249 }
250 reply.read(rebootCounterParam);
251
Michael Tritz206a8332017-02-06 16:01:23 -0600252 if (strParam == "yes")
253 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600254 method = this->bus.new_method_call(REBOOTCOUNTER_SERVICE,
255 REBOOTCOUNTER_PATH,
256 REBOOTCOUNTER_INTERFACE,
257 "setValue");
258 if( rebootCounterParam > 0)
259 {
260 // Reduce BOOTCOUNT by 1
261 method.append((sdbusplus::message::variant_ns::
262 get<int>(rebootCounterParam)) - 1);
263 this->bus.call_noreply(method);
264 return true;
265 }
266 if(rebootCounterParam == 0)
Josh D. King929ef702017-03-02 10:58:11 -0600267 {
Saqib Khancbe08d12017-03-10 01:29:20 -0600268 // Reset reboot counter and go to quiesce state
269 method.append(DEFAULT_BOOTCOUNT);
270 this->bus.call_noreply(method);
271 return false;
272 }
Michael Tritz206a8332017-02-06 16:01:23 -0600273 }
274
275 return false;
276}
277
Andrew Geissler4da7e002017-01-24 15:21:40 -0600278int Host::sysStateChangeSignal(sd_bus_message *msg, void *userData,
279 sd_bus_error *retError)
Andrew Geissleref621162016-12-08 12:56:21 -0600280{
Andrew Geissler4da7e002017-01-24 15:21:40 -0600281 return static_cast<Host*>(userData)->sysStateChange(msg, retError);
282}
Andrew Geissleref621162016-12-08 12:56:21 -0600283
Andrew Geissler4da7e002017-01-24 15:21:40 -0600284int Host::sysStateChange(sd_bus_message* msg,
285 sd_bus_error* retError)
286{
287 uint32_t newStateID {};
288 sdbusplus::message::object_path newStateObjPath;
289 std::string newStateUnit{};
290 std::string newStateResult{};
291
292 auto sdPlusMsg = sdbusplus::message::message(msg);
293 //Read the msg and populate each variable
294 sdPlusMsg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
295
296 if((newStateUnit == HOST_STATE_POWEROFF_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600297 (newStateResult == "done") &&
298 (!stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissleref621162016-12-08 12:56:21 -0600299 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930300 log<level::INFO>("Received signal that host is off");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600301 this->currentHostState(server::Host::HostState::Off);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600302
303 // Check if we need to start a new transition (i.e. a Reboot)
Andrew Geissler4da7e002017-01-24 15:21:40 -0600304 if(this->server::Host::requestedHostTransition() ==
305 Transition::Reboot)
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600306 {
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600307 log<level::DEBUG>("Reached intermediate state, going to next");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600308 this->executeTransition(server::Host::Transition::On);
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600309 }
Andrew Geissleref621162016-12-08 12:56:21 -0600310 }
Andrew Geissler4da7e002017-01-24 15:21:40 -0600311 else if((newStateUnit == HOST_STATE_POWERON_TGT) &&
Josh D. King929ef702017-03-02 10:58:11 -0600312 (newStateResult == "done") &&
313 (stateActive(HOST_STATE_POWERON_TGT)))
Andrew Geissler4da7e002017-01-24 15:21:40 -0600314 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930315 log<level::INFO>("Received signal that host is running");
Andrew Geissler4da7e002017-01-24 15:21:40 -0600316 this->currentHostState(server::Host::HostState::Running);
317 }
Michael Tritz206a8332017-02-06 16:01:23 -0600318 else if((newStateUnit == HOST_STATE_QUIESCE_TGT) &&
319 (newStateResult == "done"))
320 {
321 if (Host::isAutoReboot())
322 {
323 log<level::INFO>("Auto reboot enabled. Beginning reboot...");
324 Host::requestedHostTransition(server::Host::Transition::Reboot);
325 }
326 else
327 {
328 log<level::INFO>("Auto reboot disabled. Maintaining quiesce.");
Saqib Khanadaa7212017-02-23 13:19:28 -0600329 this->currentHostState(server::Host::HostState::Quiesced);
Michael Tritz206a8332017-02-06 16:01:23 -0600330 }
331
332 }
Andrew Geissleref621162016-12-08 12:56:21 -0600333
334 return 0;
335}
336
Andrew Geissleref3c1842016-12-01 12:33:09 -0600337Host::Transition Host::requestedHostTransition(Transition value)
338{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600339 log<level::INFO>(
340 "Host State transaction request",
341 entry("REQUESTED_HOST_TRANSITION=%s",
342 convertForMessage(value).c_str()));
Andrew Geissler0cd2eaf2016-12-07 10:50:13 -0600343
Andrew Geissler06dbc5b2016-12-13 11:46:16 -0600344 Transition tranReq = value;
345 if(value == server::Host::Transition::Reboot)
346 {
347 // On reboot requests we just need to do a off if we're on and
348 // vice versa. The handleSysStateChange() code above handles the
349 // second part of the reboot
350 if(this->server::Host::currentHostState() ==
351 server::Host::HostState::Off)
352 {
353 tranReq = server::Host::Transition::On;
354 }
355 else
356 {
357 tranReq = server::Host::Transition::Off;
358 }
359 }
360
361 executeTransition(tranReq);
Andrew Geissler3e3b84b2016-12-02 15:46:17 -0600362 return server::Host::requestedHostTransition(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600363}
364
Andrew Geissleref3c1842016-12-01 12:33:09 -0600365Host::HostState Host::currentHostState(HostState value)
366{
Andrew Geissler1e3bf942016-12-13 15:32:22 -0600367 log<level::INFO>("Change to Host State",
368 entry("CURRENT_HOST_STATE=%s",
369 convertForMessage(value).c_str()));
Andrew Geissleref621162016-12-08 12:56:21 -0600370 return server::Host::currentHostState(value);
Andrew Geissleref3c1842016-12-01 12:33:09 -0600371}
372
Andrew Geissler36529022016-11-29 15:23:54 -0600373} // namespace manager
374} // namespace state
375} // namepsace phosphor