1 #include "config.h" 2 3 #include "host_state_manager.hpp" 4 #include "settings.hpp" 5 #include "xyz/openbmc_project/Common/error.hpp" 6 #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp" 7 8 #include <getopt.h> 9 #include <systemd/sd-bus.h> 10 11 #include <phosphor-logging/elog-errors.hpp> 12 #include <phosphor-logging/lg2.hpp> 13 #include <sdbusplus/exception.hpp> 14 #include <sdbusplus/server.hpp> 15 16 #include <iostream> 17 #include <map> 18 #include <string> 19 20 namespace phosphor 21 { 22 namespace state 23 { 24 namespace manager 25 { 26 27 PHOSPHOR_LOG2_USING; 28 29 using namespace phosphor::logging; 30 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 31 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; 32 33 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 34 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 35 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 36 37 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 38 39 std::string getService(sdbusplus::bus::bus& bus, std::string path, 40 std::string interface) 41 { 42 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 43 MAPPER_INTERFACE, "GetObject"); 44 45 mapper.append(path, std::vector<std::string>({interface})); 46 47 std::map<std::string, std::vector<std::string>> mapperResponse; 48 try 49 { 50 auto mapperResponseMsg = bus.call(mapper); 51 52 mapperResponseMsg.read(mapperResponse); 53 if (mapperResponse.empty()) 54 { 55 error("Mapper response empty, does not have path {PATH} and " 56 "interface {INTERFACE}", 57 "PATH", path, "INTERFACE", interface); 58 throw std::runtime_error("Error reading mapper response"); 59 } 60 } 61 catch (const sdbusplus::exception::exception& e) 62 { 63 error("Error in mapper call for path {PATH} and interface {INTERFACE} " 64 "with error {ERROR}", 65 "PATH", path, "INTERFACE", interface, "ERROR", e); 66 throw; 67 } 68 69 return mapperResponse.begin()->first; 70 } 71 72 std::string getProperty(sdbusplus::bus::bus& bus, std::string path, 73 std::string interface, std::string propertyName) 74 { 75 std::variant<std::string> property; 76 std::string service = getService(bus, path, interface); 77 78 auto method = bus.new_method_call(service.c_str(), path.c_str(), 79 PROPERTY_INTERFACE, "Get"); 80 81 method.append(interface, propertyName); 82 83 try 84 { 85 auto reply = bus.call(method); 86 reply.read(property); 87 } 88 catch (const sdbusplus::exception::exception& e) 89 { 90 error("Error in property Get, error {ERROR}, property {PROPERTY}", 91 "ERROR", e, "PROPERTY", propertyName); 92 throw; 93 } 94 95 if (std::get<std::string>(property).empty()) 96 { 97 error("Error reading property response for {PROPERTY}", "PROPERTY", 98 propertyName); 99 throw std::runtime_error("Error reading property response"); 100 } 101 102 return std::get<std::string>(property); 103 } 104 105 void setProperty(sdbusplus::bus::bus& bus, const std::string& path, 106 const std::string& interface, const std::string& property, 107 const std::string& value) 108 { 109 std::variant<std::string> variantValue = value; 110 std::string service = getService(bus, path, interface); 111 112 auto method = bus.new_method_call(service.c_str(), path.c_str(), 113 PROPERTY_INTERFACE, "Set"); 114 115 method.append(interface, property, variantValue); 116 bus.call_noreply(method); 117 118 return; 119 } 120 121 } // namespace manager 122 } // namespace state 123 } // namespace phosphor 124 125 int main(int argc, char** argv) 126 { 127 using namespace phosphor::logging; 128 129 std::string hostPath = "/xyz/openbmc_project/state/host0"; 130 int arg; 131 int optIndex = 0; 132 133 static struct option longOpts[] = {{"host", required_argument, 0, 'h'}, 134 {0, 0, 0, 0}}; 135 136 while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1) 137 { 138 switch (arg) 139 { 140 case 'h': 141 hostPath = 142 std::string("/xyz/openbmc_project/state/host") + optarg; 143 break; 144 default: 145 break; 146 } 147 } 148 149 auto bus = sdbusplus::bus::new_default(); 150 151 using namespace settings; 152 Objects settings(bus); 153 154 using namespace phosphor::state::manager; 155 namespace server = sdbusplus::xyz::openbmc_project::State::server; 156 157 // This application is only run if chassis power is off 158 159 /* The logic here is to first check the one-time PowerRestorePolicy setting. 160 * If this property is not the default then look at the persistent 161 * user setting in the non one-time object, otherwise honor the one-time 162 * setting. 163 */ 164 auto methodOneTime = bus.new_method_call( 165 settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(), 166 settings.powerRestorePolicyOneTime.c_str(), 167 "org.freedesktop.DBus.Properties", "Get"); 168 methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy"); 169 170 auto methodUserSetting = bus.new_method_call( 171 settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(), 172 settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties", 173 "Get"); 174 methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy"); 175 176 std::variant<std::string> result; 177 try 178 { 179 auto reply = bus.call(methodOneTime); 180 reply.read(result); 181 auto powerPolicy = std::get<std::string>(result); 182 183 if (RestorePolicy::Policy::None == 184 RestorePolicy::convertPolicyFromString(powerPolicy)) 185 { 186 // one_time is set to None so use the customer setting 187 info("One time not set, check user setting of power policy"); 188 auto reply = bus.call(methodUserSetting); 189 reply.read(result); 190 powerPolicy = std::get<std::string>(result); 191 } 192 else 193 { 194 // one_time setting was set so we're going to use it. Reset it 195 // to default for next time. 196 info("One time set, use it and reset to default"); 197 setProperty(bus, settings.powerRestorePolicyOneTime.c_str(), 198 powerRestoreIntf, "PowerRestorePolicy", 199 convertForMessage(RestorePolicy::Policy::None)); 200 } 201 202 info("Host power is off, processing power policy {POWER_POLICY}", 203 "POWER_POLICY", powerPolicy); 204 205 if (RestorePolicy::Policy::AlwaysOn == 206 RestorePolicy::convertPolicyFromString(powerPolicy)) 207 { 208 info("power_policy=ALWAYS_POWER_ON, powering host on"); 209 setProperty(bus, hostPath, HOST_BUSNAME, "RestartCause", 210 convertForMessage( 211 server::Host::RestartCause::PowerPolicyAlwaysOn)); 212 setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 213 convertForMessage(server::Host::Transition::On)); 214 } 215 else if (RestorePolicy::Policy::Restore == 216 RestorePolicy::convertPolicyFromString(powerPolicy)) 217 { 218 info("power_policy=RESTORE, restoring last state"); 219 setProperty( 220 bus, hostPath, HOST_BUSNAME, "RestartCause", 221 convertForMessage( 222 server::Host::RestartCause::PowerPolicyPreviousState)); 223 // Read last requested state and re-request it to execute it 224 auto hostReqState = getProperty(bus, hostPath, HOST_BUSNAME, 225 "RequestedHostTransition"); 226 setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 227 hostReqState); 228 } 229 } 230 catch (const sdbusplus::exception::exception& e) 231 { 232 error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e); 233 elog<InternalFailure>(); 234 } 235 236 return 0; 237 } 238