blob: 81dff7fc4f294f5a32d549554c88275271d7027e [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;
Andrew Geissler0971dea2017-03-28 14:32:40 -050023
Andrew Geisslereeaccf82017-03-28 15:23:27 -050024// Required strings for sending the msg to check on host
25constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
26constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
27constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050028constexpr auto CONDITION_HOST_INTERFACE =
29 "xyz.openbmc_project.Condition.HostFirmware";
30constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
31constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
Andrew Geissler0971dea2017-03-28 14:32:40 -050032
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040033constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
34constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0";
35constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
36constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
37
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050038// Find all implementations of Condition interface and check if host is
39// running over it
40bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus)
Andrew Geissler0971dea2017-03-28 14:32:40 -050041{
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050042 // Find all implementations of host firmware condition interface
Andrew Geissler58a18012018-01-19 19:36:05 -080043 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050044 MAPPER_INTERFACE, "GetSubTree");
Andrew Geisslereeaccf82017-03-28 15:23:27 -050045
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050046 mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
Andrew Geisslereeaccf82017-03-28 15:23:27 -050047
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050048 std::map<std::string, std::map<std::string, std::vector<std::string>>>
49 mapperResponse;
Anthony Wilson32c532e2018-10-25 21:56:07 -050050
51 try
52 {
53 auto mapperResponseMsg = bus.call(mapper);
54 mapperResponseMsg.read(mapperResponse);
55 }
Patrick Williams0a675212021-09-02 09:49:43 -050056 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050057 {
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050058 log<level::ERR>(
59 "Error in mapper GetSubTree call for HostFirmware condition",
60 entry("ERROR=%s", e.what()));
Anthony Wilson32c532e2018-10-25 21:56:07 -050061 throw;
Andrew Geisslereeaccf82017-03-28 15:23:27 -050062 }
63
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050064 if (mapperResponse.empty())
65 {
66 log<level::INFO>(
67 "Mapper response for HostFirmware conditions is empty!");
68 return false;
69 }
70
71 // Now read the CurrentFirmwareCondition from all interfaces we found
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050072 // Currently there are two implementations of this interface. One by IPMI
73 // and one by PLDM. The IPMI interface does a realtime check with the host
74 // when the interface is called. This means if the host is not running,
75 // we will have to wait for the timeout (currently set to 3 seconds). The
76 // PLDM interface reads a cached state. The PLDM service does not put itself
77 // on D-Bus until it has checked with the host. Therefore it's most
78 // efficient to call the PLDM interface first. Do that by going in reverse
79 // of the interfaces returned to us (PLDM will be last if available)
80 for (const auto& [path, services] :
81 boost::adaptors::reverse(mapperResponse))
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050082 {
83 for (const auto& serviceIter : services)
84 {
85 const std::string& service = serviceIter.first;
86
87 try
88 {
89 auto method = bus.new_method_call(service.c_str(), path.c_str(),
90 PROPERTY_INTERFACE, "Get");
91 method.append(CONDITION_HOST_INTERFACE,
92 CONDITION_HOST_PROPERTY);
93
94 auto response = bus.call(method);
95
96 std::variant<std::string> currentFwCond;
97 response.read(currentFwCond);
98
99 if (std::get<std::string>(currentFwCond) ==
100 "xyz.openbmc_project.Condition.HostFirmware."
101 "FirmwareCondition."
102 "Running")
103 {
104 return true;
105 }
106 }
Patrick Williams0a675212021-09-02 09:49:43 -0500107 catch (const sdbusplus::exception::exception& e)
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500108 {
109 log<level::ERR>("Error reading HostFirmware condition",
110 entry("ERROR=%s", e.what()),
111 entry("SERVICE=%s", service.c_str()),
112 entry("PATH=%s", path.c_str()));
113 throw;
114 }
115 }
116 }
117 return false;
Andrew Geisslereeaccf82017-03-28 15:23:27 -0500118}
119
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400120// Helper function to check if chassis power is on
121bool isChassiPowerOn(sdbusplus::bus::bus& bus)
122{
123 try
124 {
125 auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH,
126 PROPERTY_INTERFACE, "Get");
127 method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
128
129 auto response = bus.call(method);
130
131 std::variant<std::string> currentPowerState;
132 response.read(currentPowerState);
133
134 if (std::get<std::string>(currentPowerState) ==
135 "xyz.openbmc_project.State.Chassis.PowerState.On")
136 {
137 return true;
138 }
139 }
Patrick Williams0a675212021-09-02 09:49:43 -0500140 catch (const sdbusplus::exception::exception& e)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400141 {
142 log<level::ERR>("Error reading Chassis Power State",
143 entry("ERROR=%s", e.what()),
144 entry("SERVICE=%s", CHASSIS_STATE_SVC),
145 entry("PATH=%s", CHASSIS_STATE_PATH));
146
147 throw;
148 }
149 return false;
150}
151
152bool isHostRunning()
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500153{
154 log<level::INFO>("Check if host is running");
155
Andrew Geissler0971dea2017-03-28 14:32:40 -0500156 auto bus = sdbusplus::bus::new_default();
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500157
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400158 // No need to check if chassis power is not on
159 if (!isChassiPowerOn(bus))
160 {
161 log<level::INFO>("Chassis power not on, exit");
162 return false;
163 }
164
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500165 // This applications systemd service is setup to only run after all other
166 // application that could possibly implement the needed interface have
167 // been started. However, the use of mapper to find those interfaces means
168 // we have a condition where the interface may be on D-Bus but not stored
169 // within mapper yet. Keep it simple and just build one retry into the
170 // check if it's found the host is not up. This service is only called if
171 // chassis power is on when the BMC comes up, so this wont impact most
172 // normal cases where the BMC is rebooted with chassis power off. In
173 // cases where chassis power is on, the host is likely running so we want
174 // to be sure we check all interfaces
175 for (int i = 0; i < 2; i++)
Andrew Geissler0971dea2017-03-28 14:32:40 -0500176 {
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500177 // Give mapper a small window to introspect new objects on bus
178 std::this_thread::sleep_for(std::chrono::milliseconds(500));
179 if (checkFirmwareConditionRunning(bus))
180 {
181 log<level::INFO>("Host is running!");
182 // Create file for host instance and create in filesystem to
183 // indicate to services that host is running
184 auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
185 size++; // null
186 std::unique_ptr<char[]> buf(new char[size]);
187 std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
188 std::ofstream outfile(buf.get());
189 outfile.close();
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400190 return true;
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500191 }
Andrew Geissler07d0ed52017-03-28 15:44:18 -0500192 }
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500193 log<level::INFO>("Host is not running!");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400194 return false;
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500195}