xref: /openbmc/phosphor-state-manager/host_check.cpp (revision 3ff5a3603df99030a4b9bdfe6da73356eec66f96)
10d1c3f1fSAndrew Geissler #include "config.h"
20d1c3f1fSAndrew Geissler 
30d1c3f1fSAndrew Geissler #include "host_check.hpp"
40d1c3f1fSAndrew Geissler 
50d1c3f1fSAndrew Geissler #include <unistd.h>
60d1c3f1fSAndrew Geissler 
78ffdb269SAndrew Geissler #include <phosphor-logging/lg2.hpp>
80d1c3f1fSAndrew Geissler #include <sdbusplus/bus.hpp>
90d1c3f1fSAndrew Geissler #include <sdbusplus/exception.hpp>
109a286db2SPatrick Williams #include <xyz/openbmc_project/Condition/HostFirmware/client.hpp>
119a286db2SPatrick Williams #include <xyz/openbmc_project/ObjectMapper/client.hpp>
129a286db2SPatrick Williams #include <xyz/openbmc_project/State/Chassis/client.hpp>
130d1c3f1fSAndrew Geissler 
140d1c3f1fSAndrew Geissler #include <cstdio>
150d1c3f1fSAndrew Geissler #include <cstdlib>
1678c066f6SPatrick Williams #include <format>
170d1c3f1fSAndrew Geissler #include <fstream>
180d1c3f1fSAndrew Geissler #include <iostream>
19e9040bb8SPatrick Williams #include <ranges>
200d1c3f1fSAndrew Geissler #include <thread>
210d1c3f1fSAndrew Geissler #include <vector>
220d1c3f1fSAndrew Geissler 
238ffdb269SAndrew Geissler namespace phosphor
248ffdb269SAndrew Geissler {
258ffdb269SAndrew Geissler namespace state
268ffdb269SAndrew Geissler {
278ffdb269SAndrew Geissler namespace manager
288ffdb269SAndrew Geissler {
298ffdb269SAndrew Geissler 
308ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING;
318ffdb269SAndrew Geissler 
320d1c3f1fSAndrew Geissler using namespace std::literals;
339a286db2SPatrick Williams 
349a286db2SPatrick Williams using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
359a286db2SPatrick Williams using Chassis = sdbusplus::client::xyz::openbmc_project::state::Chassis<>;
369a286db2SPatrick Williams using HostFirmware =
379a286db2SPatrick Williams     sdbusplus::client::xyz::openbmc_project::condition::HostFirmware<>;
380d1c3f1fSAndrew Geissler 
390d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host
400d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
410d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
420d1c3f1fSAndrew Geissler 
430d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "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
checkFirmwareConditionRunning(sdbusplus::bus_t & bus)48f053e6feSPatrick Williams bool checkFirmwareConditionRunning(sdbusplus::bus_t& bus)
490d1c3f1fSAndrew Geissler {
500d1c3f1fSAndrew Geissler     // Find all implementations of host firmware condition interface
519a286db2SPatrick Williams     auto mapper = bus.new_method_call(ObjectMapper::default_service,
529a286db2SPatrick Williams                                       ObjectMapper::instance_path,
539a286db2SPatrick Williams                                       ObjectMapper::interface, "GetSubTree");
540d1c3f1fSAndrew Geissler 
559a286db2SPatrick Williams     mapper.append("/", 0, std::vector<std::string>({HostFirmware::interface}));
560d1c3f1fSAndrew Geissler 
570d1c3f1fSAndrew Geissler     std::map<std::string, std::map<std::string, std::vector<std::string>>>
580d1c3f1fSAndrew Geissler         mapperResponse;
590d1c3f1fSAndrew Geissler 
600d1c3f1fSAndrew Geissler     try
610d1c3f1fSAndrew Geissler     {
620d1c3f1fSAndrew Geissler         auto mapperResponseMsg = bus.call(mapper);
630d1c3f1fSAndrew Geissler         mapperResponseMsg.read(mapperResponse);
640d1c3f1fSAndrew Geissler     }
65f053e6feSPatrick Williams     catch (const sdbusplus::exception_t& e)
660d1c3f1fSAndrew Geissler     {
67ad65b2d6SAndrew Geissler         error(
68ad65b2d6SAndrew Geissler             "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}",
698ffdb269SAndrew Geissler             "ERROR", e);
700d1c3f1fSAndrew Geissler         throw;
710d1c3f1fSAndrew Geissler     }
720d1c3f1fSAndrew Geissler 
730d1c3f1fSAndrew Geissler     if (mapperResponse.empty())
740d1c3f1fSAndrew Geissler     {
758ffdb269SAndrew Geissler         info("Mapper response for HostFirmware conditions is empty!");
760d1c3f1fSAndrew Geissler         return false;
770d1c3f1fSAndrew Geissler     }
780d1c3f1fSAndrew Geissler 
790d1c3f1fSAndrew Geissler     // Now read the CurrentFirmwareCondition from all interfaces we found
800d1c3f1fSAndrew Geissler     // Currently there are two implementations of this interface. One by IPMI
810d1c3f1fSAndrew Geissler     // and one by PLDM. The IPMI interface does a realtime check with the host
820d1c3f1fSAndrew Geissler     // when the interface is called. This means if the host is not running,
830d1c3f1fSAndrew Geissler     // we will have to wait for the timeout (currently set to 3 seconds). The
840d1c3f1fSAndrew Geissler     // PLDM interface reads a cached state. The PLDM service does not put itself
850d1c3f1fSAndrew Geissler     // on D-Bus until it has checked with the host. Therefore it's most
860d1c3f1fSAndrew Geissler     // efficient to call the PLDM interface first. Do that by going in reverse
870d1c3f1fSAndrew Geissler     // of the interfaces returned to us (PLDM will be last if available)
88e9040bb8SPatrick Williams     for (const auto& [path, services] : std::views::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");
989a286db2SPatrick Williams                 method.append(HostFirmware::interface, CONDITION_HOST_PROPERTY);
990d1c3f1fSAndrew Geissler 
1000d1c3f1fSAndrew Geissler                 auto response = bus.call(method);
1019a286db2SPatrick Williams                 std::variant<HostFirmware::FirmwareCondition> currentFwCondV;
10280db4752SNodeMan97                 response.read(currentFwCondV);
10380db4752SNodeMan97                 auto currentFwCond =
1049a286db2SPatrick Williams                     std::get<HostFirmware::FirmwareCondition>(currentFwCondV);
1050d1c3f1fSAndrew Geissler 
106ef828c4bSNodeMan97                 info(
107ef828c4bSNodeMan97                     "Read host fw condition {COND_VALUE} from {COND_SERVICE}, {COND_PATH}",
108ef828c4bSNodeMan97                     "COND_VALUE", currentFwCond, "COND_SERVICE", service,
109ef828c4bSNodeMan97                     "COND_PATH", path);
110ef828c4bSNodeMan97 
1119a286db2SPatrick Williams                 if (currentFwCond == HostFirmware::FirmwareCondition::Running)
1120d1c3f1fSAndrew Geissler                 {
1130d1c3f1fSAndrew Geissler                     return true;
1140d1c3f1fSAndrew Geissler                 }
1150d1c3f1fSAndrew Geissler             }
116f053e6feSPatrick Williams             catch (const sdbusplus::exception_t& e)
1170d1c3f1fSAndrew Geissler             {
1188ffdb269SAndrew Geissler                 error("Error reading HostFirmware condition, error: {ERROR}, "
1198ffdb269SAndrew Geissler                       "service: {SERVICE} path: {PATH}",
1208ffdb269SAndrew Geissler                       "ERROR", e, "SERVICE", service, "PATH", path);
1210d1c3f1fSAndrew Geissler                 throw;
1220d1c3f1fSAndrew Geissler             }
1230d1c3f1fSAndrew Geissler         }
1240d1c3f1fSAndrew Geissler     }
1250d1c3f1fSAndrew Geissler     return false;
1260d1c3f1fSAndrew Geissler }
1270d1c3f1fSAndrew Geissler 
1280d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on
isChassiPowerOn(sdbusplus::bus_t & bus,size_t id)129f053e6feSPatrick Williams bool isChassiPowerOn(sdbusplus::bus_t& bus, size_t id)
1300d1c3f1fSAndrew Geissler {
13107e73aaaSPotin Lai     auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id);
1329a286db2SPatrick Williams     auto objpath = std::string{Chassis::namespace_path::value} + "/" +
1339a286db2SPatrick Williams                    std::string{Chassis::namespace_path::chassis} +
1349a286db2SPatrick Williams                    std::to_string(id);
13570f36d8eSPotin Lai 
1360d1c3f1fSAndrew Geissler     try
1370d1c3f1fSAndrew Geissler     {
13870f36d8eSPotin Lai         auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(),
1390d1c3f1fSAndrew Geissler                                           PROPERTY_INTERFACE, "Get");
1409a286db2SPatrick Williams         method.append(Chassis::interface, CHASSIS_STATE_POWER_PROP);
1410d1c3f1fSAndrew Geissler 
1420d1c3f1fSAndrew Geissler         auto response = bus.call(method);
1439a286db2SPatrick Williams         std::variant<Chassis::PowerState> currentPowerStateV;
14480db4752SNodeMan97         response.read(currentPowerStateV);
145ef828c4bSNodeMan97 
1469a286db2SPatrick Williams         auto currentPowerState =
1479a286db2SPatrick Williams             std::get<Chassis::PowerState>(currentPowerStateV);
1480d1c3f1fSAndrew Geissler 
1499a286db2SPatrick Williams         if (currentPowerState == Chassis::PowerState::On)
1500d1c3f1fSAndrew Geissler         {
1510d1c3f1fSAndrew Geissler             return true;
1520d1c3f1fSAndrew Geissler         }
1530d1c3f1fSAndrew Geissler     }
154f053e6feSPatrick Williams     catch (const sdbusplus::exception_t& e)
1550d1c3f1fSAndrew Geissler     {
1568ffdb269SAndrew Geissler         error("Error reading Chassis Power State, error: {ERROR}, "
1578ffdb269SAndrew Geissler               "service: {SERVICE} path: {PATH}",
15870f36d8eSPotin Lai               "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str());
1590d1c3f1fSAndrew Geissler         throw;
1600d1c3f1fSAndrew Geissler     }
1610d1c3f1fSAndrew Geissler     return false;
1620d1c3f1fSAndrew Geissler }
1630d1c3f1fSAndrew Geissler 
isHostRunning(size_t id)16470f36d8eSPotin Lai bool isHostRunning(size_t id)
1650d1c3f1fSAndrew Geissler {
1668ffdb269SAndrew Geissler     info("Check if host is running");
1670d1c3f1fSAndrew Geissler 
1680d1c3f1fSAndrew Geissler     auto bus = sdbusplus::bus::new_default();
1690d1c3f1fSAndrew Geissler 
1700d1c3f1fSAndrew Geissler     // No need to check if chassis power is not on
17170f36d8eSPotin Lai     if (!isChassiPowerOn(bus, id))
1720d1c3f1fSAndrew Geissler     {
1738ffdb269SAndrew Geissler         info("Chassis power not on, exit");
1740d1c3f1fSAndrew Geissler         return false;
1750d1c3f1fSAndrew Geissler     }
1760d1c3f1fSAndrew Geissler 
1770d1c3f1fSAndrew Geissler     // This applications systemd service is setup to only run after all other
1780d1c3f1fSAndrew Geissler     // application that could possibly implement the needed interface have
1790d1c3f1fSAndrew Geissler     // been started. However, the use of mapper to find those interfaces means
1800d1c3f1fSAndrew Geissler     // we have a condition where the interface may be on D-Bus but not stored
181ef828c4bSNodeMan97     // within mapper yet. There are five built in retries to check if it's
182ef828c4bSNodeMan97     // found the host is not up. This service is only called if chassis power
183*3ff5a360SManojkiran Eda     // is on when the BMC comes up, so this won't impact most normal cases
184ef828c4bSNodeMan97     // where the BMC is rebooted with chassis power off. In cases where
185ef828c4bSNodeMan97     // chassis power is on, the host is likely running so we want to be sure
186ef828c4bSNodeMan97     // we check all interfaces
187ef828c4bSNodeMan97     for (int i = 0; i < 5; i++)
1880d1c3f1fSAndrew Geissler     {
189ef828c4bSNodeMan97         debug(
190ef828c4bSNodeMan97             "Introspecting new bus objects for bus id: {ID} sleeping for 1 second.",
191ef828c4bSNodeMan97             "ID", id);
1920d1c3f1fSAndrew Geissler         // Give mapper a small window to introspect new objects on bus
193ef828c4bSNodeMan97         std::this_thread::sleep_for(std::chrono::milliseconds(1000));
1940d1c3f1fSAndrew Geissler         if (checkFirmwareConditionRunning(bus))
1950d1c3f1fSAndrew Geissler         {
1968ffdb269SAndrew Geissler             info("Host is running!");
1970d1c3f1fSAndrew Geissler             // Create file for host instance and create in filesystem to
1980d1c3f1fSAndrew Geissler             // indicate to services that host is running
19978c066f6SPatrick Williams             std::string hostFile = std::format(HOST_RUNNING_FILE, 0);
20078c066f6SPatrick Williams             std::ofstream outfile(hostFile);
2010d1c3f1fSAndrew Geissler             outfile.close();
2020d1c3f1fSAndrew Geissler             return true;
2030d1c3f1fSAndrew Geissler         }
2040d1c3f1fSAndrew Geissler     }
2058ffdb269SAndrew Geissler     info("Host is not running!");
2060d1c3f1fSAndrew Geissler     return false;
2070d1c3f1fSAndrew Geissler }
2088ffdb269SAndrew Geissler 
2098ffdb269SAndrew Geissler } // namespace manager
2108ffdb269SAndrew Geissler } // namespace state
2118ffdb269SAndrew Geissler } // namespace phosphor
212