blob: ced4ba377d2ed79f5b6ec1189721e1a3178dadb1 [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
Andrew Geissler0d1c3f12021-07-27 16:21:01 -04003#include "host_check.hpp"
4
Andrew Geissler0971dea2017-03-28 14:32:40 -05005#include <unistd.h>
Andrew Geisslere426b582020-05-28 12:40:55 -05006
Andrew Geisslercd0ebe82021-05-14 21:39:51 -05007#include <boost/range/adaptor/reversed.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -05008#include <phosphor-logging/lg2.hpp>
Andrew Geisslerb09463d2017-03-24 15:55:17 -05009#include <sdbusplus/bus.hpp>
Anthony Wilson32c532e2018-10-25 21:56:07 -050010#include <sdbusplus/exception.hpp>
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050011#include <xyz/openbmc_project/Condition/HostFirmware/server.hpp>
NodeMan9780db4752022-05-04 09:06:40 -050012#include <xyz/openbmc_project/State/Chassis/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050013
14#include <cstdio>
15#include <cstdlib>
16#include <fstream>
17#include <iostream>
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050018#include <thread>
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050019#include <vector>
Andrew Geisslerb09463d2017-03-24 15:55:17 -050020
Andrew Geissler8ffdb262021-09-20 15:25:19 -050021namespace phosphor
22{
23namespace state
24{
25namespace manager
26{
27
28PHOSPHOR_LOG2_USING;
29
Andrew Geisslereeaccf82017-03-28 15:23:27 -050030using namespace std::literals;
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050031using namespace sdbusplus::xyz::openbmc_project::Condition::server;
Andrew Geissler0971dea2017-03-28 14:32:40 -050032
Andrew Geisslereeaccf82017-03-28 15:23:27 -050033// Required strings for sending the msg to check on host
34constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
35constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
36constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050037constexpr auto CONDITION_HOST_INTERFACE =
38 "xyz.openbmc_project.Condition.HostFirmware";
39constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
40constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
Andrew Geissler0971dea2017-03-28 14:32:40 -050041
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040042constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
Potin Lai70f36d82022-03-15 10:25:39 +080043constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis";
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040044constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
45constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
46
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050047// Find all implementations of Condition interface and check if host is
48// running over it
Patrick Williamsf053e6f2022-07-22 19:26:54 -050049bool checkFirmwareConditionRunning(sdbusplus::bus_t& bus)
Andrew Geissler0971dea2017-03-28 14:32:40 -050050{
NodeMan9780db4752022-05-04 09:06:40 -050051 using FirmwareCondition = HostFirmware::FirmwareCondition;
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050052 // Find all implementations of host firmware condition interface
Andrew Geissler58a18012018-01-19 19:36:05 -080053 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050054 MAPPER_INTERFACE, "GetSubTree");
Andrew Geisslereeaccf82017-03-28 15:23:27 -050055
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050056 mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
Andrew Geisslereeaccf82017-03-28 15:23:27 -050057
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050058 std::map<std::string, std::map<std::string, std::vector<std::string>>>
59 mapperResponse;
Anthony Wilson32c532e2018-10-25 21:56:07 -050060
61 try
62 {
63 auto mapperResponseMsg = bus.call(mapper);
64 mapperResponseMsg.read(mapperResponse);
65 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050066 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050067 {
Andrew Geisslerad65b2d2021-09-21 12:53:29 -050068 error(
69 "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}",
70 "ERROR", e);
Anthony Wilson32c532e2018-10-25 21:56:07 -050071 throw;
Andrew Geisslereeaccf82017-03-28 15:23:27 -050072 }
73
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050074 if (mapperResponse.empty())
75 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050076 info("Mapper response for HostFirmware conditions is empty!");
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050077 return false;
78 }
79
80 // Now read the CurrentFirmwareCondition from all interfaces we found
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050081 // Currently there are two implementations of this interface. One by IPMI
82 // and one by PLDM. The IPMI interface does a realtime check with the host
83 // when the interface is called. This means if the host is not running,
84 // we will have to wait for the timeout (currently set to 3 seconds). The
85 // PLDM interface reads a cached state. The PLDM service does not put itself
86 // on D-Bus until it has checked with the host. Therefore it's most
87 // efficient to call the PLDM interface first. Do that by going in reverse
88 // of the interfaces returned to us (PLDM will be last if available)
89 for (const auto& [path, services] :
90 boost::adaptors::reverse(mapperResponse))
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050091 {
92 for (const auto& serviceIter : services)
93 {
94 const std::string& service = serviceIter.first;
95
96 try
97 {
98 auto method = bus.new_method_call(service.c_str(), path.c_str(),
99 PROPERTY_INTERFACE, "Get");
100 method.append(CONDITION_HOST_INTERFACE,
101 CONDITION_HOST_PROPERTY);
102
103 auto response = bus.call(method);
NodeMan9780db4752022-05-04 09:06:40 -0500104 std::variant<FirmwareCondition> currentFwCondV;
105 response.read(currentFwCondV);
106 auto currentFwCond =
107 std::get<FirmwareCondition>(currentFwCondV);
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500108
NodeMan97ef828c42022-05-31 11:24:32 -0500109 info(
110 "Read host fw condition {COND_VALUE} from {COND_SERVICE}, {COND_PATH}",
111 "COND_VALUE", currentFwCond, "COND_SERVICE", service,
112 "COND_PATH", path);
113
NodeMan9780db4752022-05-04 09:06:40 -0500114 if (currentFwCond == FirmwareCondition::Running)
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500115 {
116 return true;
117 }
118 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500119 catch (const sdbusplus::exception_t& e)
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500120 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500121 error("Error reading HostFirmware condition, error: {ERROR}, "
122 "service: {SERVICE} path: {PATH}",
123 "ERROR", e, "SERVICE", service, "PATH", path);
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500124 throw;
125 }
126 }
127 }
128 return false;
Andrew Geisslereeaccf82017-03-28 15:23:27 -0500129}
130
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400131// Helper function to check if chassis power is on
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500132bool isChassiPowerOn(sdbusplus::bus_t& bus, size_t id)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400133{
Potin Lai07e73aa2022-03-29 15:16:55 +0800134 auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id);
135 auto objpath = std::string{CHASSIS_STATE_PATH} + std::to_string(id);
Potin Lai70f36d82022-03-15 10:25:39 +0800136
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400137 try
138 {
NodeMan9780db4752022-05-04 09:06:40 -0500139 using PowerState =
140 sdbusplus::xyz::openbmc_project::State::server::Chassis::PowerState;
Potin Lai70f36d82022-03-15 10:25:39 +0800141 auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(),
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400142 PROPERTY_INTERFACE, "Get");
143 method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
144
145 auto response = bus.call(method);
NodeMan9780db4752022-05-04 09:06:40 -0500146 std::variant<PowerState> currentPowerStateV;
147 response.read(currentPowerStateV);
NodeMan97ef828c42022-05-31 11:24:32 -0500148
NodeMan9780db4752022-05-04 09:06:40 -0500149 auto currentPowerState = std::get<PowerState>(currentPowerStateV);
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400150
NodeMan9780db4752022-05-04 09:06:40 -0500151 if (currentPowerState == PowerState::On)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400152 {
153 return true;
154 }
155 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500156 catch (const sdbusplus::exception_t& e)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400157 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500158 error("Error reading Chassis Power State, error: {ERROR}, "
159 "service: {SERVICE} path: {PATH}",
Potin Lai70f36d82022-03-15 10:25:39 +0800160 "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str());
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400161 throw;
162 }
163 return false;
164}
165
Potin Lai70f36d82022-03-15 10:25:39 +0800166bool isHostRunning(size_t id)
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500167{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500168 info("Check if host is running");
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500169
Andrew Geissler0971dea2017-03-28 14:32:40 -0500170 auto bus = sdbusplus::bus::new_default();
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500171
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400172 // No need to check if chassis power is not on
Potin Lai70f36d82022-03-15 10:25:39 +0800173 if (!isChassiPowerOn(bus, id))
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400174 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500175 info("Chassis power not on, exit");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400176 return false;
177 }
178
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500179 // This applications systemd service is setup to only run after all other
180 // application that could possibly implement the needed interface have
181 // been started. However, the use of mapper to find those interfaces means
182 // we have a condition where the interface may be on D-Bus but not stored
NodeMan97ef828c42022-05-31 11:24:32 -0500183 // within mapper yet. There are five built in retries to check if it's
184 // found the host is not up. This service is only called if chassis power
185 // is on when the BMC comes up, so this wont impact most normal cases
186 // where the BMC is rebooted with chassis power off. In cases where
187 // chassis power is on, the host is likely running so we want to be sure
188 // we check all interfaces
189 for (int i = 0; i < 5; i++)
Andrew Geissler0971dea2017-03-28 14:32:40 -0500190 {
NodeMan97ef828c42022-05-31 11:24:32 -0500191 debug(
192 "Introspecting new bus objects for bus id: {ID} sleeping for 1 second.",
193 "ID", id);
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500194 // Give mapper a small window to introspect new objects on bus
NodeMan97ef828c42022-05-31 11:24:32 -0500195 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500196 if (checkFirmwareConditionRunning(bus))
197 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500198 info("Host is running!");
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500199 // Create file for host instance and create in filesystem to
200 // indicate to services that host is running
201 auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
202 size++; // null
203 std::unique_ptr<char[]> buf(new char[size]);
204 std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
205 std::ofstream outfile(buf.get());
206 outfile.close();
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400207 return true;
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500208 }
Andrew Geissler07d0ed52017-03-28 15:44:18 -0500209 }
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500210 info("Host is not running!");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400211 return false;
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500212}
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500213
214} // namespace manager
215} // namespace state
216} // namespace phosphor