1 #include "config.h" 2 3 #include "host_state_manager.hpp" 4 #include "settings.hpp" 5 #include "utils.hpp" 6 #include "xyz/openbmc_project/Common/error.hpp" 7 #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp" 8 9 #include <fmt/format.h> 10 #include <fmt/printf.h> 11 #include <getopt.h> 12 #include <systemd/sd-bus.h> 13 14 #include <phosphor-logging/elog-errors.hpp> 15 #include <phosphor-logging/lg2.hpp> 16 #include <sdbusplus/exception.hpp> 17 #include <sdbusplus/server.hpp> 18 #include <xyz/openbmc_project/State/BMC/client.hpp> 19 #include <xyz/openbmc_project/State/Host/client.hpp> 20 21 #include <filesystem> 22 #include <iostream> 23 #include <map> 24 #include <string> 25 #include <thread> 26 27 namespace phosphor 28 { 29 namespace state 30 { 31 namespace manager 32 { 33 34 PHOSPHOR_LOG2_USING; 35 36 using namespace phosphor::logging; 37 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 38 using namespace sdbusplus::server::xyz::openbmc_project::control::power; 39 40 } // namespace manager 41 } // namespace state 42 } // namespace phosphor 43 44 int main(int argc, char** argv) 45 { 46 using namespace phosphor::logging; 47 48 size_t hostId = 0; 49 int arg; 50 int optIndex = 0; 51 52 static struct option longOpts[] = {{"host", required_argument, 0, 'h'}, 53 {0, 0, 0, 0}}; 54 55 while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1) 56 { 57 switch (arg) 58 { 59 case 'h': 60 hostId = std::stoul(optarg); 61 break; 62 default: 63 break; 64 } 65 } 66 67 using Host = sdbusplus::client::xyz::openbmc_project::state::Host<>; 68 std::string hostPath = std::string(Host::namespace_path::value) + "/" + 69 std::string(Host::namespace_path::host) + 70 std::to_string(hostId); 71 72 auto bus = sdbusplus::bus::new_default(); 73 74 using namespace settings; 75 HostObjects settings(bus, hostId); 76 77 using namespace phosphor::state::manager; 78 namespace server = sdbusplus::server::xyz::openbmc_project::state; 79 80 // This application is only run if chassis power is off 81 82 // If the BMC was rebooted due to a user initiated pinhole reset, do not 83 // implement any power restore policies 84 using BMC = sdbusplus::client::xyz::openbmc_project::state::BMC<>; 85 auto bmcPath = sdbusplus::message::object_path(BMC::namespace_path::value) / 86 BMC::namespace_path::bmc; 87 88 auto bmcRebootCause = 89 sdbusplus::message::convert_from_string<BMC::RebootCause>( 90 phosphor::state::manager::utils::getProperty( 91 bus, bmcPath.str.c_str(), BMC_BUSNAME, "LastRebootCause")); 92 93 if (bmcRebootCause == BMC::RebootCause::PinholeReset) 94 { 95 info( 96 "BMC was reset due to pinhole reset, no power restore policy will be run"); 97 return 0; 98 } 99 else if (bmcRebootCause == BMC::RebootCause::Watchdog) 100 { 101 info( 102 "BMC was reset due to cold reset, no power restore policy will be run"); 103 return 0; 104 } 105 106 /* The logic here is to first check the one-time PowerRestorePolicy setting. 107 * If this property is not the default then look at the persistent 108 * user setting in the non one-time object, otherwise honor the one-time 109 * setting. 110 */ 111 auto methodOneTime = bus.new_method_call( 112 settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(), 113 settings.powerRestorePolicyOneTime.c_str(), 114 "org.freedesktop.DBus.Properties", "Get"); 115 methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy"); 116 117 auto methodUserSetting = bus.new_method_call( 118 settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(), 119 settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties", 120 "Get"); 121 methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy"); 122 123 std::variant<std::string> result; 124 try 125 { 126 auto reply = bus.call(methodOneTime); 127 reply.read(result); 128 auto powerPolicy = std::get<std::string>(result); 129 130 if (RestorePolicy::Policy::None == 131 RestorePolicy::convertPolicyFromString(powerPolicy)) 132 { 133 // one_time is set to None so use the customer setting 134 info("One time not set, check user setting of power policy"); 135 136 auto reply = bus.call(methodUserSetting); 137 reply.read(result); 138 powerPolicy = std::get<std::string>(result); 139 } 140 else 141 { 142 // one_time setting was set so we're going to use it. Reset it 143 // to default for next time. 144 info("One time set, use it and reset to default"); 145 phosphor::state::manager::utils::setProperty( 146 bus, settings.powerRestorePolicyOneTime.c_str(), 147 powerRestoreIntf, "PowerRestorePolicy", 148 convertForMessage(RestorePolicy::Policy::None)); 149 } 150 151 auto methodUserSettingDelay = bus.new_method_call( 152 settings.service(settings.powerRestorePolicy, powerRestoreIntf) 153 .c_str(), 154 settings.powerRestorePolicy.c_str(), 155 "org.freedesktop.DBus.Properties", "Get"); 156 157 methodUserSettingDelay.append(powerRestoreIntf, "PowerRestoreDelay"); 158 159 std::variant<uint64_t> restoreDelay; 160 161 auto delayResult = bus.call(methodUserSettingDelay); 162 delayResult.read(restoreDelay); 163 auto powerRestoreDelayUsec = 164 std::chrono::microseconds(std::get<uint64_t>(restoreDelay)); 165 auto powerRestoreDelaySec = 166 std::chrono::duration_cast<std::chrono::seconds>( 167 powerRestoreDelayUsec); 168 169 info("Host power is off, processing power policy {POWER_POLICY}", 170 "POWER_POLICY", powerPolicy); 171 172 if (RestorePolicy::Policy::AlwaysOn == 173 RestorePolicy::convertPolicyFromString(powerPolicy)) 174 { 175 info( 176 "power_policy=ALWAYS_POWER_ON, powering host on ({DELAY}s delay)", 177 "DELAY", powerRestoreDelaySec.count()); 178 utils::waitBmcReady(bus, powerRestoreDelaySec); 179 phosphor::state::manager::utils::setProperty( 180 bus, hostPath, HOST_BUSNAME, "RestartCause", 181 convertForMessage( 182 server::Host::RestartCause::PowerPolicyAlwaysOn)); 183 phosphor::state::manager::utils::setProperty( 184 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 185 convertForMessage(server::Host::Transition::On)); 186 } 187 // Always execute power on if AlwaysOn is set, otherwise check config 188 // option (and AC loss status) on whether to execute other policy 189 // settings 190 #if ONLY_RUN_APR_ON_POWER_LOSS 191 else if (!phosphor::state::manager::utils::checkACLoss(hostId)) 192 { 193 info( 194 "Chassis power was not on prior to BMC reboot so do not run any further power policy"); 195 return 0; 196 } 197 #endif 198 else if (RestorePolicy::Policy::AlwaysOff == 199 RestorePolicy::convertPolicyFromString(powerPolicy)) 200 { 201 info( 202 "power_policy=ALWAYS_POWER_OFF, set requested state to off ({DELAY}s delay)", 203 "DELAY", powerRestoreDelaySec.count()); 204 utils::waitBmcReady(bus, powerRestoreDelaySec); 205 // Read last requested state and re-request it to execute it 206 auto hostReqState = phosphor::state::manager::utils::getProperty( 207 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition"); 208 if (hostReqState != 209 convertForMessage(server::Host::Transition::Off)) 210 { 211 phosphor::state::manager::utils::setProperty( 212 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 213 convertForMessage(server::Host::Transition::Off)); 214 } 215 } 216 else if (RestorePolicy::Policy::Restore == 217 RestorePolicy::convertPolicyFromString(powerPolicy)) 218 { 219 info("power_policy=RESTORE, restoring last state ({DELAY}s delay)", 220 "DELAY", powerRestoreDelaySec.count()); 221 utils::waitBmcReady(bus, powerRestoreDelaySec); 222 // Read last requested state and re-request it to execute it 223 auto hostReqState = phosphor::state::manager::utils::getProperty( 224 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition"); 225 226 // As long as the host transition is not 'Off' power on host state. 227 if (hostReqState != 228 convertForMessage(server::Host::Transition::Off)) 229 { 230 phosphor::state::manager::utils::setProperty( 231 bus, hostPath, HOST_BUSNAME, "RestartCause", 232 convertForMessage( 233 server::Host::RestartCause::PowerPolicyPreviousState)); 234 phosphor::state::manager::utils::setProperty( 235 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 236 convertForMessage(server::Host::Transition::On)); 237 } 238 } 239 } 240 catch (const sdbusplus::exception_t& e) 241 { 242 error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e); 243 elog<InternalFailure>(); 244 } 245 246 return 0; 247 } 248