1 #include "bmc_state_manager.hpp" 2 3 #include "xyz/openbmc_project/Common/error.hpp" 4 5 #include <sys/sysinfo.h> 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/log.hpp> 9 #include <sdbusplus/exception.hpp> 10 11 #include <cassert> 12 13 namespace phosphor 14 { 15 namespace state 16 { 17 namespace manager 18 { 19 20 // When you see server:: you know we're referencing our base class 21 namespace server = sdbusplus::xyz::openbmc_project::State::server; 22 23 using namespace phosphor::logging; 24 using sdbusplus::exception::SdBusError; 25 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 26 27 constexpr auto obmcStandbyTarget = "multi-user.target"; 28 constexpr auto signalDone = "done"; 29 constexpr auto activeState = "active"; 30 31 /* Map a transition to it's systemd target */ 32 const std::map<server::BMC::Transition, const char*> SYSTEMD_TABLE = { 33 {server::BMC::Transition::Reboot, "reboot.target"}}; 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 SYSTEMD_PRP_INTERFACE = "org.freedesktop.DBus.Properties"; 39 40 void BMC::discoverInitialState() 41 { 42 std::variant<std::string> currentState; 43 sdbusplus::message::object_path unitTargetPath; 44 45 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 46 SYSTEMD_INTERFACE, "GetUnit"); 47 48 method.append(obmcStandbyTarget); 49 50 try 51 { 52 auto result = this->bus.call(method); 53 result.read(unitTargetPath); 54 } 55 catch (const SdBusError& e) 56 { 57 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what())); 58 return; 59 } 60 61 method = this->bus.new_method_call( 62 SYSTEMD_SERVICE, 63 static_cast<const std::string&>(unitTargetPath).c_str(), 64 SYSTEMD_PRP_INTERFACE, "Get"); 65 66 method.append("org.freedesktop.systemd1.Unit", "ActiveState"); 67 68 try 69 { 70 auto result = this->bus.call(method); 71 72 // Is obmc-standby.target active or inactive? 73 result.read(currentState); 74 } 75 catch (const SdBusError& e) 76 { 77 log<level::INFO>("Error in ActiveState Get", 78 entry("ERROR=%s", e.what())); 79 return; 80 } 81 82 auto currentStateStr = std::get<std::string>(currentState); 83 if (currentStateStr == activeState) 84 { 85 log<level::INFO>("Setting the BMCState field", 86 entry("CURRENT_BMC_STATE=%s", "BMC_READY")); 87 this->currentBMCState(BMCState::Ready); 88 89 // Unsubscribe so we stop processing all other signals 90 method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 91 SYSTEMD_INTERFACE, "Unsubscribe"); 92 try 93 { 94 this->bus.call(method); 95 this->stateSignal.release(); 96 } 97 catch (const SdBusError& e) 98 { 99 log<level::INFO>("Error in Unsubscribe", 100 entry("ERROR=%s", e.what())); 101 } 102 } 103 else 104 { 105 log<level::INFO>("Setting the BMCState field", 106 entry("CURRENT_BMC_STATE=%s", "BMC_NOTREADY")); 107 this->currentBMCState(BMCState::NotReady); 108 } 109 110 return; 111 } 112 113 void BMC::subscribeToSystemdSignals() 114 { 115 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 116 SYSTEMD_INTERFACE, "Subscribe"); 117 118 try 119 { 120 this->bus.call(method); 121 } 122 catch (const SdBusError& e) 123 { 124 log<level::ERR>("Failed to subscribe to systemd signals", 125 entry("ERR=%s", e.what())); 126 elog<InternalFailure>(); 127 } 128 129 return; 130 } 131 132 void BMC::executeTransition(const Transition tranReq) 133 { 134 // HardReboot does not shutdown any services and immediately transitions 135 // into the reboot process 136 if (server::BMC::Transition::HardReboot == tranReq) 137 { 138 auto method = this->bus.new_method_call( 139 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot"); 140 try 141 { 142 this->bus.call(method); 143 } 144 catch (const SdBusError& e) 145 { 146 log<level::INFO>("Error in HardReboot", 147 entry("ERROR=%s", e.what())); 148 } 149 } 150 else 151 { 152 // Check to make sure it can be found 153 auto iter = SYSTEMD_TABLE.find(tranReq); 154 if (iter == SYSTEMD_TABLE.end()) 155 return; 156 157 const auto& sysdUnit = iter->second; 158 159 auto method = this->bus.new_method_call( 160 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit"); 161 // The only valid transition is reboot and that 162 // needs to be irreversible once started 163 164 method.append(sysdUnit, "replace-irreversibly"); 165 166 try 167 { 168 this->bus.call(method); 169 } 170 catch (const SdBusError& e) 171 { 172 log<level::INFO>("Error in StartUnit - replace-irreversibly", 173 entry("ERROR=%s", e.what())); 174 } 175 } 176 return; 177 } 178 179 int BMC::bmcStateChange(sdbusplus::message::message& msg) 180 { 181 uint32_t newStateID{}; 182 sdbusplus::message::object_path newStateObjPath; 183 std::string newStateUnit{}; 184 std::string newStateResult{}; 185 186 // Read the msg and populate each variable 187 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 188 189 // Caught the signal that indicates the BMC is now BMC_READY 190 if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone)) 191 { 192 log<level::INFO>("BMC_READY"); 193 this->currentBMCState(BMCState::Ready); 194 195 // Unsubscribe so we stop processing all other signals 196 auto method = 197 this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 198 SYSTEMD_INTERFACE, "Unsubscribe"); 199 200 try 201 { 202 this->bus.call(method); 203 this->stateSignal.release(); 204 } 205 catch (const SdBusError& e) 206 { 207 log<level::INFO>("Error in Unsubscribe", 208 entry("ERROR=%s", e.what())); 209 } 210 } 211 212 return 0; 213 } 214 215 BMC::Transition BMC::requestedBMCTransition(Transition value) 216 { 217 log<level::INFO>("Setting the RequestedBMCTransition field", 218 entry("REQUESTED_BMC_TRANSITION=0x%s", 219 convertForMessage(value).c_str())); 220 221 executeTransition(value); 222 return server::BMC::requestedBMCTransition(value); 223 } 224 225 BMC::BMCState BMC::currentBMCState(BMCState value) 226 { 227 log<level::INFO>( 228 "Setting the BMCState field", 229 entry("CURRENT_BMC_STATE=0x%s", convertForMessage(value).c_str())); 230 231 return server::BMC::currentBMCState(value); 232 } 233 234 uint64_t BMC::lastRebootTime() const 235 { 236 using namespace std::chrono; 237 struct sysinfo info; 238 239 auto rc = sysinfo(&info); 240 assert(rc == 0); 241 242 // Since uptime is in seconds, also get the current time in seconds. 243 auto now = time_point_cast<seconds>(system_clock::now()); 244 auto rebootTime = now - seconds(info.uptime); 245 246 return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count(); 247 } 248 249 } // namespace manager 250 } // namespace state 251 } // namespace phosphor 252