blob: 23715a14687569de3cfc0d103ee7a3bf42390f36 [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 Geisslere426b582020-05-28 12:40:55 -05008#include <phosphor-logging/log.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>
Andrew Geisslere426b582020-05-28 12:40:55 -050012
13#include <cstdio>
14#include <cstdlib>
15#include <fstream>
16#include <iostream>
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050017#include <thread>
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050018#include <vector>
Andrew Geisslerb09463d2017-03-24 15:55:17 -050019
Andrew Geisslereeaccf82017-03-28 15:23:27 -050020using namespace std::literals;
Andrew Geisslerb09463d2017-03-24 15:55:17 -050021using namespace phosphor::logging;
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050022using namespace sdbusplus::xyz::openbmc_project::Condition::server;
Anthony Wilson32c532e2018-10-25 21:56:07 -050023using sdbusplus::exception::SdBusError;
Andrew Geissler0971dea2017-03-28 14:32:40 -050024
Andrew Geisslereeaccf82017-03-28 15:23:27 -050025// Required strings for sending the msg to check on host
26constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
27constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
28constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050029constexpr auto CONDITION_HOST_INTERFACE =
30 "xyz.openbmc_project.Condition.HostFirmware";
31constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
32constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
Andrew Geissler0971dea2017-03-28 14:32:40 -050033
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040034constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
35constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0";
36constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
37constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
38
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050039// Find all implementations of Condition interface and check if host is
40// running over it
41bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus)
Andrew Geissler0971dea2017-03-28 14:32:40 -050042{
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050043 // Find all implementations of host firmware condition interface
Andrew Geissler58a18012018-01-19 19:36:05 -080044 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050045 MAPPER_INTERFACE, "GetSubTree");
Andrew Geisslereeaccf82017-03-28 15:23:27 -050046
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050047 mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
Andrew Geisslereeaccf82017-03-28 15:23:27 -050048
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050049 std::map<std::string, std::map<std::string, std::vector<std::string>>>
50 mapperResponse;
Anthony Wilson32c532e2018-10-25 21:56:07 -050051
52 try
53 {
54 auto mapperResponseMsg = bus.call(mapper);
55 mapperResponseMsg.read(mapperResponse);
56 }
57 catch (const SdBusError& e)
58 {
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050059 log<level::ERR>(
60 "Error in mapper GetSubTree call for HostFirmware condition",
61 entry("ERROR=%s", e.what()));
Anthony Wilson32c532e2018-10-25 21:56:07 -050062 throw;
Andrew Geisslereeaccf82017-03-28 15:23:27 -050063 }
64
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050065 if (mapperResponse.empty())
66 {
67 log<level::INFO>(
68 "Mapper response for HostFirmware conditions is empty!");
69 return false;
70 }
71
72 // Now read the CurrentFirmwareCondition from all interfaces we found
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050073 // Currently there are two implementations of this interface. One by IPMI
74 // and one by PLDM. The IPMI interface does a realtime check with the host
75 // when the interface is called. This means if the host is not running,
76 // we will have to wait for the timeout (currently set to 3 seconds). The
77 // PLDM interface reads a cached state. The PLDM service does not put itself
78 // on D-Bus until it has checked with the host. Therefore it's most
79 // efficient to call the PLDM interface first. Do that by going in reverse
80 // of the interfaces returned to us (PLDM will be last if available)
81 for (const auto& [path, services] :
82 boost::adaptors::reverse(mapperResponse))
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050083 {
84 for (const auto& serviceIter : services)
85 {
86 const std::string& service = serviceIter.first;
87
88 try
89 {
90 auto method = bus.new_method_call(service.c_str(), path.c_str(),
91 PROPERTY_INTERFACE, "Get");
92 method.append(CONDITION_HOST_INTERFACE,
93 CONDITION_HOST_PROPERTY);
94
95 auto response = bus.call(method);
96
97 std::variant<std::string> currentFwCond;
98 response.read(currentFwCond);
99
100 if (std::get<std::string>(currentFwCond) ==
101 "xyz.openbmc_project.Condition.HostFirmware."
102 "FirmwareCondition."
103 "Running")
104 {
105 return true;
106 }
107 }
108 catch (const SdBusError& e)
109 {
110 log<level::ERR>("Error reading HostFirmware condition",
111 entry("ERROR=%s", e.what()),
112 entry("SERVICE=%s", service.c_str()),
113 entry("PATH=%s", path.c_str()));
114 throw;
115 }
116 }
117 }
118 return false;
Andrew Geisslereeaccf82017-03-28 15:23:27 -0500119}
120
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400121// Helper function to check if chassis power is on
122bool isChassiPowerOn(sdbusplus::bus::bus& bus)
123{
124 try
125 {
126 auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH,
127 PROPERTY_INTERFACE, "Get");
128 method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
129
130 auto response = bus.call(method);
131
132 std::variant<std::string> currentPowerState;
133 response.read(currentPowerState);
134
135 if (std::get<std::string>(currentPowerState) ==
136 "xyz.openbmc_project.State.Chassis.PowerState.On")
137 {
138 return true;
139 }
140 }
141 catch (const SdBusError& e)
142 {
143 log<level::ERR>("Error reading Chassis Power State",
144 entry("ERROR=%s", e.what()),
145 entry("SERVICE=%s", CHASSIS_STATE_SVC),
146 entry("PATH=%s", CHASSIS_STATE_PATH));
147
148 throw;
149 }
150 return false;
151}
152
153bool isHostRunning()
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500154{
155 log<level::INFO>("Check if host is running");
156
Andrew Geissler0971dea2017-03-28 14:32:40 -0500157 auto bus = sdbusplus::bus::new_default();
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500158
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400159 // No need to check if chassis power is not on
160 if (!isChassiPowerOn(bus))
161 {
162 log<level::INFO>("Chassis power not on, exit");
163 return false;
164 }
165
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500166 // This applications systemd service is setup to only run after all other
167 // application that could possibly implement the needed interface have
168 // been started. However, the use of mapper to find those interfaces means
169 // we have a condition where the interface may be on D-Bus but not stored
170 // within mapper yet. Keep it simple and just build one retry into the
171 // check if it's found the host is not up. This service is only called if
172 // chassis power is on when the BMC comes up, so this wont impact most
173 // normal cases where the BMC is rebooted with chassis power off. In
174 // cases where chassis power is on, the host is likely running so we want
175 // to be sure we check all interfaces
176 for (int i = 0; i < 2; i++)
Andrew Geissler0971dea2017-03-28 14:32:40 -0500177 {
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500178 // Give mapper a small window to introspect new objects on bus
179 std::this_thread::sleep_for(std::chrono::milliseconds(500));
180 if (checkFirmwareConditionRunning(bus))
181 {
182 log<level::INFO>("Host is running!");
183 // Create file for host instance and create in filesystem to
184 // indicate to services that host is running
185 auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
186 size++; // null
187 std::unique_ptr<char[]> buf(new char[size]);
188 std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
189 std::ofstream outfile(buf.get());
190 outfile.close();
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400191 return true;
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500192 }
Andrew Geissler07d0ed52017-03-28 15:44:18 -0500193 }
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500194 log<level::INFO>("Host is not running!");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400195 return false;
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500196}