blob: a4f8e0d555e1d12e8d4d6df27d3157304b5793df [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>
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 Geissler8ffdb262021-09-20 15:25:19 -050020namespace phosphor
21{
22namespace state
23{
24namespace manager
25{
26
27PHOSPHOR_LOG2_USING;
28
Andrew Geisslereeaccf82017-03-28 15:23:27 -050029using namespace std::literals;
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050030using namespace sdbusplus::xyz::openbmc_project::Condition::server;
Andrew Geissler0971dea2017-03-28 14:32:40 -050031
Andrew Geisslereeaccf82017-03-28 15:23:27 -050032// Required strings for sending the msg to check on host
33constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
34constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
35constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050036constexpr auto CONDITION_HOST_INTERFACE =
37 "xyz.openbmc_project.Condition.HostFirmware";
38constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
39constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
Andrew Geissler0971dea2017-03-28 14:32:40 -050040
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040041constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
42constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0";
43constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
44constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
45
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050046// Find all implementations of Condition interface and check if host is
47// running over it
48bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus)
Andrew Geissler0971dea2017-03-28 14:32:40 -050049{
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050050 // Find all implementations of host firmware condition interface
Andrew Geissler58a18012018-01-19 19:36:05 -080051 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050052 MAPPER_INTERFACE, "GetSubTree");
Andrew Geisslereeaccf82017-03-28 15:23:27 -050053
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050054 mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
Andrew Geisslereeaccf82017-03-28 15:23:27 -050055
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050056 std::map<std::string, std::map<std::string, std::vector<std::string>>>
57 mapperResponse;
Anthony Wilson32c532e2018-10-25 21:56:07 -050058
59 try
60 {
61 auto mapperResponseMsg = bus.call(mapper);
62 mapperResponseMsg.read(mapperResponse);
63 }
Patrick Williams0a675212021-09-02 09:49:43 -050064 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050065 {
Andrew Geisslerad65b2d2021-09-21 12:53:29 -050066 error(
67 "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}",
68 "ERROR", e);
Anthony Wilson32c532e2018-10-25 21:56:07 -050069 throw;
Andrew Geisslereeaccf82017-03-28 15:23:27 -050070 }
71
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050072 if (mapperResponse.empty())
73 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050074 info("Mapper response for HostFirmware conditions is empty!");
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050075 return false;
76 }
77
78 // Now read the CurrentFirmwareCondition from all interfaces we found
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050079 // Currently there are two implementations of this interface. One by IPMI
80 // and one by PLDM. The IPMI interface does a realtime check with the host
81 // when the interface is called. This means if the host is not running,
82 // we will have to wait for the timeout (currently set to 3 seconds). The
83 // PLDM interface reads a cached state. The PLDM service does not put itself
84 // on D-Bus until it has checked with the host. Therefore it's most
85 // efficient to call the PLDM interface first. Do that by going in reverse
86 // of the interfaces returned to us (PLDM will be last if available)
87 for (const auto& [path, services] :
88 boost::adaptors::reverse(mapperResponse))
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050089 {
90 for (const auto& serviceIter : services)
91 {
92 const std::string& service = serviceIter.first;
93
94 try
95 {
96 auto method = bus.new_method_call(service.c_str(), path.c_str(),
97 PROPERTY_INTERFACE, "Get");
98 method.append(CONDITION_HOST_INTERFACE,
99 CONDITION_HOST_PROPERTY);
100
101 auto response = bus.call(method);
102
103 std::variant<std::string> currentFwCond;
104 response.read(currentFwCond);
105
106 if (std::get<std::string>(currentFwCond) ==
107 "xyz.openbmc_project.Condition.HostFirmware."
108 "FirmwareCondition."
109 "Running")
110 {
111 return true;
112 }
113 }
Patrick Williams0a675212021-09-02 09:49:43 -0500114 catch (const sdbusplus::exception::exception& e)
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500115 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500116 error("Error reading HostFirmware condition, error: {ERROR}, "
117 "service: {SERVICE} path: {PATH}",
118 "ERROR", e, "SERVICE", service, "PATH", path);
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500119 throw;
120 }
121 }
122 }
123 return false;
Andrew Geisslereeaccf82017-03-28 15:23:27 -0500124}
125
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400126// Helper function to check if chassis power is on
127bool isChassiPowerOn(sdbusplus::bus::bus& bus)
128{
129 try
130 {
131 auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH,
132 PROPERTY_INTERFACE, "Get");
133 method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
134
135 auto response = bus.call(method);
136
137 std::variant<std::string> currentPowerState;
138 response.read(currentPowerState);
139
140 if (std::get<std::string>(currentPowerState) ==
141 "xyz.openbmc_project.State.Chassis.PowerState.On")
142 {
143 return true;
144 }
145 }
Patrick Williams0a675212021-09-02 09:49:43 -0500146 catch (const sdbusplus::exception::exception& e)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400147 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500148 error("Error reading Chassis Power State, error: {ERROR}, "
149 "service: {SERVICE} path: {PATH}",
150 "ERROR", e, "SERVICE", CHASSIS_STATE_SVC, "PATH",
151 CHASSIS_STATE_PATH);
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400152 throw;
153 }
154 return false;
155}
156
157bool isHostRunning()
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500158{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500159 info("Check if host is running");
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500160
Andrew Geissler0971dea2017-03-28 14:32:40 -0500161 auto bus = sdbusplus::bus::new_default();
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500162
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400163 // No need to check if chassis power is not on
164 if (!isChassiPowerOn(bus))
165 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500166 info("Chassis power not on, exit");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400167 return false;
168 }
169
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500170 // This applications systemd service is setup to only run after all other
171 // application that could possibly implement the needed interface have
172 // been started. However, the use of mapper to find those interfaces means
173 // we have a condition where the interface may be on D-Bus but not stored
174 // within mapper yet. Keep it simple and just build one retry into the
175 // check if it's found the host is not up. This service is only called if
176 // chassis power is on when the BMC comes up, so this wont impact most
177 // normal cases where the BMC is rebooted with chassis power off. In
178 // cases where chassis power is on, the host is likely running so we want
179 // to be sure we check all interfaces
180 for (int i = 0; i < 2; i++)
Andrew Geissler0971dea2017-03-28 14:32:40 -0500181 {
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500182 // Give mapper a small window to introspect new objects on bus
183 std::this_thread::sleep_for(std::chrono::milliseconds(500));
184 if (checkFirmwareConditionRunning(bus))
185 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500186 info("Host is running!");
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500187 // Create file for host instance and create in filesystem to
188 // indicate to services that host is running
189 auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
190 size++; // null
191 std::unique_ptr<char[]> buf(new char[size]);
192 std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
193 std::ofstream outfile(buf.get());
194 outfile.close();
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400195 return true;
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500196 }
Andrew Geissler07d0ed52017-03-28 15:44:18 -0500197 }
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500198 info("Host is not running!");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400199 return false;
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500200}
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500201
202} // namespace manager
203} // namespace state
204} // namespace phosphor