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