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