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