xref: /openbmc/phosphor-state-manager/host_check.cpp (revision 8ffdb26928f53ab906a45a43d3447815f32c9055)
10d1c3f1fSAndrew Geissler #include "config.h"
20d1c3f1fSAndrew Geissler 
30d1c3f1fSAndrew Geissler #include "host_check.hpp"
40d1c3f1fSAndrew Geissler 
50d1c3f1fSAndrew Geissler #include <unistd.h>
60d1c3f1fSAndrew Geissler 
70d1c3f1fSAndrew Geissler #include <boost/range/adaptor/reversed.hpp>
8*8ffdb269SAndrew Geissler #include <phosphor-logging/lg2.hpp>
90d1c3f1fSAndrew Geissler #include <sdbusplus/bus.hpp>
100d1c3f1fSAndrew Geissler #include <sdbusplus/exception.hpp>
110d1c3f1fSAndrew Geissler #include <xyz/openbmc_project/Condition/HostFirmware/server.hpp>
120d1c3f1fSAndrew Geissler 
130d1c3f1fSAndrew Geissler #include <cstdio>
140d1c3f1fSAndrew Geissler #include <cstdlib>
150d1c3f1fSAndrew Geissler #include <fstream>
160d1c3f1fSAndrew Geissler #include <iostream>
170d1c3f1fSAndrew Geissler #include <thread>
180d1c3f1fSAndrew Geissler #include <vector>
190d1c3f1fSAndrew Geissler 
20*8ffdb269SAndrew Geissler namespace phosphor
21*8ffdb269SAndrew Geissler {
22*8ffdb269SAndrew Geissler namespace state
23*8ffdb269SAndrew Geissler {
24*8ffdb269SAndrew Geissler namespace manager
25*8ffdb269SAndrew Geissler {
26*8ffdb269SAndrew Geissler 
27*8ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING;
28*8ffdb269SAndrew Geissler 
290d1c3f1fSAndrew Geissler using namespace std::literals;
300d1c3f1fSAndrew Geissler using namespace sdbusplus::xyz::openbmc_project::Condition::server;
310d1c3f1fSAndrew Geissler 
320d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host
330d1c3f1fSAndrew Geissler constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
340d1c3f1fSAndrew Geissler constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
350d1c3f1fSAndrew Geissler constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
360d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_INTERFACE =
370d1c3f1fSAndrew Geissler     "xyz.openbmc_project.Condition.HostFirmware";
380d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
390d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
400d1c3f1fSAndrew Geissler 
410d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
420d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0";
430d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
440d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
450d1c3f1fSAndrew Geissler 
460d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is
470d1c3f1fSAndrew Geissler // running over it
480d1c3f1fSAndrew Geissler bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus)
490d1c3f1fSAndrew Geissler {
500d1c3f1fSAndrew Geissler     // Find all implementations of host firmware condition interface
510d1c3f1fSAndrew Geissler     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
520d1c3f1fSAndrew Geissler                                       MAPPER_INTERFACE, "GetSubTree");
530d1c3f1fSAndrew Geissler 
540d1c3f1fSAndrew Geissler     mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
550d1c3f1fSAndrew Geissler 
560d1c3f1fSAndrew Geissler     std::map<std::string, std::map<std::string, std::vector<std::string>>>
570d1c3f1fSAndrew Geissler         mapperResponse;
580d1c3f1fSAndrew Geissler 
590d1c3f1fSAndrew Geissler     try
600d1c3f1fSAndrew Geissler     {
610d1c3f1fSAndrew Geissler         auto mapperResponseMsg = bus.call(mapper);
620d1c3f1fSAndrew Geissler         mapperResponseMsg.read(mapperResponse);
630d1c3f1fSAndrew Geissler     }
640a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
650d1c3f1fSAndrew Geissler     {
66*8ffdb269SAndrew Geissler         error("Error in mapper GetSubTree call for HostFirmware condition: "
67*8ffdb269SAndrew Geissler               "{ERROR}",
68*8ffdb269SAndrew Geissler               "ERROR", e);
690d1c3f1fSAndrew Geissler         throw;
700d1c3f1fSAndrew Geissler     }
710d1c3f1fSAndrew Geissler 
720d1c3f1fSAndrew Geissler     if (mapperResponse.empty())
730d1c3f1fSAndrew Geissler     {
74*8ffdb269SAndrew Geissler         info("Mapper response for HostFirmware conditions is empty!");
750d1c3f1fSAndrew Geissler         return false;
760d1c3f1fSAndrew Geissler     }
770d1c3f1fSAndrew Geissler 
780d1c3f1fSAndrew Geissler     // Now read the CurrentFirmwareCondition from all interfaces we found
790d1c3f1fSAndrew Geissler     // Currently there are two implementations of this interface. One by IPMI
800d1c3f1fSAndrew Geissler     // and one by PLDM. The IPMI interface does a realtime check with the host
810d1c3f1fSAndrew Geissler     // when the interface is called. This means if the host is not running,
820d1c3f1fSAndrew Geissler     // we will have to wait for the timeout (currently set to 3 seconds). The
830d1c3f1fSAndrew Geissler     // PLDM interface reads a cached state. The PLDM service does not put itself
840d1c3f1fSAndrew Geissler     // on D-Bus until it has checked with the host. Therefore it's most
850d1c3f1fSAndrew Geissler     // efficient to call the PLDM interface first. Do that by going in reverse
860d1c3f1fSAndrew Geissler     // of the interfaces returned to us (PLDM will be last if available)
870d1c3f1fSAndrew Geissler     for (const auto& [path, services] :
880d1c3f1fSAndrew Geissler          boost::adaptors::reverse(mapperResponse))
890d1c3f1fSAndrew Geissler     {
900d1c3f1fSAndrew Geissler         for (const auto& serviceIter : services)
910d1c3f1fSAndrew Geissler         {
920d1c3f1fSAndrew Geissler             const std::string& service = serviceIter.first;
930d1c3f1fSAndrew Geissler 
940d1c3f1fSAndrew Geissler             try
950d1c3f1fSAndrew Geissler             {
960d1c3f1fSAndrew Geissler                 auto method = bus.new_method_call(service.c_str(), path.c_str(),
970d1c3f1fSAndrew Geissler                                                   PROPERTY_INTERFACE, "Get");
980d1c3f1fSAndrew Geissler                 method.append(CONDITION_HOST_INTERFACE,
990d1c3f1fSAndrew Geissler                               CONDITION_HOST_PROPERTY);
1000d1c3f1fSAndrew Geissler 
1010d1c3f1fSAndrew Geissler                 auto response = bus.call(method);
1020d1c3f1fSAndrew Geissler 
1030d1c3f1fSAndrew Geissler                 std::variant<std::string> currentFwCond;
1040d1c3f1fSAndrew Geissler                 response.read(currentFwCond);
1050d1c3f1fSAndrew Geissler 
1060d1c3f1fSAndrew Geissler                 if (std::get<std::string>(currentFwCond) ==
1070d1c3f1fSAndrew Geissler                     "xyz.openbmc_project.Condition.HostFirmware."
1080d1c3f1fSAndrew Geissler                     "FirmwareCondition."
1090d1c3f1fSAndrew Geissler                     "Running")
1100d1c3f1fSAndrew Geissler                 {
1110d1c3f1fSAndrew Geissler                     return true;
1120d1c3f1fSAndrew Geissler                 }
1130d1c3f1fSAndrew Geissler             }
1140a675215SPatrick Williams             catch (const sdbusplus::exception::exception& e)
1150d1c3f1fSAndrew Geissler             {
116*8ffdb269SAndrew Geissler                 error("Error reading HostFirmware condition, error: {ERROR}, "
117*8ffdb269SAndrew Geissler                       "service: {SERVICE} path: {PATH}",
118*8ffdb269SAndrew Geissler                       "ERROR", e, "SERVICE", service, "PATH", path);
1190d1c3f1fSAndrew Geissler                 throw;
1200d1c3f1fSAndrew Geissler             }
1210d1c3f1fSAndrew Geissler         }
1220d1c3f1fSAndrew Geissler     }
1230d1c3f1fSAndrew Geissler     return false;
1240d1c3f1fSAndrew Geissler }
1250d1c3f1fSAndrew Geissler 
1260d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on
1270d1c3f1fSAndrew Geissler bool isChassiPowerOn(sdbusplus::bus::bus& bus)
1280d1c3f1fSAndrew Geissler {
1290d1c3f1fSAndrew Geissler     try
1300d1c3f1fSAndrew Geissler     {
1310d1c3f1fSAndrew Geissler         auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH,
1320d1c3f1fSAndrew Geissler                                           PROPERTY_INTERFACE, "Get");
1330d1c3f1fSAndrew Geissler         method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
1340d1c3f1fSAndrew Geissler 
1350d1c3f1fSAndrew Geissler         auto response = bus.call(method);
1360d1c3f1fSAndrew Geissler 
1370d1c3f1fSAndrew Geissler         std::variant<std::string> currentPowerState;
1380d1c3f1fSAndrew Geissler         response.read(currentPowerState);
1390d1c3f1fSAndrew Geissler 
1400d1c3f1fSAndrew Geissler         if (std::get<std::string>(currentPowerState) ==
1410d1c3f1fSAndrew Geissler             "xyz.openbmc_project.State.Chassis.PowerState.On")
1420d1c3f1fSAndrew Geissler         {
1430d1c3f1fSAndrew Geissler             return true;
1440d1c3f1fSAndrew Geissler         }
1450d1c3f1fSAndrew Geissler     }
1460a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
1470d1c3f1fSAndrew Geissler     {
148*8ffdb269SAndrew Geissler         error("Error reading Chassis Power State, error: {ERROR}, "
149*8ffdb269SAndrew Geissler               "service: {SERVICE} path: {PATH}",
150*8ffdb269SAndrew Geissler               "ERROR", e, "SERVICE", CHASSIS_STATE_SVC, "PATH",
151*8ffdb269SAndrew Geissler               CHASSIS_STATE_PATH);
1520d1c3f1fSAndrew Geissler         throw;
1530d1c3f1fSAndrew Geissler     }
1540d1c3f1fSAndrew Geissler     return false;
1550d1c3f1fSAndrew Geissler }
1560d1c3f1fSAndrew Geissler 
1570d1c3f1fSAndrew Geissler bool isHostRunning()
1580d1c3f1fSAndrew Geissler {
159*8ffdb269SAndrew Geissler     info("Check if host is running");
1600d1c3f1fSAndrew Geissler 
1610d1c3f1fSAndrew Geissler     auto bus = sdbusplus::bus::new_default();
1620d1c3f1fSAndrew Geissler 
1630d1c3f1fSAndrew Geissler     // No need to check if chassis power is not on
1640d1c3f1fSAndrew Geissler     if (!isChassiPowerOn(bus))
1650d1c3f1fSAndrew Geissler     {
166*8ffdb269SAndrew Geissler         info("Chassis power not on, exit");
1670d1c3f1fSAndrew Geissler         return false;
1680d1c3f1fSAndrew Geissler     }
1690d1c3f1fSAndrew Geissler 
1700d1c3f1fSAndrew Geissler     // This applications systemd service is setup to only run after all other
1710d1c3f1fSAndrew Geissler     // application that could possibly implement the needed interface have
1720d1c3f1fSAndrew Geissler     // been started. However, the use of mapper to find those interfaces means
1730d1c3f1fSAndrew Geissler     // we have a condition where the interface may be on D-Bus but not stored
1740d1c3f1fSAndrew Geissler     // within mapper yet. Keep it simple and just build one retry into the
1750d1c3f1fSAndrew Geissler     // check if it's found the host is not up. This service is only called if
1760d1c3f1fSAndrew Geissler     // chassis power is on when the BMC comes up, so this wont impact most
1770d1c3f1fSAndrew Geissler     // normal cases where the BMC is rebooted with chassis power off. In
1780d1c3f1fSAndrew Geissler     // cases where chassis power is on, the host is likely running so we want
1790d1c3f1fSAndrew Geissler     // to be sure we check all interfaces
1800d1c3f1fSAndrew Geissler     for (int i = 0; i < 2; i++)
1810d1c3f1fSAndrew Geissler     {
1820d1c3f1fSAndrew Geissler         // Give mapper a small window to introspect new objects on bus
1830d1c3f1fSAndrew Geissler         std::this_thread::sleep_for(std::chrono::milliseconds(500));
1840d1c3f1fSAndrew Geissler         if (checkFirmwareConditionRunning(bus))
1850d1c3f1fSAndrew Geissler         {
186*8ffdb269SAndrew Geissler             info("Host is running!");
1870d1c3f1fSAndrew Geissler             // Create file for host instance and create in filesystem to
1880d1c3f1fSAndrew Geissler             // indicate to services that host is running
1890d1c3f1fSAndrew Geissler             auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
1900d1c3f1fSAndrew Geissler             size++; // null
1910d1c3f1fSAndrew Geissler             std::unique_ptr<char[]> buf(new char[size]);
1920d1c3f1fSAndrew Geissler             std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
1930d1c3f1fSAndrew Geissler             std::ofstream outfile(buf.get());
1940d1c3f1fSAndrew Geissler             outfile.close();
1950d1c3f1fSAndrew Geissler             return true;
1960d1c3f1fSAndrew Geissler         }
1970d1c3f1fSAndrew Geissler     }
198*8ffdb269SAndrew Geissler     info("Host is not running!");
1990d1c3f1fSAndrew Geissler     return false;
2000d1c3f1fSAndrew Geissler }
201*8ffdb269SAndrew Geissler 
202*8ffdb269SAndrew Geissler } // namespace manager
203*8ffdb269SAndrew Geissler } // namespace state
204*8ffdb269SAndrew Geissler } // namespace phosphor
205