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/server.hpp> 10 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 11 #include <xyz/openbmc_project/State/Boot/Progress/server.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 constexpr auto HOST_STATE_SVC = "xyz.openbmc_project.State.Host"; 27 constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; 28 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 29 constexpr auto BOOT_STATE_INTF = "xyz.openbmc_project.State.Boot.Progress"; 30 constexpr auto BOOT_PROGRESS_PROP = "BootProgress"; 31 32 constexpr auto LOGGING_SVC = "xyz.openbmc_project.Logging"; 33 constexpr auto LOGGING_PATH = "/xyz/openbmc_project/logging"; 34 constexpr auto LOGGING_CREATE_INTF = "xyz.openbmc_project.Logging.Create"; 35 36 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 37 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 38 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 39 constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target"; 40 41 bool wasHostBooting(sdbusplus::bus_t& bus) 42 { 43 try 44 { 45 using ProgressStages = sdbusplus::xyz::openbmc_project::State::Boot:: 46 server::Progress::ProgressStages; 47 48 auto method = bus.new_method_call(HOST_STATE_SVC, HOST_STATE_PATH, 49 PROPERTY_INTERFACE, "Get"); 50 method.append(BOOT_STATE_INTF, 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(LOGGING_SVC, LOGGING_PATH, 90 LOGGING_CREATE_INTF, "Create"); 91 method.append(errorMessage, 92 sdbusplus::server::xyz::openbmc_project::logging::Entry:: 93 Level::Error, 94 additionalData); 95 auto resp = bus.call(method); 96 } 97 catch (const sdbusplus::exception_t& e) 98 { 99 error( 100 "sdbusplus D-Bus call exception, error {ERROR}, objpath {OBJPATH}, " 101 "interface {INTERFACE}", 102 "ERROR", e, "OBJPATH", LOGGING_PATH, "INTERFACE", 103 LOGGING_CREATE_INTF); 104 105 throw std::runtime_error( 106 "Error in invoking D-Bus logging create interface"); 107 } 108 catch (const std::exception& e) 109 { 110 error("D-bus call exception: {ERROR}", "ERROR", e); 111 throw e; 112 } 113 } 114 115 // Once CHASSIS_ON_FILE is removed, the obmc-chassis-poweron@.target has 116 // completed and the phosphor-chassis-state-manager code has processed it. 117 bool isChassisTargetComplete() 118 { 119 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0); 120 size++; // null 121 std::unique_ptr<char[]> buf(new char[size]); 122 std::snprintf(buf.get(), size, CHASSIS_ON_FILE, 0); 123 124 std::ifstream f(buf.get()); 125 return !f.good(); 126 } 127 128 void moveToHostQuiesce(sdbusplus::bus_t& bus) 129 { 130 try 131 { 132 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 133 SYSTEMD_INTERFACE, "StartUnit"); 134 135 method.append(HOST_STATE_QUIESCE_TGT); 136 method.append("replace"); 137 138 bus.call_noreply(method); 139 } 140 catch (const sdbusplus::exception_t& e) 141 { 142 error("sdbusplus call exception starting quiesce target: {ERROR}", 143 "ERROR", e); 144 145 throw std::runtime_error( 146 "Error in invoking D-Bus systemd StartUnit method"); 147 } 148 } 149 150 } // namespace manager 151 } // namespace state 152 } // namespace phosphor 153 154 int main() 155 { 156 using namespace phosphor::state::manager; 157 PHOSPHOR_LOG2_USING; 158 159 auto bus = sdbusplus::bus::new_default(); 160 161 // Chassis power is on if this service starts but need to wait for the 162 // obmc-chassis-poweron@.target to complete before potentially initiating 163 // another systemd target transition (i.e. Quiesce->Reboot) 164 while (!isChassisTargetComplete()) 165 { 166 debug("Waiting for chassis on target to complete"); 167 std::this_thread::sleep_for(std::chrono::seconds(1)); 168 169 // There is no timeout here, wait until it happens or until system 170 // is powered off and this service is stopped 171 } 172 173 info("Chassis power on has completed, checking if host is " 174 "still running after the BMC reboot"); 175 176 // Check the last BootProgeress to see if the host was booting before 177 // the BMC reboot occurred 178 if (!wasHostBooting(bus)) 179 { 180 return 0; 181 } 182 183 // Host was booting before the BMC reboot so log an error and go to host 184 // quiesce target 185 createErrorLog(bus); 186 moveToHostQuiesce(bus); 187 188 return 0; 189 } 190