xref: /openbmc/phosphor-state-manager/host_check.cpp (revision 9a286db223cecd88ecdcf29e6a1e713fdd1bf115)
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>
11*9a286db2SPatrick Williams #include <xyz/openbmc_project/Condition/HostFirmware/client.hpp>
12*9a286db2SPatrick Williams #include <xyz/openbmc_project/ObjectMapper/client.hpp>
13*9a286db2SPatrick Williams #include <xyz/openbmc_project/State/Chassis/client.hpp>
140d1c3f1fSAndrew Geissler 
150d1c3f1fSAndrew Geissler #include <cstdio>
160d1c3f1fSAndrew Geissler #include <cstdlib>
170d1c3f1fSAndrew Geissler #include <fstream>
180d1c3f1fSAndrew Geissler #include <iostream>
190d1c3f1fSAndrew Geissler #include <thread>
200d1c3f1fSAndrew Geissler #include <vector>
210d1c3f1fSAndrew Geissler 
228ffdb269SAndrew Geissler namespace phosphor
238ffdb269SAndrew Geissler {
248ffdb269SAndrew Geissler namespace state
258ffdb269SAndrew Geissler {
268ffdb269SAndrew Geissler namespace manager
278ffdb269SAndrew Geissler {
288ffdb269SAndrew Geissler 
298ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING;
308ffdb269SAndrew Geissler 
310d1c3f1fSAndrew Geissler using namespace std::literals;
32*9a286db2SPatrick Williams 
33*9a286db2SPatrick Williams using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
34*9a286db2SPatrick Williams using Chassis = sdbusplus::client::xyz::openbmc_project::state::Chassis<>;
35*9a286db2SPatrick Williams using HostFirmware =
36*9a286db2SPatrick Williams     sdbusplus::client::xyz::openbmc_project::condition::HostFirmware<>;
370d1c3f1fSAndrew Geissler 
380d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host
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";
430d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
440d1c3f1fSAndrew Geissler 
450d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is
460d1c3f1fSAndrew Geissler // running over it
47f053e6feSPatrick Williams bool checkFirmwareConditionRunning(sdbusplus::bus_t& bus)
480d1c3f1fSAndrew Geissler {
490d1c3f1fSAndrew Geissler     // Find all implementations of host firmware condition interface
50*9a286db2SPatrick Williams     auto mapper = bus.new_method_call(ObjectMapper::default_service,
51*9a286db2SPatrick Williams                                       ObjectMapper::instance_path,
52*9a286db2SPatrick Williams                                       ObjectMapper::interface, "GetSubTree");
530d1c3f1fSAndrew Geissler 
54*9a286db2SPatrick Williams     mapper.append("/", 0, std::vector<std::string>({HostFirmware::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     }
64f053e6feSPatrick Williams     catch (const sdbusplus::exception_t& e)
650d1c3f1fSAndrew Geissler     {
66ad65b2d6SAndrew Geissler         error(
67ad65b2d6SAndrew Geissler             "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}",
688ffdb269SAndrew Geissler             "ERROR", e);
690d1c3f1fSAndrew Geissler         throw;
700d1c3f1fSAndrew Geissler     }
710d1c3f1fSAndrew Geissler 
720d1c3f1fSAndrew Geissler     if (mapperResponse.empty())
730d1c3f1fSAndrew Geissler     {
748ffdb269SAndrew 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");
98*9a286db2SPatrick Williams                 method.append(HostFirmware::interface, CONDITION_HOST_PROPERTY);
990d1c3f1fSAndrew Geissler 
1000d1c3f1fSAndrew Geissler                 auto response = bus.call(method);
101*9a286db2SPatrick Williams                 std::variant<HostFirmware::FirmwareCondition> currentFwCondV;
10280db4752SNodeMan97                 response.read(currentFwCondV);
10380db4752SNodeMan97                 auto currentFwCond =
104*9a286db2SPatrick 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 
111*9a286db2SPatrick 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
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);
132*9a286db2SPatrick Williams     auto objpath = std::string{Chassis::namespace_path::value} + "/" +
133*9a286db2SPatrick Williams                    std::string{Chassis::namespace_path::chassis} +
134*9a286db2SPatrick 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");
140*9a286db2SPatrick Williams         method.append(Chassis::interface, CHASSIS_STATE_POWER_PROP);
1410d1c3f1fSAndrew Geissler 
1420d1c3f1fSAndrew Geissler         auto response = bus.call(method);
143*9a286db2SPatrick Williams         std::variant<Chassis::PowerState> currentPowerStateV;
14480db4752SNodeMan97         response.read(currentPowerStateV);
145ef828c4bSNodeMan97 
146*9a286db2SPatrick Williams         auto currentPowerState =
147*9a286db2SPatrick Williams             std::get<Chassis::PowerState>(currentPowerStateV);
1480d1c3f1fSAndrew Geissler 
149*9a286db2SPatrick 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 
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
183ef828c4bSNodeMan97     // is on when the BMC comes up, so this wont 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
1990d1c3f1fSAndrew Geissler             auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
2000d1c3f1fSAndrew Geissler             size++; // null
2010d1c3f1fSAndrew Geissler             std::unique_ptr<char[]> buf(new char[size]);
2020d1c3f1fSAndrew Geissler             std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
2030d1c3f1fSAndrew Geissler             std::ofstream outfile(buf.get());
2040d1c3f1fSAndrew Geissler             outfile.close();
2050d1c3f1fSAndrew Geissler             return true;
2060d1c3f1fSAndrew Geissler         }
2070d1c3f1fSAndrew Geissler     }
2088ffdb269SAndrew Geissler     info("Host is not running!");
2090d1c3f1fSAndrew Geissler     return false;
2100d1c3f1fSAndrew Geissler }
2118ffdb269SAndrew Geissler 
2128ffdb269SAndrew Geissler } // namespace manager
2138ffdb269SAndrew Geissler } // namespace state
2148ffdb269SAndrew Geissler } // namespace phosphor
215