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