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