1 #include "config.h" 2 3 #include <unistd.h> 4 5 #include <phosphor-logging/elog.hpp> 6 #include <phosphor-logging/lg2.hpp> 7 #include <sdbusplus/bus.hpp> 8 #include <sdbusplus/exception.hpp> 9 #include <xyz/openbmc_project/Logging/Create/client.hpp> 10 #include <xyz/openbmc_project/Logging/Entry/client.hpp> 11 #include <xyz/openbmc_project/State/Boot/Progress/client.hpp> 12 13 #include <cstdlib> 14 #include <format> 15 #include <fstream> 16 #include <string> 17 18 namespace phosphor 19 { 20 namespace state 21 { 22 namespace manager 23 { 24 25 PHOSPHOR_LOG2_USING; 26 27 using BootProgress = 28 sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>; 29 using LoggingCreate = 30 sdbusplus::client::xyz::openbmc_project::logging::Create<>; 31 using LoggingEntry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>; 32 33 constexpr auto HOST_STATE_SVC = "xyz.openbmc_project.State.Host"; 34 constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; 35 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 36 constexpr auto BOOT_PROGRESS_PROP = "BootProgress"; 37 38 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 39 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 40 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 41 constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target"; 42 43 bool wasHostBooting(sdbusplus::bus_t& bus) 44 { 45 try 46 { 47 using ProgressStages = BootProgress::ProgressStages; 48 49 auto method = bus.new_method_call(HOST_STATE_SVC, HOST_STATE_PATH, 50 PROPERTY_INTERFACE, "Get"); 51 method.append(BootProgress::interface, BOOT_PROGRESS_PROP); 52 53 auto response = bus.call(method); 54 55 std::variant<ProgressStages> bootProgressV; 56 response.read(bootProgressV); 57 auto bootProgress = std::get<ProgressStages>(bootProgressV); 58 59 if (bootProgress == ProgressStages::Unspecified) 60 { 61 info("Host was not booting before BMC reboot"); 62 return false; 63 } 64 65 info("Host was booting before BMC reboot: {BOOTPROGRESS}", 66 "BOOTPROGRESS", bootProgress); 67 } 68 catch (const sdbusplus::exception_t& e) 69 { 70 error("Error reading BootProgress, error {ERROR}, service {SERVICE}, " 71 "path {PATH}", 72 "ERROR", e, "SERVICE", HOST_STATE_SVC, "PATH", HOST_STATE_PATH); 73 74 throw; 75 } 76 77 return true; 78 } 79 80 void createErrorLog(sdbusplus::bus_t& bus) 81 { 82 try 83 { 84 // Create interface requires something for additionalData 85 std::map<std::string, std::string> additionalData; 86 additionalData.emplace("_PID", std::to_string(getpid())); 87 88 static constexpr auto errorMessage = 89 "xyz.openbmc_project.State.Error.HostNotRunning"; 90 auto method = bus.new_method_call(LoggingCreate::default_service, 91 LoggingCreate::instance_path, 92 LoggingCreate::interface, "Create"); 93 method.append(errorMessage, LoggingEntry::Level::Error, additionalData); 94 auto resp = bus.call(method); 95 } 96 catch (const sdbusplus::exception_t& e) 97 { 98 error( 99 "sdbusplus D-Bus call exception, error {ERROR}, objpath {OBJPATH}, " 100 "interface {INTERFACE}", 101 "ERROR", e, "OBJPATH", LoggingCreate::instance_path, "INTERFACE", 102 LoggingCreate::interface); 103 104 throw std::runtime_error( 105 "Error in invoking D-Bus logging create interface"); 106 } 107 catch (const std::exception& e) 108 { 109 error("D-bus call exception: {ERROR}", "ERROR", e); 110 throw e; 111 } 112 } 113 114 // Once CHASSIS_ON_FILE is removed, the obmc-chassis-poweron@.target has 115 // completed and the phosphor-chassis-state-manager code has processed it. 116 bool isChassisTargetComplete() 117 { 118 auto chassisFile = std::format(CHASSIS_ON_FILE, 0); 119 120 std::ifstream f(chassisFile); 121 return !f.good(); 122 } 123 124 void moveToHostQuiesce(sdbusplus::bus_t& bus) 125 { 126 try 127 { 128 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 129 SYSTEMD_INTERFACE, "StartUnit"); 130 131 method.append(HOST_STATE_QUIESCE_TGT); 132 method.append("replace"); 133 134 bus.call_noreply(method); 135 } 136 catch (const sdbusplus::exception_t& e) 137 { 138 error("sdbusplus call exception starting quiesce target: {ERROR}", 139 "ERROR", e); 140 141 throw std::runtime_error( 142 "Error in invoking D-Bus systemd StartUnit method"); 143 } 144 } 145 146 } // namespace manager 147 } // namespace state 148 } // namespace phosphor 149 150 int main() 151 { 152 using namespace phosphor::state::manager; 153 PHOSPHOR_LOG2_USING; 154 155 auto bus = sdbusplus::bus::new_default(); 156 157 // Chassis power is on if this service starts but need to wait for the 158 // obmc-chassis-poweron@.target to complete before potentially initiating 159 // another systemd target transition (i.e. Quiesce->Reboot) 160 while (!isChassisTargetComplete()) 161 { 162 debug("Waiting for chassis on target to complete"); 163 std::this_thread::sleep_for(std::chrono::seconds(1)); 164 165 // There is no timeout here, wait until it happens or until system 166 // is powered off and this service is stopped 167 } 168 169 info("Chassis power on has completed, checking if host is " 170 "still running after the BMC reboot"); 171 172 // Check the last BootProgeress to see if the host was booting before 173 // the BMC reboot occurred 174 if (!wasHostBooting(bus)) 175 { 176 return 0; 177 } 178 179 // Host was booting before the BMC reboot so log an error and go to host 180 // quiesce target 181 createErrorLog(bus); 182 moveToHostQuiesce(bus); 183 184 return 0; 185 } 186