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