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 sdbusplus::message::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 = 79 sdbusplus::message::variant_ns::get<std::string>(currentState); 80 if (currentStateStr == activeState) 81 { 82 log<level::INFO>("Setting the BMCState field", 83 entry("CURRENT_BMC_STATE=%s", "BMC_READY")); 84 this->currentBMCState(BMCState::Ready); 85 86 // Unsubscribe so we stop processing all other signals 87 method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 88 SYSTEMD_INTERFACE, "Unsubscribe"); 89 try 90 { 91 this->bus.call(method); 92 this->stateSignal.release(); 93 } 94 catch (const SdBusError& e) 95 { 96 log<level::INFO>("Error in Unsubscribe", 97 entry("ERROR=%s", e.what())); 98 } 99 } 100 else 101 { 102 log<level::INFO>("Setting the BMCState field", 103 entry("CURRENT_BMC_STATE=%s", "BMC_NOTREADY")); 104 this->currentBMCState(BMCState::NotReady); 105 } 106 107 return; 108 } 109 110 void BMC::subscribeToSystemdSignals() 111 { 112 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 113 SYSTEMD_INTERFACE, "Subscribe"); 114 115 try 116 { 117 this->bus.call(method); 118 } 119 catch (const SdBusError& e) 120 { 121 log<level::ERR>("Failed to subscribe to systemd signals", 122 entry("ERR=%s", e.what())); 123 elog<InternalFailure>(); 124 } 125 126 return; 127 } 128 129 void BMC::executeTransition(const Transition tranReq) 130 { 131 // Check to make sure it can be found 132 auto iter = SYSTEMD_TABLE.find(tranReq); 133 if (iter == SYSTEMD_TABLE.end()) 134 return; 135 136 const auto& sysdUnit = iter->second; 137 138 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 139 SYSTEMD_INTERFACE, "StartUnit"); 140 // The only valid transition is reboot and that 141 // needs to be irreversible once started 142 method.append(sysdUnit, "replace-irreversibly"); 143 144 try 145 { 146 this->bus.call(method); 147 } 148 catch (const SdBusError& e) 149 { 150 log<level::INFO>("Error in StartUnit - replace-irreversibly", 151 entry("ERROR=%s", e.what())); 152 } 153 154 return; 155 } 156 157 int BMC::bmcStateChange(sdbusplus::message::message& msg) 158 { 159 uint32_t newStateID{}; 160 sdbusplus::message::object_path newStateObjPath; 161 std::string newStateUnit{}; 162 std::string newStateResult{}; 163 164 // Read the msg and populate each variable 165 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 166 167 // Caught the signal that indicates the BMC is now BMC_READY 168 if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone)) 169 { 170 log<level::INFO>("BMC_READY"); 171 this->currentBMCState(BMCState::Ready); 172 173 // Unsubscribe so we stop processing all other signals 174 auto method = 175 this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 176 SYSTEMD_INTERFACE, "Unsubscribe"); 177 178 try 179 { 180 this->bus.call(method); 181 this->stateSignal.release(); 182 } 183 catch (const SdBusError& e) 184 { 185 log<level::INFO>("Error in Unsubscribe", 186 entry("ERROR=%s", e.what())); 187 } 188 } 189 190 return 0; 191 } 192 193 BMC::Transition BMC::requestedBMCTransition(Transition value) 194 { 195 log<level::INFO>("Setting the RequestedBMCTransition field", 196 entry("REQUESTED_BMC_TRANSITION=0x%s", 197 convertForMessage(value).c_str())); 198 199 executeTransition(value); 200 return server::BMC::requestedBMCTransition(value); 201 } 202 203 BMC::BMCState BMC::currentBMCState(BMCState value) 204 { 205 log<level::INFO>( 206 "Setting the BMCState field", 207 entry("CURRENT_BMC_STATE=0x%s", convertForMessage(value).c_str())); 208 209 return server::BMC::currentBMCState(value); 210 } 211 212 uint64_t BMC::lastRebootTime() const 213 { 214 using namespace std::chrono; 215 struct sysinfo info; 216 217 auto rc = sysinfo(&info); 218 assert(rc == 0); 219 220 // Since uptime is in seconds, also get the current time in seconds. 221 auto now = time_point_cast<seconds>(system_clock::now()); 222 auto rebootTime = now - seconds(info.uptime); 223 224 return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count(); 225 } 226 227 } // namespace manager 228 } // namespace state 229 } // namespace phosphor 230