xref: /openbmc/phosphor-state-manager/host_check.cpp (revision 80db4752d278247ad69ed45bb81f8113eb730e00)
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>
88ffdb269SAndrew 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>
12*80db4752SNodeMan97 #include <xyz/openbmc_project/State/Chassis/server.hpp>
130d1c3f1fSAndrew Geissler 
140d1c3f1fSAndrew Geissler #include <cstdio>
150d1c3f1fSAndrew Geissler #include <cstdlib>
160d1c3f1fSAndrew Geissler #include <fstream>
170d1c3f1fSAndrew Geissler #include <iostream>
180d1c3f1fSAndrew Geissler #include <thread>
190d1c3f1fSAndrew Geissler #include <vector>
200d1c3f1fSAndrew Geissler 
218ffdb269SAndrew Geissler namespace phosphor
228ffdb269SAndrew Geissler {
238ffdb269SAndrew Geissler namespace state
248ffdb269SAndrew Geissler {
258ffdb269SAndrew Geissler namespace manager
268ffdb269SAndrew Geissler {
278ffdb269SAndrew Geissler 
288ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING;
298ffdb269SAndrew Geissler 
300d1c3f1fSAndrew Geissler using namespace std::literals;
310d1c3f1fSAndrew Geissler using namespace sdbusplus::xyz::openbmc_project::Condition::server;
320d1c3f1fSAndrew Geissler 
330d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host
340d1c3f1fSAndrew Geissler constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
350d1c3f1fSAndrew Geissler constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
360d1c3f1fSAndrew Geissler constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
370d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_INTERFACE =
380d1c3f1fSAndrew Geissler     "xyz.openbmc_project.Condition.HostFirmware";
390d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
400d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
410d1c3f1fSAndrew Geissler 
420d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
4370f36d8eSPotin Lai constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis";
440d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis";
450d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
460d1c3f1fSAndrew Geissler 
470d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is
480d1c3f1fSAndrew Geissler // running over it
490d1c3f1fSAndrew Geissler bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus)
500d1c3f1fSAndrew Geissler {
51*80db4752SNodeMan97     using FirmwareCondition = HostFirmware::FirmwareCondition;
520d1c3f1fSAndrew Geissler     // Find all implementations of host firmware condition interface
530d1c3f1fSAndrew Geissler     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
540d1c3f1fSAndrew Geissler                                       MAPPER_INTERFACE, "GetSubTree");
550d1c3f1fSAndrew Geissler 
560d1c3f1fSAndrew Geissler     mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE}));
570d1c3f1fSAndrew Geissler 
580d1c3f1fSAndrew Geissler     std::map<std::string, std::map<std::string, std::vector<std::string>>>
590d1c3f1fSAndrew Geissler         mapperResponse;
600d1c3f1fSAndrew Geissler 
610d1c3f1fSAndrew Geissler     try
620d1c3f1fSAndrew Geissler     {
630d1c3f1fSAndrew Geissler         auto mapperResponseMsg = bus.call(mapper);
640d1c3f1fSAndrew Geissler         mapperResponseMsg.read(mapperResponse);
650d1c3f1fSAndrew Geissler     }
660a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
670d1c3f1fSAndrew Geissler     {
68ad65b2d6SAndrew Geissler         error(
69ad65b2d6SAndrew Geissler             "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}",
708ffdb269SAndrew Geissler             "ERROR", e);
710d1c3f1fSAndrew Geissler         throw;
720d1c3f1fSAndrew Geissler     }
730d1c3f1fSAndrew Geissler 
740d1c3f1fSAndrew Geissler     if (mapperResponse.empty())
750d1c3f1fSAndrew Geissler     {
768ffdb269SAndrew Geissler         info("Mapper response for HostFirmware conditions is empty!");
770d1c3f1fSAndrew Geissler         return false;
780d1c3f1fSAndrew Geissler     }
790d1c3f1fSAndrew Geissler 
800d1c3f1fSAndrew Geissler     // Now read the CurrentFirmwareCondition from all interfaces we found
810d1c3f1fSAndrew Geissler     // Currently there are two implementations of this interface. One by IPMI
820d1c3f1fSAndrew Geissler     // and one by PLDM. The IPMI interface does a realtime check with the host
830d1c3f1fSAndrew Geissler     // when the interface is called. This means if the host is not running,
840d1c3f1fSAndrew Geissler     // we will have to wait for the timeout (currently set to 3 seconds). The
850d1c3f1fSAndrew Geissler     // PLDM interface reads a cached state. The PLDM service does not put itself
860d1c3f1fSAndrew Geissler     // on D-Bus until it has checked with the host. Therefore it's most
870d1c3f1fSAndrew Geissler     // efficient to call the PLDM interface first. Do that by going in reverse
880d1c3f1fSAndrew Geissler     // of the interfaces returned to us (PLDM will be last if available)
890d1c3f1fSAndrew Geissler     for (const auto& [path, services] :
900d1c3f1fSAndrew Geissler          boost::adaptors::reverse(mapperResponse))
910d1c3f1fSAndrew Geissler     {
920d1c3f1fSAndrew Geissler         for (const auto& serviceIter : services)
930d1c3f1fSAndrew Geissler         {
940d1c3f1fSAndrew Geissler             const std::string& service = serviceIter.first;
950d1c3f1fSAndrew Geissler 
960d1c3f1fSAndrew Geissler             try
970d1c3f1fSAndrew Geissler             {
980d1c3f1fSAndrew Geissler                 auto method = bus.new_method_call(service.c_str(), path.c_str(),
990d1c3f1fSAndrew Geissler                                                   PROPERTY_INTERFACE, "Get");
1000d1c3f1fSAndrew Geissler                 method.append(CONDITION_HOST_INTERFACE,
1010d1c3f1fSAndrew Geissler                               CONDITION_HOST_PROPERTY);
1020d1c3f1fSAndrew Geissler 
1030d1c3f1fSAndrew Geissler                 auto response = bus.call(method);
1040d1c3f1fSAndrew Geissler 
105*80db4752SNodeMan97                 std::variant<FirmwareCondition> currentFwCondV;
106*80db4752SNodeMan97                 response.read(currentFwCondV);
107*80db4752SNodeMan97                 auto currentFwCond =
108*80db4752SNodeMan97                     std::get<FirmwareCondition>(currentFwCondV);
1090d1c3f1fSAndrew Geissler 
110*80db4752SNodeMan97                 if (currentFwCond == FirmwareCondition::Running)
1110d1c3f1fSAndrew Geissler                 {
1120d1c3f1fSAndrew Geissler                     return true;
1130d1c3f1fSAndrew Geissler                 }
1140d1c3f1fSAndrew Geissler             }
1150a675215SPatrick Williams             catch (const sdbusplus::exception::exception& e)
1160d1c3f1fSAndrew Geissler             {
1178ffdb269SAndrew Geissler                 error("Error reading HostFirmware condition, error: {ERROR}, "
1188ffdb269SAndrew Geissler                       "service: {SERVICE} path: {PATH}",
1198ffdb269SAndrew Geissler                       "ERROR", e, "SERVICE", service, "PATH", path);
1200d1c3f1fSAndrew Geissler                 throw;
1210d1c3f1fSAndrew Geissler             }
1220d1c3f1fSAndrew Geissler         }
1230d1c3f1fSAndrew Geissler     }
1240d1c3f1fSAndrew Geissler     return false;
1250d1c3f1fSAndrew Geissler }
1260d1c3f1fSAndrew Geissler 
1270d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on
12870f36d8eSPotin Lai bool isChassiPowerOn(sdbusplus::bus::bus& bus, size_t id)
1290d1c3f1fSAndrew Geissler {
13007e73aaaSPotin Lai     auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id);
13107e73aaaSPotin Lai     auto objpath = std::string{CHASSIS_STATE_PATH} + std::to_string(id);
13270f36d8eSPotin Lai 
1330d1c3f1fSAndrew Geissler     try
1340d1c3f1fSAndrew Geissler     {
135*80db4752SNodeMan97         using PowerState =
136*80db4752SNodeMan97             sdbusplus::xyz::openbmc_project::State::server::Chassis::PowerState;
13770f36d8eSPotin Lai         auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(),
1380d1c3f1fSAndrew Geissler                                           PROPERTY_INTERFACE, "Get");
1390d1c3f1fSAndrew Geissler         method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
1400d1c3f1fSAndrew Geissler 
1410d1c3f1fSAndrew Geissler         auto response = bus.call(method);
1420d1c3f1fSAndrew Geissler 
143*80db4752SNodeMan97         std::variant<PowerState> currentPowerStateV;
144*80db4752SNodeMan97         response.read(currentPowerStateV);
145*80db4752SNodeMan97         auto currentPowerState = std::get<PowerState>(currentPowerStateV);
1460d1c3f1fSAndrew Geissler 
147*80db4752SNodeMan97         if (currentPowerState == PowerState::On)
1480d1c3f1fSAndrew Geissler         {
1490d1c3f1fSAndrew Geissler             return true;
1500d1c3f1fSAndrew Geissler         }
1510d1c3f1fSAndrew Geissler     }
1520a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
1530d1c3f1fSAndrew Geissler     {
1548ffdb269SAndrew Geissler         error("Error reading Chassis Power State, error: {ERROR}, "
1558ffdb269SAndrew Geissler               "service: {SERVICE} path: {PATH}",
15670f36d8eSPotin Lai               "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str());
1570d1c3f1fSAndrew Geissler         throw;
1580d1c3f1fSAndrew Geissler     }
1590d1c3f1fSAndrew Geissler     return false;
1600d1c3f1fSAndrew Geissler }
1610d1c3f1fSAndrew Geissler 
16270f36d8eSPotin Lai bool isHostRunning(size_t id)
1630d1c3f1fSAndrew Geissler {
1648ffdb269SAndrew Geissler     info("Check if host is running");
1650d1c3f1fSAndrew Geissler 
1660d1c3f1fSAndrew Geissler     auto bus = sdbusplus::bus::new_default();
1670d1c3f1fSAndrew Geissler 
1680d1c3f1fSAndrew Geissler     // No need to check if chassis power is not on
16970f36d8eSPotin Lai     if (!isChassiPowerOn(bus, id))
1700d1c3f1fSAndrew Geissler     {
1718ffdb269SAndrew Geissler         info("Chassis power not on, exit");
1720d1c3f1fSAndrew Geissler         return false;
1730d1c3f1fSAndrew Geissler     }
1740d1c3f1fSAndrew Geissler 
1750d1c3f1fSAndrew Geissler     // This applications systemd service is setup to only run after all other
1760d1c3f1fSAndrew Geissler     // application that could possibly implement the needed interface have
1770d1c3f1fSAndrew Geissler     // been started. However, the use of mapper to find those interfaces means
1780d1c3f1fSAndrew Geissler     // we have a condition where the interface may be on D-Bus but not stored
1790d1c3f1fSAndrew Geissler     // within mapper yet. Keep it simple and just build one retry into the
1800d1c3f1fSAndrew Geissler     // check if it's found the host is not up. This service is only called if
1810d1c3f1fSAndrew Geissler     // chassis power is on when the BMC comes up, so this wont impact most
1820d1c3f1fSAndrew Geissler     // normal cases where the BMC is rebooted with chassis power off. In
1830d1c3f1fSAndrew Geissler     // cases where chassis power is on, the host is likely running so we want
1840d1c3f1fSAndrew Geissler     // to be sure we check all interfaces
1850d1c3f1fSAndrew Geissler     for (int i = 0; i < 2; i++)
1860d1c3f1fSAndrew Geissler     {
1870d1c3f1fSAndrew Geissler         // Give mapper a small window to introspect new objects on bus
1880d1c3f1fSAndrew Geissler         std::this_thread::sleep_for(std::chrono::milliseconds(500));
1890d1c3f1fSAndrew Geissler         if (checkFirmwareConditionRunning(bus))
1900d1c3f1fSAndrew Geissler         {
1918ffdb269SAndrew Geissler             info("Host is running!");
1920d1c3f1fSAndrew Geissler             // Create file for host instance and create in filesystem to
1930d1c3f1fSAndrew Geissler             // indicate to services that host is running
1940d1c3f1fSAndrew Geissler             auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
1950d1c3f1fSAndrew Geissler             size++; // null
1960d1c3f1fSAndrew Geissler             std::unique_ptr<char[]> buf(new char[size]);
1970d1c3f1fSAndrew Geissler             std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
1980d1c3f1fSAndrew Geissler             std::ofstream outfile(buf.get());
1990d1c3f1fSAndrew Geissler             outfile.close();
2000d1c3f1fSAndrew Geissler             return true;
2010d1c3f1fSAndrew Geissler         }
2020d1c3f1fSAndrew Geissler     }
2038ffdb269SAndrew Geissler     info("Host is not running!");
2040d1c3f1fSAndrew Geissler     return false;
2050d1c3f1fSAndrew Geissler }
2068ffdb269SAndrew Geissler 
2078ffdb269SAndrew Geissler } // namespace manager
2088ffdb269SAndrew Geissler } // namespace state
2098ffdb269SAndrew Geissler } // namespace phosphor
210