1 #include <getopt.h> 2 #include <iostream> 3 #include <map> 4 #include <string> 5 #include <config.h> 6 #include <systemd/sd-bus.h> 7 #include <sdbusplus/server.hpp> 8 #include <phosphor-logging/log.hpp> 9 #include <phosphor-logging/elog-errors.hpp> 10 #include "host_state_manager.hpp" 11 #include "settings.hpp" 12 #include "xyz/openbmc_project/Common/error.hpp" 13 #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp" 14 15 namespace phosphor 16 { 17 namespace state 18 { 19 namespace manager 20 { 21 22 using namespace phosphor::logging; 23 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 24 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; 25 26 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 27 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 28 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 29 30 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 31 32 std::string getService(sdbusplus::bus::bus& bus, std::string path, 33 std::string interface) 34 { 35 auto mapper = bus.new_method_call(MAPPER_BUSNAME, 36 MAPPER_PATH, 37 MAPPER_INTERFACE, 38 "GetObject"); 39 40 mapper.append(path, std::vector<std::string>({interface})); 41 auto mapperResponseMsg = bus.call(mapper); 42 43 if (mapperResponseMsg.is_method_error()) 44 { 45 log<level::ERR>("Error in mapper call", 46 entry("PATH=%s", path.c_str()), 47 entry("INTERFACE=%s", interface.c_str())); 48 throw std::runtime_error("Error in mapper call"); 49 } 50 51 std::map<std::string, std::vector<std::string>> mapperResponse; 52 mapperResponseMsg.read(mapperResponse); 53 if (mapperResponse.empty()) 54 { 55 log<level::ERR>("Error reading mapper response", 56 entry("PATH=%s", path.c_str()), 57 entry("INTERFACE=%s", interface.c_str())); 58 throw std::runtime_error("Error reading mapper response"); 59 } 60 61 return mapperResponse.begin()->first; 62 } 63 64 std::string getProperty(sdbusplus::bus::bus& bus, std::string path, 65 std::string interface, std::string propertyName) 66 { 67 sdbusplus::message::variant<std::string> property; 68 std::string service = getService(bus, path, interface); 69 70 auto method = bus.new_method_call(service.c_str(), 71 path.c_str(), 72 PROPERTY_INTERFACE, 73 "Get"); 74 75 method.append(interface, propertyName); 76 auto reply = bus.call(method); 77 78 if (reply.is_method_error()) 79 { 80 log<level::ERR>("Error in property Get", 81 entry("PROPERTY=%s", propertyName.c_str())); 82 throw std::runtime_error("Error in property Get"); 83 } 84 85 reply.read(property); 86 87 if (sdbusplus::message::variant_ns::get<std::string>(property).empty()) 88 { 89 log<level::ERR>("Error reading property response", 90 entry("PROPERTY=%s", propertyName.c_str())); 91 throw std::runtime_error("Error reading property response"); 92 } 93 94 return sdbusplus::message::variant_ns::get<std::string>(property); 95 } 96 97 void setProperty(sdbusplus::bus::bus& bus, std::string path, 98 std::string interface, std::string property, std::string value) 99 { 100 sdbusplus::message::variant<std::string> variantValue = value; 101 std::string service = getService(bus, path, interface); 102 103 auto method = bus.new_method_call(service.c_str(), 104 path.c_str(), 105 PROPERTY_INTERFACE, 106 "Set"); 107 108 method.append(interface, property, variantValue); 109 bus.call_noreply(method); 110 111 return; 112 } 113 114 } // namespace manager 115 } // namespace state 116 } // namepsace phosphor 117 118 int main(int argc, char** argv) 119 { 120 using namespace phosphor::logging; 121 122 std::string hostPath = "/xyz/openbmc_project/state/host0"; 123 int arg; 124 int optIndex = 0; 125 126 static struct option longOpts[] = 127 { 128 {"host", required_argument, 0, 'h'}, 129 {0, 0, 0, 0} 130 }; 131 132 while((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1) 133 { 134 switch (arg) 135 { 136 case 'h': 137 hostPath = std::string("/xyz/openbmc_project/state/host") + 138 optarg; 139 break; 140 default: 141 break; 142 } 143 } 144 145 auto bus = sdbusplus::bus::new_default(); 146 147 using namespace settings; 148 Objects settings(bus); 149 150 using namespace phosphor::state::manager; 151 namespace server = sdbusplus::xyz::openbmc_project::State::server; 152 153 // This application is only run if chassis power is off 154 155 auto method = 156 bus.new_method_call( 157 settings.service(settings.powerRestorePolicy, 158 powerRestoreIntf).c_str(), 159 settings.powerRestorePolicy.c_str(), 160 "org.freedesktop.DBus.Properties", 161 "Get"); 162 method.append(powerRestoreIntf, "PowerRestorePolicy"); 163 auto reply = bus.call(method); 164 if (reply.is_method_error()) 165 { 166 log<level::ERR>("Error in PowerRestorePolicy Get"); 167 elog<InternalFailure>(); 168 } 169 170 sdbusplus::message::variant<std::string> result; 171 reply.read(result); 172 auto powerPolicy = result.get<std::string>(); 173 174 log<level::INFO>("Host power is off, checking power policy", 175 entry("POWER_POLICY=%s", powerPolicy.c_str())); 176 177 if (RestorePolicy::Policy::AlwaysOn == 178 RestorePolicy::convertPolicyFromString(powerPolicy)) 179 { 180 log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on"); 181 setProperty(bus, hostPath, HOST_BUSNAME, 182 "RequestedHostTransition", 183 convertForMessage(server::Host::Transition::On)); 184 } 185 else if(RestorePolicy::Policy::Restore == 186 RestorePolicy::convertPolicyFromString(powerPolicy)) 187 { 188 log<level::INFO>("power_policy=RESTORE, restoring last state"); 189 190 // Read last requested state and re-request it to execute it 191 auto hostReqState = getProperty(bus, hostPath, 192 HOST_BUSNAME, 193 "RequestedHostTransition"); 194 setProperty(bus, hostPath, HOST_BUSNAME, 195 "RequestedHostTransition", 196 hostReqState); 197 } 198 199 return 0; 200 } 201