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