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 std::string chassisLostPowerFileFmt = 129 fmt::sprintf(CHASSIS_LOST_POWER_FILE, hostId); 130 fs::path chassisPowerLossFile{chassisLostPowerFileFmt}; 131 if (!fs::exists(chassisPowerLossFile)) 132 { 133 info( 134 "Chassis power was not on prior to BMC reboot so do not run any power policy"); 135 return 0; 136 } 137 #endif 138 auto reply = bus.call(methodUserSetting); 139 reply.read(result); 140 powerPolicy = std::get<std::string>(result); 141 } 142 else 143 { 144 // one_time setting was set so we're going to use it. Reset it 145 // to default for next time. 146 info("One time set, use it and reset to default"); 147 phosphor::state::manager::utils::setProperty( 148 bus, settings.powerRestorePolicyOneTime.c_str(), 149 powerRestoreIntf, "PowerRestorePolicy", 150 convertForMessage(RestorePolicy::Policy::None)); 151 } 152 153 auto methodUserSettingDelay = bus.new_method_call( 154 settings.service(settings.powerRestorePolicy, powerRestoreIntf) 155 .c_str(), 156 settings.powerRestorePolicy.c_str(), 157 "org.freedesktop.DBus.Properties", "Get"); 158 159 methodUserSettingDelay.append(powerRestoreIntf, "PowerRestoreDelay"); 160 161 std::variant<uint64_t> restoreDelay; 162 163 auto delayResult = bus.call(methodUserSettingDelay); 164 delayResult.read(restoreDelay); 165 auto powerRestoreDelayUsec = 166 std::chrono::microseconds(std::get<uint64_t>(restoreDelay)); 167 auto powerRestoreDelaySec = 168 std::chrono::duration_cast<std::chrono::seconds>( 169 powerRestoreDelayUsec); 170 171 info("Host power is off, processing power policy {POWER_POLICY}", 172 "POWER_POLICY", powerPolicy); 173 174 if (RestorePolicy::Policy::AlwaysOn == 175 RestorePolicy::convertPolicyFromString(powerPolicy)) 176 { 177 info( 178 "power_policy=ALWAYS_POWER_ON, powering host on ({DELAY}s delay)", 179 "DELAY", powerRestoreDelaySec.count()); 180 std::this_thread::sleep_for(powerRestoreDelayUsec); 181 phosphor::state::manager::utils::setProperty( 182 bus, hostPath, HOST_BUSNAME, "RestartCause", 183 convertForMessage( 184 server::Host::RestartCause::PowerPolicyAlwaysOn)); 185 phosphor::state::manager::utils::setProperty( 186 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 187 convertForMessage(server::Host::Transition::On)); 188 } 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