1*0d1c3f1fSAndrew Geissler #include "config.h" 2*0d1c3f1fSAndrew Geissler 3*0d1c3f1fSAndrew Geissler #include "host_check.hpp" 4*0d1c3f1fSAndrew Geissler 5*0d1c3f1fSAndrew Geissler #include <unistd.h> 6*0d1c3f1fSAndrew Geissler 7*0d1c3f1fSAndrew Geissler #include <boost/range/adaptor/reversed.hpp> 8*0d1c3f1fSAndrew Geissler #include <phosphor-logging/log.hpp> 9*0d1c3f1fSAndrew Geissler #include <sdbusplus/bus.hpp> 10*0d1c3f1fSAndrew Geissler #include <sdbusplus/exception.hpp> 11*0d1c3f1fSAndrew Geissler #include <xyz/openbmc_project/Condition/HostFirmware/server.hpp> 12*0d1c3f1fSAndrew Geissler 13*0d1c3f1fSAndrew Geissler #include <cstdio> 14*0d1c3f1fSAndrew Geissler #include <cstdlib> 15*0d1c3f1fSAndrew Geissler #include <fstream> 16*0d1c3f1fSAndrew Geissler #include <iostream> 17*0d1c3f1fSAndrew Geissler #include <thread> 18*0d1c3f1fSAndrew Geissler #include <vector> 19*0d1c3f1fSAndrew Geissler 20*0d1c3f1fSAndrew Geissler using namespace std::literals; 21*0d1c3f1fSAndrew Geissler using namespace phosphor::logging; 22*0d1c3f1fSAndrew Geissler using namespace sdbusplus::xyz::openbmc_project::Condition::server; 23*0d1c3f1fSAndrew Geissler using sdbusplus::exception::SdBusError; 24*0d1c3f1fSAndrew Geissler 25*0d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host 26*0d1c3f1fSAndrew Geissler constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 27*0d1c3f1fSAndrew Geissler constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 28*0d1c3f1fSAndrew Geissler constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 29*0d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_INTERFACE = 30*0d1c3f1fSAndrew Geissler "xyz.openbmc_project.Condition.HostFirmware"; 31*0d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition"; 32*0d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 33*0d1c3f1fSAndrew Geissler 34*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis"; 35*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0"; 36*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis"; 37*0d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState"; 38*0d1c3f1fSAndrew Geissler 39*0d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is 40*0d1c3f1fSAndrew Geissler // running over it 41*0d1c3f1fSAndrew Geissler bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus) 42*0d1c3f1fSAndrew Geissler { 43*0d1c3f1fSAndrew Geissler // Find all implementations of host firmware condition interface 44*0d1c3f1fSAndrew Geissler auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 45*0d1c3f1fSAndrew Geissler MAPPER_INTERFACE, "GetSubTree"); 46*0d1c3f1fSAndrew Geissler 47*0d1c3f1fSAndrew Geissler mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE})); 48*0d1c3f1fSAndrew Geissler 49*0d1c3f1fSAndrew Geissler std::map<std::string, std::map<std::string, std::vector<std::string>>> 50*0d1c3f1fSAndrew Geissler mapperResponse; 51*0d1c3f1fSAndrew Geissler 52*0d1c3f1fSAndrew Geissler try 53*0d1c3f1fSAndrew Geissler { 54*0d1c3f1fSAndrew Geissler auto mapperResponseMsg = bus.call(mapper); 55*0d1c3f1fSAndrew Geissler mapperResponseMsg.read(mapperResponse); 56*0d1c3f1fSAndrew Geissler } 57*0d1c3f1fSAndrew Geissler catch (const SdBusError& e) 58*0d1c3f1fSAndrew Geissler { 59*0d1c3f1fSAndrew Geissler log<level::ERR>( 60*0d1c3f1fSAndrew Geissler "Error in mapper GetSubTree call for HostFirmware condition", 61*0d1c3f1fSAndrew Geissler entry("ERROR=%s", e.what())); 62*0d1c3f1fSAndrew Geissler throw; 63*0d1c3f1fSAndrew Geissler } 64*0d1c3f1fSAndrew Geissler 65*0d1c3f1fSAndrew Geissler if (mapperResponse.empty()) 66*0d1c3f1fSAndrew Geissler { 67*0d1c3f1fSAndrew Geissler log<level::INFO>( 68*0d1c3f1fSAndrew Geissler "Mapper response for HostFirmware conditions is empty!"); 69*0d1c3f1fSAndrew Geissler return false; 70*0d1c3f1fSAndrew Geissler } 71*0d1c3f1fSAndrew Geissler 72*0d1c3f1fSAndrew Geissler // Now read the CurrentFirmwareCondition from all interfaces we found 73*0d1c3f1fSAndrew Geissler // Currently there are two implementations of this interface. One by IPMI 74*0d1c3f1fSAndrew Geissler // and one by PLDM. The IPMI interface does a realtime check with the host 75*0d1c3f1fSAndrew Geissler // when the interface is called. This means if the host is not running, 76*0d1c3f1fSAndrew Geissler // we will have to wait for the timeout (currently set to 3 seconds). The 77*0d1c3f1fSAndrew Geissler // PLDM interface reads a cached state. The PLDM service does not put itself 78*0d1c3f1fSAndrew Geissler // on D-Bus until it has checked with the host. Therefore it's most 79*0d1c3f1fSAndrew Geissler // efficient to call the PLDM interface first. Do that by going in reverse 80*0d1c3f1fSAndrew Geissler // of the interfaces returned to us (PLDM will be last if available) 81*0d1c3f1fSAndrew Geissler for (const auto& [path, services] : 82*0d1c3f1fSAndrew Geissler boost::adaptors::reverse(mapperResponse)) 83*0d1c3f1fSAndrew Geissler { 84*0d1c3f1fSAndrew Geissler for (const auto& serviceIter : services) 85*0d1c3f1fSAndrew Geissler { 86*0d1c3f1fSAndrew Geissler const std::string& service = serviceIter.first; 87*0d1c3f1fSAndrew Geissler 88*0d1c3f1fSAndrew Geissler try 89*0d1c3f1fSAndrew Geissler { 90*0d1c3f1fSAndrew Geissler auto method = bus.new_method_call(service.c_str(), path.c_str(), 91*0d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 92*0d1c3f1fSAndrew Geissler method.append(CONDITION_HOST_INTERFACE, 93*0d1c3f1fSAndrew Geissler CONDITION_HOST_PROPERTY); 94*0d1c3f1fSAndrew Geissler 95*0d1c3f1fSAndrew Geissler auto response = bus.call(method); 96*0d1c3f1fSAndrew Geissler 97*0d1c3f1fSAndrew Geissler std::variant<std::string> currentFwCond; 98*0d1c3f1fSAndrew Geissler response.read(currentFwCond); 99*0d1c3f1fSAndrew Geissler 100*0d1c3f1fSAndrew Geissler if (std::get<std::string>(currentFwCond) == 101*0d1c3f1fSAndrew Geissler "xyz.openbmc_project.Condition.HostFirmware." 102*0d1c3f1fSAndrew Geissler "FirmwareCondition." 103*0d1c3f1fSAndrew Geissler "Running") 104*0d1c3f1fSAndrew Geissler { 105*0d1c3f1fSAndrew Geissler return true; 106*0d1c3f1fSAndrew Geissler } 107*0d1c3f1fSAndrew Geissler } 108*0d1c3f1fSAndrew Geissler catch (const SdBusError& e) 109*0d1c3f1fSAndrew Geissler { 110*0d1c3f1fSAndrew Geissler log<level::ERR>("Error reading HostFirmware condition", 111*0d1c3f1fSAndrew Geissler entry("ERROR=%s", e.what()), 112*0d1c3f1fSAndrew Geissler entry("SERVICE=%s", service.c_str()), 113*0d1c3f1fSAndrew Geissler entry("PATH=%s", path.c_str())); 114*0d1c3f1fSAndrew Geissler throw; 115*0d1c3f1fSAndrew Geissler } 116*0d1c3f1fSAndrew Geissler } 117*0d1c3f1fSAndrew Geissler } 118*0d1c3f1fSAndrew Geissler return false; 119*0d1c3f1fSAndrew Geissler } 120*0d1c3f1fSAndrew Geissler 121*0d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on 122*0d1c3f1fSAndrew Geissler bool isChassiPowerOn(sdbusplus::bus::bus& bus) 123*0d1c3f1fSAndrew Geissler { 124*0d1c3f1fSAndrew Geissler try 125*0d1c3f1fSAndrew Geissler { 126*0d1c3f1fSAndrew Geissler auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH, 127*0d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 128*0d1c3f1fSAndrew Geissler method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP); 129*0d1c3f1fSAndrew Geissler 130*0d1c3f1fSAndrew Geissler auto response = bus.call(method); 131*0d1c3f1fSAndrew Geissler 132*0d1c3f1fSAndrew Geissler std::variant<std::string> currentPowerState; 133*0d1c3f1fSAndrew Geissler response.read(currentPowerState); 134*0d1c3f1fSAndrew Geissler 135*0d1c3f1fSAndrew Geissler if (std::get<std::string>(currentPowerState) == 136*0d1c3f1fSAndrew Geissler "xyz.openbmc_project.State.Chassis.PowerState.On") 137*0d1c3f1fSAndrew Geissler { 138*0d1c3f1fSAndrew Geissler return true; 139*0d1c3f1fSAndrew Geissler } 140*0d1c3f1fSAndrew Geissler } 141*0d1c3f1fSAndrew Geissler catch (const SdBusError& e) 142*0d1c3f1fSAndrew Geissler { 143*0d1c3f1fSAndrew Geissler log<level::ERR>("Error reading Chassis Power State", 144*0d1c3f1fSAndrew Geissler entry("ERROR=%s", e.what()), 145*0d1c3f1fSAndrew Geissler entry("SERVICE=%s", CHASSIS_STATE_SVC), 146*0d1c3f1fSAndrew Geissler entry("PATH=%s", CHASSIS_STATE_PATH)); 147*0d1c3f1fSAndrew Geissler 148*0d1c3f1fSAndrew Geissler throw; 149*0d1c3f1fSAndrew Geissler } 150*0d1c3f1fSAndrew Geissler return false; 151*0d1c3f1fSAndrew Geissler } 152*0d1c3f1fSAndrew Geissler 153*0d1c3f1fSAndrew Geissler bool isHostRunning() 154*0d1c3f1fSAndrew Geissler { 155*0d1c3f1fSAndrew Geissler log<level::INFO>("Check if host is running"); 156*0d1c3f1fSAndrew Geissler 157*0d1c3f1fSAndrew Geissler auto bus = sdbusplus::bus::new_default(); 158*0d1c3f1fSAndrew Geissler 159*0d1c3f1fSAndrew Geissler // No need to check if chassis power is not on 160*0d1c3f1fSAndrew Geissler if (!isChassiPowerOn(bus)) 161*0d1c3f1fSAndrew Geissler { 162*0d1c3f1fSAndrew Geissler log<level::INFO>("Chassis power not on, exit"); 163*0d1c3f1fSAndrew Geissler return false; 164*0d1c3f1fSAndrew Geissler } 165*0d1c3f1fSAndrew Geissler 166*0d1c3f1fSAndrew Geissler // This applications systemd service is setup to only run after all other 167*0d1c3f1fSAndrew Geissler // application that could possibly implement the needed interface have 168*0d1c3f1fSAndrew Geissler // been started. However, the use of mapper to find those interfaces means 169*0d1c3f1fSAndrew Geissler // we have a condition where the interface may be on D-Bus but not stored 170*0d1c3f1fSAndrew Geissler // within mapper yet. Keep it simple and just build one retry into the 171*0d1c3f1fSAndrew Geissler // check if it's found the host is not up. This service is only called if 172*0d1c3f1fSAndrew Geissler // chassis power is on when the BMC comes up, so this wont impact most 173*0d1c3f1fSAndrew Geissler // normal cases where the BMC is rebooted with chassis power off. In 174*0d1c3f1fSAndrew Geissler // cases where chassis power is on, the host is likely running so we want 175*0d1c3f1fSAndrew Geissler // to be sure we check all interfaces 176*0d1c3f1fSAndrew Geissler for (int i = 0; i < 2; i++) 177*0d1c3f1fSAndrew Geissler { 178*0d1c3f1fSAndrew Geissler // Give mapper a small window to introspect new objects on bus 179*0d1c3f1fSAndrew Geissler std::this_thread::sleep_for(std::chrono::milliseconds(500)); 180*0d1c3f1fSAndrew Geissler if (checkFirmwareConditionRunning(bus)) 181*0d1c3f1fSAndrew Geissler { 182*0d1c3f1fSAndrew Geissler log<level::INFO>("Host is running!"); 183*0d1c3f1fSAndrew Geissler // Create file for host instance and create in filesystem to 184*0d1c3f1fSAndrew Geissler // indicate to services that host is running 185*0d1c3f1fSAndrew Geissler auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0); 186*0d1c3f1fSAndrew Geissler size++; // null 187*0d1c3f1fSAndrew Geissler std::unique_ptr<char[]> buf(new char[size]); 188*0d1c3f1fSAndrew Geissler std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0); 189*0d1c3f1fSAndrew Geissler std::ofstream outfile(buf.get()); 190*0d1c3f1fSAndrew Geissler outfile.close(); 191*0d1c3f1fSAndrew Geissler return true; 192*0d1c3f1fSAndrew Geissler } 193*0d1c3f1fSAndrew Geissler } 194*0d1c3f1fSAndrew Geissler log<level::INFO>("Host is not running!"); 195*0d1c3f1fSAndrew Geissler return false; 196*0d1c3f1fSAndrew Geissler } 197