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 12 #include <cstdlib> 13 #include <fstream> 14 #include <string> 15 16 namespace phosphor 17 { 18 namespace state 19 { 20 namespace manager 21 { 22 23 PHOSPHOR_LOG2_USING; 24 25 constexpr auto HOST_STATE_SVC = "xyz.openbmc_project.State.Host"; 26 constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; 27 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 28 constexpr auto BOOT_STATE_INTF = "xyz.openbmc_project.State.Boot.Progress"; 29 constexpr auto BOOT_PROGRESS_PROP = "BootProgress"; 30 31 constexpr auto LOGGING_SVC = "xyz.openbmc_project.Logging"; 32 constexpr auto LOGGING_PATH = "/xyz/openbmc_project/logging"; 33 constexpr auto LOGGING_CREATE_INTF = "xyz.openbmc_project.Logging.Create"; 34 35 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 36 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 37 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 38 constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target"; 39 40 bool wasHostBooting(sdbusplus::bus::bus& bus) 41 { 42 try 43 { 44 auto method = bus.new_method_call(HOST_STATE_SVC, HOST_STATE_PATH, 45 PROPERTY_INTERFACE, "Get"); 46 method.append(BOOT_STATE_INTF, BOOT_PROGRESS_PROP); 47 48 auto response = bus.call(method); 49 50 std::variant<std::string> bootProgress; 51 response.read(bootProgress); 52 53 if (std::get<std::string>(bootProgress) == 54 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 55 "Unspecified") 56 { 57 info("Host was not booting before BMC reboot"); 58 return false; 59 } 60 61 info("Host was booting before BMC reboot: {BOOTPROGRESS}", 62 "BOOTPROGRESS", std::get<std::string>(bootProgress)); 63 } 64 catch (const sdbusplus::exception::exception& e) 65 { 66 error("Error reading BootProgress, error {ERROR}, service {SERVICE}, " 67 "path {PATH}", 68 "ERROR", e, "SERVICE", HOST_STATE_SVC, "PATH", HOST_STATE_PATH); 69 70 throw; 71 } 72 73 return true; 74 } 75 76 void createErrorLog(sdbusplus::bus::bus& bus) 77 { 78 try 79 { 80 // Create interface requires something for additionalData 81 std::map<std::string, std::string> additionalData; 82 additionalData.emplace("_PID", std::to_string(getpid())); 83 84 static constexpr auto errorMessage = 85 "xyz.openbmc_project.State.Error.HostNotRunning"; 86 auto method = bus.new_method_call(LOGGING_SVC, LOGGING_PATH, 87 LOGGING_CREATE_INTF, "Create"); 88 auto level = 89 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 90 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 91 Error); 92 method.append(errorMessage, level, additionalData); 93 auto resp = bus.call(method); 94 } 95 catch (const sdbusplus::exception::exception& e) 96 { 97 error( 98 "sdbusplus D-Bus call exception, error {ERROR}, objpath {OBJPATH}, " 99 "interface {INTERFACE}", 100 "ERROR", e, "OBJPATH", LOGGING_PATH, "INTERFACE", 101 LOGGING_CREATE_INTF); 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::bus& 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::exception& 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