xref: /openbmc/phosphor-state-manager/host_check.cpp (revision 0d1c3f1f9329c853677f0581287afef83eeea0f0)
1*0d1c3f1fSAndrew Geissler #include "config.h"
2*0d1c3f1fSAndrew Geissler 
3*0d1c3f1fSAndrew Geissler #include "host_check.hpp"
4*0d1c3f1fSAndrew Geissler 
5*0d1c3f1fSAndrew Geissler #include <unistd.h>
6*0d1c3f1fSAndrew Geissler 
7*0d1c3f1fSAndrew Geissler #include <boost/range/adaptor/reversed.hpp>
8*0d1c3f1fSAndrew Geissler #include <phosphor-logging/log.hpp>
9*0d1c3f1fSAndrew Geissler #include <sdbusplus/bus.hpp>
10*0d1c3f1fSAndrew Geissler #include <sdbusplus/exception.hpp>
11*0d1c3f1fSAndrew Geissler #include <xyz/openbmc_project/Condition/HostFirmware/server.hpp>
12*0d1c3f1fSAndrew Geissler 
13*0d1c3f1fSAndrew Geissler #include <cstdio>
14*0d1c3f1fSAndrew Geissler #include <cstdlib>
15*0d1c3f1fSAndrew Geissler #include <fstream>
16*0d1c3f1fSAndrew Geissler #include <iostream>
17*0d1c3f1fSAndrew Geissler #include <thread>
18*0d1c3f1fSAndrew Geissler #include <vector>
19*0d1c3f1fSAndrew Geissler 
20*0d1c3f1fSAndrew Geissler using namespace std::literals;
21*0d1c3f1fSAndrew Geissler using namespace phosphor::logging;
22*0d1c3f1fSAndrew Geissler using namespace sdbusplus::xyz::openbmc_project::Condition::server;
23*0d1c3f1fSAndrew Geissler using sdbusplus::exception::SdBusError;
24*0d1c3f1fSAndrew Geissler 
25*0d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host
26*0d1c3f1fSAndrew Geissler constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
27*0d1c3f1fSAndrew Geissler constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
28*0d1c3f1fSAndrew Geissler constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
29*0d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_INTERFACE =
30*0d1c3f1fSAndrew Geissler     "xyz.openbmc_project.Condition.HostFirmware";
31*0d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
32*0d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
33*0d1c3f1fSAndrew Geissler 
34*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
35*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0";
36*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
37*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
38*0d1c3f1fSAndrew Geissler 
39*0d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is
40*0d1c3f1fSAndrew Geissler // running over it
41*0d1c3f1fSAndrew Geissler bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus)
42*0d1c3f1fSAndrew Geissler {
43*0d1c3f1fSAndrew Geissler     // Find all implementations of host firmware condition interface
44*0d1c3f1fSAndrew Geissler     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
45*0d1c3f1fSAndrew Geissler                                       MAPPER_INTERFACE, "GetSubTree");
46*0d1c3f1fSAndrew Geissler 
47*0d1c3f1fSAndrew Geissler     mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
48*0d1c3f1fSAndrew Geissler 
49*0d1c3f1fSAndrew Geissler     std::map<std::string, std::map<std::string, std::vector<std::string>>>
50*0d1c3f1fSAndrew Geissler         mapperResponse;
51*0d1c3f1fSAndrew Geissler 
52*0d1c3f1fSAndrew Geissler     try
53*0d1c3f1fSAndrew Geissler     {
54*0d1c3f1fSAndrew Geissler         auto mapperResponseMsg = bus.call(mapper);
55*0d1c3f1fSAndrew Geissler         mapperResponseMsg.read(mapperResponse);
56*0d1c3f1fSAndrew Geissler     }
57*0d1c3f1fSAndrew Geissler     catch (const SdBusError& e)
58*0d1c3f1fSAndrew Geissler     {
59*0d1c3f1fSAndrew Geissler         log<level::ERR>(
60*0d1c3f1fSAndrew Geissler             "Error in mapper GetSubTree call for HostFirmware condition",
61*0d1c3f1fSAndrew Geissler             entry("ERROR=%s", e.what()));
62*0d1c3f1fSAndrew Geissler         throw;
63*0d1c3f1fSAndrew Geissler     }
64*0d1c3f1fSAndrew Geissler 
65*0d1c3f1fSAndrew Geissler     if (mapperResponse.empty())
66*0d1c3f1fSAndrew Geissler     {
67*0d1c3f1fSAndrew Geissler         log<level::INFO>(
68*0d1c3f1fSAndrew Geissler             "Mapper response for HostFirmware conditions is empty!");
69*0d1c3f1fSAndrew Geissler         return false;
70*0d1c3f1fSAndrew Geissler     }
71*0d1c3f1fSAndrew Geissler 
72*0d1c3f1fSAndrew Geissler     // Now read the CurrentFirmwareCondition from all interfaces we found
73*0d1c3f1fSAndrew Geissler     // Currently there are two implementations of this interface. One by IPMI
74*0d1c3f1fSAndrew Geissler     // and one by PLDM. The IPMI interface does a realtime check with the host
75*0d1c3f1fSAndrew Geissler     // when the interface is called. This means if the host is not running,
76*0d1c3f1fSAndrew Geissler     // we will have to wait for the timeout (currently set to 3 seconds). The
77*0d1c3f1fSAndrew Geissler     // PLDM interface reads a cached state. The PLDM service does not put itself
78*0d1c3f1fSAndrew Geissler     // on D-Bus until it has checked with the host. Therefore it's most
79*0d1c3f1fSAndrew Geissler     // efficient to call the PLDM interface first. Do that by going in reverse
80*0d1c3f1fSAndrew Geissler     // of the interfaces returned to us (PLDM will be last if available)
81*0d1c3f1fSAndrew Geissler     for (const auto& [path, services] :
82*0d1c3f1fSAndrew Geissler          boost::adaptors::reverse(mapperResponse))
83*0d1c3f1fSAndrew Geissler     {
84*0d1c3f1fSAndrew Geissler         for (const auto& serviceIter : services)
85*0d1c3f1fSAndrew Geissler         {
86*0d1c3f1fSAndrew Geissler             const std::string& service = serviceIter.first;
87*0d1c3f1fSAndrew Geissler 
88*0d1c3f1fSAndrew Geissler             try
89*0d1c3f1fSAndrew Geissler             {
90*0d1c3f1fSAndrew Geissler                 auto method = bus.new_method_call(service.c_str(), path.c_str(),
91*0d1c3f1fSAndrew Geissler                                                   PROPERTY_INTERFACE, "Get");
92*0d1c3f1fSAndrew Geissler                 method.append(CONDITION_HOST_INTERFACE,
93*0d1c3f1fSAndrew Geissler                               CONDITION_HOST_PROPERTY);
94*0d1c3f1fSAndrew Geissler 
95*0d1c3f1fSAndrew Geissler                 auto response = bus.call(method);
96*0d1c3f1fSAndrew Geissler 
97*0d1c3f1fSAndrew Geissler                 std::variant<std::string> currentFwCond;
98*0d1c3f1fSAndrew Geissler                 response.read(currentFwCond);
99*0d1c3f1fSAndrew Geissler 
100*0d1c3f1fSAndrew Geissler                 if (std::get<std::string>(currentFwCond) ==
101*0d1c3f1fSAndrew Geissler                     "xyz.openbmc_project.Condition.HostFirmware."
102*0d1c3f1fSAndrew Geissler                     "FirmwareCondition."
103*0d1c3f1fSAndrew Geissler                     "Running")
104*0d1c3f1fSAndrew Geissler                 {
105*0d1c3f1fSAndrew Geissler                     return true;
106*0d1c3f1fSAndrew Geissler                 }
107*0d1c3f1fSAndrew Geissler             }
108*0d1c3f1fSAndrew Geissler             catch (const SdBusError& e)
109*0d1c3f1fSAndrew Geissler             {
110*0d1c3f1fSAndrew Geissler                 log<level::ERR>("Error reading HostFirmware condition",
111*0d1c3f1fSAndrew Geissler                                 entry("ERROR=%s", e.what()),
112*0d1c3f1fSAndrew Geissler                                 entry("SERVICE=%s", service.c_str()),
113*0d1c3f1fSAndrew Geissler                                 entry("PATH=%s", path.c_str()));
114*0d1c3f1fSAndrew Geissler                 throw;
115*0d1c3f1fSAndrew Geissler             }
116*0d1c3f1fSAndrew Geissler         }
117*0d1c3f1fSAndrew Geissler     }
118*0d1c3f1fSAndrew Geissler     return false;
119*0d1c3f1fSAndrew Geissler }
120*0d1c3f1fSAndrew Geissler 
121*0d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on
122*0d1c3f1fSAndrew Geissler bool isChassiPowerOn(sdbusplus::bus::bus& bus)
123*0d1c3f1fSAndrew Geissler {
124*0d1c3f1fSAndrew Geissler     try
125*0d1c3f1fSAndrew Geissler     {
126*0d1c3f1fSAndrew Geissler         auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH,
127*0d1c3f1fSAndrew Geissler                                           PROPERTY_INTERFACE, "Get");
128*0d1c3f1fSAndrew Geissler         method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
129*0d1c3f1fSAndrew Geissler 
130*0d1c3f1fSAndrew Geissler         auto response = bus.call(method);
131*0d1c3f1fSAndrew Geissler 
132*0d1c3f1fSAndrew Geissler         std::variant<std::string> currentPowerState;
133*0d1c3f1fSAndrew Geissler         response.read(currentPowerState);
134*0d1c3f1fSAndrew Geissler 
135*0d1c3f1fSAndrew Geissler         if (std::get<std::string>(currentPowerState) ==
136*0d1c3f1fSAndrew Geissler             "xyz.openbmc_project.State.Chassis.PowerState.On")
137*0d1c3f1fSAndrew Geissler         {
138*0d1c3f1fSAndrew Geissler             return true;
139*0d1c3f1fSAndrew Geissler         }
140*0d1c3f1fSAndrew Geissler     }
141*0d1c3f1fSAndrew Geissler     catch (const SdBusError& e)
142*0d1c3f1fSAndrew Geissler     {
143*0d1c3f1fSAndrew Geissler         log<level::ERR>("Error reading Chassis Power State",
144*0d1c3f1fSAndrew Geissler                         entry("ERROR=%s", e.what()),
145*0d1c3f1fSAndrew Geissler                         entry("SERVICE=%s", CHASSIS_STATE_SVC),
146*0d1c3f1fSAndrew Geissler                         entry("PATH=%s", CHASSIS_STATE_PATH));
147*0d1c3f1fSAndrew Geissler 
148*0d1c3f1fSAndrew Geissler         throw;
149*0d1c3f1fSAndrew Geissler     }
150*0d1c3f1fSAndrew Geissler     return false;
151*0d1c3f1fSAndrew Geissler }
152*0d1c3f1fSAndrew Geissler 
153*0d1c3f1fSAndrew Geissler bool isHostRunning()
154*0d1c3f1fSAndrew Geissler {
155*0d1c3f1fSAndrew Geissler     log<level::INFO>("Check if host is running");
156*0d1c3f1fSAndrew Geissler 
157*0d1c3f1fSAndrew Geissler     auto bus = sdbusplus::bus::new_default();
158*0d1c3f1fSAndrew Geissler 
159*0d1c3f1fSAndrew Geissler     // No need to check if chassis power is not on
160*0d1c3f1fSAndrew Geissler     if (!isChassiPowerOn(bus))
161*0d1c3f1fSAndrew Geissler     {
162*0d1c3f1fSAndrew Geissler         log<level::INFO>("Chassis power not on, exit");
163*0d1c3f1fSAndrew Geissler         return false;
164*0d1c3f1fSAndrew Geissler     }
165*0d1c3f1fSAndrew Geissler 
166*0d1c3f1fSAndrew Geissler     // This applications systemd service is setup to only run after all other
167*0d1c3f1fSAndrew Geissler     // application that could possibly implement the needed interface have
168*0d1c3f1fSAndrew Geissler     // been started. However, the use of mapper to find those interfaces means
169*0d1c3f1fSAndrew Geissler     // we have a condition where the interface may be on D-Bus but not stored
170*0d1c3f1fSAndrew Geissler     // within mapper yet. Keep it simple and just build one retry into the
171*0d1c3f1fSAndrew Geissler     // check if it's found the host is not up. This service is only called if
172*0d1c3f1fSAndrew Geissler     // chassis power is on when the BMC comes up, so this wont impact most
173*0d1c3f1fSAndrew Geissler     // normal cases where the BMC is rebooted with chassis power off. In
174*0d1c3f1fSAndrew Geissler     // cases where chassis power is on, the host is likely running so we want
175*0d1c3f1fSAndrew Geissler     // to be sure we check all interfaces
176*0d1c3f1fSAndrew Geissler     for (int i = 0; i < 2; i++)
177*0d1c3f1fSAndrew Geissler     {
178*0d1c3f1fSAndrew Geissler         // Give mapper a small window to introspect new objects on bus
179*0d1c3f1fSAndrew Geissler         std::this_thread::sleep_for(std::chrono::milliseconds(500));
180*0d1c3f1fSAndrew Geissler         if (checkFirmwareConditionRunning(bus))
181*0d1c3f1fSAndrew Geissler         {
182*0d1c3f1fSAndrew Geissler             log<level::INFO>("Host is running!");
183*0d1c3f1fSAndrew Geissler             // Create file for host instance and create in filesystem to
184*0d1c3f1fSAndrew Geissler             // indicate to services that host is running
185*0d1c3f1fSAndrew Geissler             auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
186*0d1c3f1fSAndrew Geissler             size++; // null
187*0d1c3f1fSAndrew Geissler             std::unique_ptr<char[]> buf(new char[size]);
188*0d1c3f1fSAndrew Geissler             std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
189*0d1c3f1fSAndrew Geissler             std::ofstream outfile(buf.get());
190*0d1c3f1fSAndrew Geissler             outfile.close();
191*0d1c3f1fSAndrew Geissler             return true;
192*0d1c3f1fSAndrew Geissler         }
193*0d1c3f1fSAndrew Geissler     }
194*0d1c3f1fSAndrew Geissler     log<level::INFO>("Host is not running!");
195*0d1c3f1fSAndrew Geissler     return false;
196*0d1c3f1fSAndrew Geissler }
197