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