xref: /openbmc/phosphor-state-manager/host_check.cpp (revision ef828c4b4ea6b18a5869bd1882175970bc048021)
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>
1280db4752SNodeMan97 #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 {
5180db4752SNodeMan97     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);
10480db4752SNodeMan97                 std::variant<FirmwareCondition> currentFwCondV;
10580db4752SNodeMan97                 response.read(currentFwCondV);
10680db4752SNodeMan97                 auto currentFwCond =
10780db4752SNodeMan97                     std::get<FirmwareCondition>(currentFwCondV);
1080d1c3f1fSAndrew Geissler 
109*ef828c4bSNodeMan97                 info(
110*ef828c4bSNodeMan97                     "Read host fw condition {COND_VALUE} from {COND_SERVICE}, {COND_PATH}",
111*ef828c4bSNodeMan97                     "COND_VALUE", currentFwCond, "COND_SERVICE", service,
112*ef828c4bSNodeMan97                     "COND_PATH", path);
113*ef828c4bSNodeMan97 
11480db4752SNodeMan97                 if (currentFwCond == FirmwareCondition::Running)
1150d1c3f1fSAndrew Geissler                 {
1160d1c3f1fSAndrew Geissler                     return true;
1170d1c3f1fSAndrew Geissler                 }
1180d1c3f1fSAndrew Geissler             }
1190a675215SPatrick Williams             catch (const sdbusplus::exception::exception& e)
1200d1c3f1fSAndrew Geissler             {
1218ffdb269SAndrew Geissler                 error("Error reading HostFirmware condition, error: {ERROR}, "
1228ffdb269SAndrew Geissler                       "service: {SERVICE} path: {PATH}",
1238ffdb269SAndrew Geissler                       "ERROR", e, "SERVICE", service, "PATH", path);
1240d1c3f1fSAndrew Geissler                 throw;
1250d1c3f1fSAndrew Geissler             }
1260d1c3f1fSAndrew Geissler         }
1270d1c3f1fSAndrew Geissler     }
1280d1c3f1fSAndrew Geissler     return false;
1290d1c3f1fSAndrew Geissler }
1300d1c3f1fSAndrew Geissler 
1310d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on
13270f36d8eSPotin Lai bool isChassiPowerOn(sdbusplus::bus::bus& bus, size_t id)
1330d1c3f1fSAndrew Geissler {
13407e73aaaSPotin Lai     auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id);
13507e73aaaSPotin Lai     auto objpath = std::string{CHASSIS_STATE_PATH} + std::to_string(id);
13670f36d8eSPotin Lai 
1370d1c3f1fSAndrew Geissler     try
1380d1c3f1fSAndrew Geissler     {
13980db4752SNodeMan97         using PowerState =
14080db4752SNodeMan97             sdbusplus::xyz::openbmc_project::State::server::Chassis::PowerState;
14170f36d8eSPotin Lai         auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(),
1420d1c3f1fSAndrew Geissler                                           PROPERTY_INTERFACE, "Get");
1430d1c3f1fSAndrew Geissler         method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP);
1440d1c3f1fSAndrew Geissler 
1450d1c3f1fSAndrew Geissler         auto response = bus.call(method);
14680db4752SNodeMan97         std::variant<PowerState> currentPowerStateV;
14780db4752SNodeMan97         response.read(currentPowerStateV);
148*ef828c4bSNodeMan97 
14980db4752SNodeMan97         auto currentPowerState = std::get<PowerState>(currentPowerStateV);
1500d1c3f1fSAndrew Geissler 
15180db4752SNodeMan97         if (currentPowerState == PowerState::On)
1520d1c3f1fSAndrew Geissler         {
1530d1c3f1fSAndrew Geissler             return true;
1540d1c3f1fSAndrew Geissler         }
1550d1c3f1fSAndrew Geissler     }
1560a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
1570d1c3f1fSAndrew Geissler     {
1588ffdb269SAndrew Geissler         error("Error reading Chassis Power State, error: {ERROR}, "
1598ffdb269SAndrew Geissler               "service: {SERVICE} path: {PATH}",
16070f36d8eSPotin Lai               "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str());
1610d1c3f1fSAndrew Geissler         throw;
1620d1c3f1fSAndrew Geissler     }
1630d1c3f1fSAndrew Geissler     return false;
1640d1c3f1fSAndrew Geissler }
1650d1c3f1fSAndrew Geissler 
16670f36d8eSPotin Lai bool isHostRunning(size_t id)
1670d1c3f1fSAndrew Geissler {
1688ffdb269SAndrew Geissler     info("Check if host is running");
1690d1c3f1fSAndrew Geissler 
1700d1c3f1fSAndrew Geissler     auto bus = sdbusplus::bus::new_default();
1710d1c3f1fSAndrew Geissler 
1720d1c3f1fSAndrew Geissler     // No need to check if chassis power is not on
17370f36d8eSPotin Lai     if (!isChassiPowerOn(bus, id))
1740d1c3f1fSAndrew Geissler     {
1758ffdb269SAndrew Geissler         info("Chassis power not on, exit");
1760d1c3f1fSAndrew Geissler         return false;
1770d1c3f1fSAndrew Geissler     }
1780d1c3f1fSAndrew Geissler 
1790d1c3f1fSAndrew Geissler     // This applications systemd service is setup to only run after all other
1800d1c3f1fSAndrew Geissler     // application that could possibly implement the needed interface have
1810d1c3f1fSAndrew Geissler     // been started. However, the use of mapper to find those interfaces means
1820d1c3f1fSAndrew Geissler     // we have a condition where the interface may be on D-Bus but not stored
183*ef828c4bSNodeMan97     // within mapper yet. There are five built in retries to check if it's
184*ef828c4bSNodeMan97     // found the host is not up. This service is only called if chassis power
185*ef828c4bSNodeMan97     // is on when the BMC comes up, so this wont impact most normal cases
186*ef828c4bSNodeMan97     // where the BMC is rebooted with chassis power off. In cases where
187*ef828c4bSNodeMan97     // chassis power is on, the host is likely running so we want to be sure
188*ef828c4bSNodeMan97     // we check all interfaces
189*ef828c4bSNodeMan97     for (int i = 0; i < 5; i++)
1900d1c3f1fSAndrew Geissler     {
191*ef828c4bSNodeMan97         debug(
192*ef828c4bSNodeMan97             "Introspecting new bus objects for bus id: {ID} sleeping for 1 second.",
193*ef828c4bSNodeMan97             "ID", id);
1940d1c3f1fSAndrew Geissler         // Give mapper a small window to introspect new objects on bus
195*ef828c4bSNodeMan97         std::this_thread::sleep_for(std::chrono::milliseconds(1000));
1960d1c3f1fSAndrew Geissler         if (checkFirmwareConditionRunning(bus))
1970d1c3f1fSAndrew Geissler         {
1988ffdb269SAndrew Geissler             info("Host is running!");
1990d1c3f1fSAndrew Geissler             // Create file for host instance and create in filesystem to
2000d1c3f1fSAndrew Geissler             // indicate to services that host is running
2010d1c3f1fSAndrew Geissler             auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
2020d1c3f1fSAndrew Geissler             size++; // null
2030d1c3f1fSAndrew Geissler             std::unique_ptr<char[]> buf(new char[size]);
2040d1c3f1fSAndrew Geissler             std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
2050d1c3f1fSAndrew Geissler             std::ofstream outfile(buf.get());
2060d1c3f1fSAndrew Geissler             outfile.close();
2070d1c3f1fSAndrew Geissler             return true;
2080d1c3f1fSAndrew Geissler         }
2090d1c3f1fSAndrew Geissler     }
2108ffdb269SAndrew Geissler     info("Host is not running!");
2110d1c3f1fSAndrew Geissler     return false;
2120d1c3f1fSAndrew Geissler }
2138ffdb269SAndrew Geissler 
2148ffdb269SAndrew Geissler } // namespace manager
2158ffdb269SAndrew Geissler } // namespace state
2168ffdb269SAndrew Geissler } // namespace phosphor
217