#include #include #include #include #include "bmc_state_manager.hpp" namespace phosphor { namespace state { namespace manager { // When you see server:: you know we're referencing our base class namespace server = sdbusplus::xyz::openbmc_project::State::server; using namespace phosphor::logging; using sdbusplus::exception::SdBusError; constexpr auto obmcStandbyTarget = "obmc-standby.target"; constexpr auto signalDone = "done"; constexpr auto activeState = "active"; /* Map a transition to it's systemd target */ const std::map SYSTEMD_TABLE = { {server::BMC::Transition::Reboot, "reboot.target"}}; constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; constexpr auto SYSTEMD_PRP_INTERFACE = "org.freedesktop.DBus.Properties"; void BMC::discoverInitialState() { sdbusplus::message::variant currentState; sdbusplus::message::object_path unitTargetPath; auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "GetUnit"); method.append(obmcStandbyTarget); try { auto result = this->bus.call(method); result.read(unitTargetPath); } catch (const SdBusError& e) { log("Error in GetUnit call", entry("ERROR=%s", e.what())); return; } method = this->bus.new_method_call( SYSTEMD_SERVICE, static_cast(unitTargetPath).c_str(), SYSTEMD_PRP_INTERFACE, "Get"); method.append("org.freedesktop.systemd1.Unit", "ActiveState"); try { auto result = this->bus.call(method); // Is obmc-standby.target active or inactive? result.read(currentState); } catch (const SdBusError& e) { log("Error in ActiveState Get", entry("ERROR=%s", e.what())); return; } auto currentStateStr = sdbusplus::message::variant_ns::get(currentState); if (currentStateStr == activeState) { log("Setting the BMCState field", entry("CURRENT_BMC_STATE=%s", "BMC_READY")); this->currentBMCState(BMCState::Ready); // Unsubscribe so we stop processing all other signals method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Unsubscribe"); try { this->bus.call(method); this->stateSignal.release(); } catch (const SdBusError& e) { log("Error in Unsubscribe", entry("ERROR=%s", e.what())); } } else { log("Setting the BMCState field", entry("CURRENT_BMC_STATE=%s", "BMC_NOTREADY")); this->currentBMCState(BMCState::NotReady); } return; } void BMC::subscribeToSystemdSignals() { auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe"); try { this->bus.call(method); } catch (const SdBusError& e) { log("Error in Subscribe", entry("ERROR=%s", e.what())); } return; } void BMC::executeTransition(const Transition tranReq) { // Check to make sure it can be found auto iter = SYSTEMD_TABLE.find(tranReq); if (iter == SYSTEMD_TABLE.end()) return; const auto& sysdUnit = iter->second; auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit"); // The only valid transition is reboot and that // needs to be irreversible once started method.append(sysdUnit, "replace-irreversibly"); try { this->bus.call(method); } catch (const SdBusError& e) { log("Error in StartUnit - replace-irreversibly", entry("ERROR=%s", e.what())); } return; } int BMC::bmcStateChange(sdbusplus::message::message& msg) { uint32_t newStateID{}; sdbusplus::message::object_path newStateObjPath; std::string newStateUnit{}; std::string newStateResult{}; // Read the msg and populate each variable msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); // Caught the signal that indicates the BMC is now BMC_READY if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone)) { log("BMC_READY"); this->currentBMCState(BMCState::Ready); // Unsubscribe so we stop processing all other signals auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Unsubscribe"); try { this->bus.call(method); this->stateSignal.release(); } catch (const SdBusError& e) { log("Error in Unsubscribe", entry("ERROR=%s", e.what())); } } return 0; } BMC::Transition BMC::requestedBMCTransition(Transition value) { log("Setting the RequestedBMCTransition field", entry("REQUESTED_BMC_TRANSITION=0x%s", convertForMessage(value).c_str())); executeTransition(value); return server::BMC::requestedBMCTransition(value); } BMC::BMCState BMC::currentBMCState(BMCState value) { log( "Setting the BMCState field", entry("CURRENT_BMC_STATE=0x%s", convertForMessage(value).c_str())); return server::BMC::currentBMCState(value); } uint64_t BMC::lastRebootTime() const { using namespace std::chrono; struct sysinfo info; auto rc = sysinfo(&info); assert(rc == 0); // Since uptime is in seconds, also get the current time in seconds. auto now = time_point_cast(system_clock::now()); auto rebootTime = now - seconds(info.uptime); return duration_cast(rebootTime.time_since_epoch()).count(); } } // namespace manager } // namespace state } // namespace phosphor