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, const std::string& path, 93 const std::string& interface, const std::string& property, 94 const std::string& value) 95 { 96 sdbusplus::message::variant<std::string> variantValue = value; 97 std::string service = getService(bus, path, interface); 98 99 auto method = bus.new_method_call(service.c_str(), path.c_str(), 100 PROPERTY_INTERFACE, "Set"); 101 102 method.append(interface, property, variantValue); 103 bus.call_noreply(method); 104 105 return; 106 } 107 108 } // namespace manager 109 } // namespace state 110 } // namespace phosphor 111 112 int main(int argc, char** argv) 113 { 114 using namespace phosphor::logging; 115 116 std::string hostPath = "/xyz/openbmc_project/state/host0"; 117 int arg; 118 int optIndex = 0; 119 120 static struct option longOpts[] = {{"host", required_argument, 0, 'h'}, 121 {0, 0, 0, 0}}; 122 123 while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1) 124 { 125 switch (arg) 126 { 127 case 'h': 128 hostPath = 129 std::string("/xyz/openbmc_project/state/host") + optarg; 130 break; 131 default: 132 break; 133 } 134 } 135 136 auto bus = sdbusplus::bus::new_default(); 137 138 using namespace settings; 139 Objects settings(bus); 140 141 using namespace phosphor::state::manager; 142 namespace server = sdbusplus::xyz::openbmc_project::State::server; 143 144 // This application is only run if chassis power is off 145 146 auto method = bus.new_method_call( 147 settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(), 148 settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties", 149 "Get"); 150 method.append(powerRestoreIntf, "PowerRestorePolicy"); 151 auto reply = bus.call(method); 152 if (reply.is_method_error()) 153 { 154 log<level::ERR>("Error in PowerRestorePolicy Get"); 155 elog<InternalFailure>(); 156 } 157 158 sdbusplus::message::variant<std::string> result; 159 reply.read(result); 160 auto powerPolicy = sdbusplus::message::variant_ns::get<std::string>(result); 161 162 log<level::INFO>("Host power is off, checking power policy", 163 entry("POWER_POLICY=%s", powerPolicy.c_str())); 164 165 if (RestorePolicy::Policy::AlwaysOn == 166 RestorePolicy::convertPolicyFromString(powerPolicy)) 167 { 168 log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on"); 169 setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 170 convertForMessage(server::Host::Transition::On)); 171 } 172 else if (RestorePolicy::Policy::Restore == 173 RestorePolicy::convertPolicyFromString(powerPolicy)) 174 { 175 log<level::INFO>("power_policy=RESTORE, restoring last state"); 176 177 // Read last requested state and re-request it to execute it 178 auto hostReqState = 179 getProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition"); 180 setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", 181 hostReqState); 182 } 183 184 return 0; 185 } 186