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