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 = 61 std::string("/xyz/openbmc_project/state/host") + 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 #ifdef ONLY_RUN_APR_ON_POWER_LOSS 128 if (!phosphor::state::manager::utils::checkACLoss(hostId)) 129 { 130 info( 131 "Chassis power was not on prior to BMC reboot so do not run any power policy"); 132 return 0; 133 } 134 #endif 135 auto reply = bus.call(methodUserSetting); 136 reply.read(result); 137 powerPolicy = std::get<std::string>(result); 138 } 139 else 140 { 141 // one_time setting was set so we're going to use it. Reset it 142 // to default for next time. 143 info("One time set, use it and reset to default"); 144 phosphor::state::manager::utils::setProperty( 145 bus, settings.powerRestorePolicyOneTime.c_str(), 146 powerRestoreIntf, "PowerRestorePolicy", 147 convertForMessage(RestorePolicy::Policy::None)); 148 } 149 150 auto methodUserSettingDelay = bus.new_method_call( 151 settings.service(settings.powerRestorePolicy, powerRestoreIntf) 152 .c_str(), 153 settings.powerRestorePolicy.c_str(), 154 "org.freedesktop.DBus.Properties", "Get"); 155 156 methodUserSettingDelay.append(powerRestoreIntf, "PowerRestoreDelay"); 157 158 std::variant<uint64_t> restoreDelay; 159 160 auto delayResult = bus.call(methodUserSettingDelay); 161 delayResult.read(restoreDelay); 162 auto powerRestoreDelayUsec = 163 std::chrono::microseconds(std::get<uint64_t>(restoreDelay)); 164 auto powerRestoreDelaySec = 165 std::chrono::duration_cast<std::chrono::seconds>( 166 powerRestoreDelayUsec); 167 168 info("Host power is off, processing power policy {POWER_POLICY}", 169 "POWER_POLICY", powerPolicy); 170 171 if (RestorePolicy::Policy::AlwaysOn == 172 RestorePolicy::convertPolicyFromString(powerPolicy)) 173 { 174 info( 175 "power_policy=ALWAYS_POWER_ON, powering host on ({DELAY}s delay)", 176 "DELAY", powerRestoreDelaySec.count()); 177 std::this_thread::sleep_for(powerRestoreDelayUsec); 178 phosphor::state::manager::utils::setProperty( 179 bus, hostPath, HOST_BUSNAME, "RestartCause", 180 convertForMessage( 181 server::Host::RestartCause::PowerPolicyAlwaysOn)); 182 phosphor::state::manager::utils::setProperty( 183 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 184 convertForMessage(server::Host::Transition::On)); 185 } 186 else if (RestorePolicy::Policy::AlwaysOff == 187 RestorePolicy::convertPolicyFromString(powerPolicy)) 188 { 189 info( 190 "power_policy=ALWAYS_POWER_OFF, set requested state to off ({DELAY}s delay)", 191 "DELAY", powerRestoreDelaySec.count()); 192 std::this_thread::sleep_for(powerRestoreDelayUsec); 193 // Read last requested state and re-request it to execute it 194 auto hostReqState = phosphor::state::manager::utils::getProperty( 195 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition"); 196 if (hostReqState != 197 convertForMessage(server::Host::Transition::Off)) 198 { 199 phosphor::state::manager::utils::setProperty( 200 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 201 convertForMessage(server::Host::Transition::Off)); 202 } 203 } 204 else if (RestorePolicy::Policy::Restore == 205 RestorePolicy::convertPolicyFromString(powerPolicy)) 206 { 207 info("power_policy=RESTORE, restoring last state ({DELAY}s delay)", 208 "DELAY", powerRestoreDelaySec.count()); 209 std::this_thread::sleep_for(powerRestoreDelayUsec); 210 // Read last requested state and re-request it to execute it 211 auto hostReqState = phosphor::state::manager::utils::getProperty( 212 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition"); 213 214 // As long as the host transition is not 'Off' power on host state. 215 if (hostReqState != 216 convertForMessage(server::Host::Transition::Off)) 217 { 218 phosphor::state::manager::utils::setProperty( 219 bus, hostPath, HOST_BUSNAME, "RestartCause", 220 convertForMessage( 221 server::Host::RestartCause::PowerPolicyPreviousState)); 222 phosphor::state::manager::utils::setProperty( 223 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 224 convertForMessage(server::Host::Transition::On)); 225 } 226 } 227 } 228 catch (const sdbusplus::exception_t& e) 229 { 230 error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e); 231 elog<InternalFailure>(); 232 } 233 234 return 0; 235 } 236