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