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