1 2 #include "absl/flags/flag.h" 3 #include "absl/flags/parse.h" 4 5 #include <boost/asio/io_context.hpp> 6 #include <boost/asio/steady_timer.hpp> 7 #include <phosphor-logging/lg2.hpp> 8 #include <sdbusplus/asio/connection.hpp> 9 #include <sdbusplus/asio/property.hpp> 10 #include <sdbusplus/bus.hpp> 11 #include <sdbusplus/bus/match.hpp> 12 13 #include <format> 14 15 ABSL_FLAG(std::string, host_label, "0", 16 "Label for the host in question. Usually this is an integer."); 17 18 const constexpr char* OperatingSystemService = 19 "xyz.openbmc_project.State.OperatingSystem{}"; 20 const constexpr char* OperatingSystemPath = "/xyz/openbmc_project/state/os"; 21 const constexpr char* OperatingSystemStatusInterface = 22 "xyz.openbmc_project.State.OperatingSystem.Status"; 23 const constexpr char* OperatingSystemStateProperty = "OperatingSystemState"; 24 const constexpr char* OperatingSystemStateStandby = 25 "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby"; 26 const constexpr char* OperatingSystemStateInactive = 27 "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive"; 28 const constexpr char* BareMetalActiveTargetTemplate = 29 "gbmc-bare-metal-active@{}.target"; 30 31 const constexpr char* SystemdService = "org.freedesktop.systemd1"; 32 const constexpr char* SystemdManagerObject = "/org/freedesktop/systemd1"; 33 const constexpr char* SystemdManagerInterface = 34 "org.freedesktop.systemd1.Manager"; 35 36 void setUnitStatus(sdbusplus::asio::connection& bus, bool status, 37 const std::string& host_instance) 38 { 39 auto method = bus.new_method_call(SystemdService, SystemdManagerObject, 40 SystemdManagerInterface, 41 status ? "StartUnit" : "StopUnit"); 42 method.append(std::format(BareMetalActiveTargetTemplate, host_instance), 43 "replace"); 44 45 bus.call(method); 46 } 47 48 void checkPostComplete(sdbusplus::asio::connection& bus, 49 const std::string& state, bool action, 50 const std::string& host_instance) 51 { 52 sdbusplus::asio::getProperty<std::string>( 53 bus, std::format(OperatingSystemService, host_instance), 54 OperatingSystemPath, OperatingSystemStatusInterface, 55 OperatingSystemStateProperty, 56 [&, state, action](const boost::system::error_code& ec, 57 const std::string& postCompleteState) { 58 if (ec) 59 { 60 lg2::error("Error when checking Post Complete GPIO state"); 61 return; 62 } 63 64 lg2::info("Post Complete state is {STATE}", "STATE", 65 postCompleteState); 66 67 /* 68 * If state is Standby, enable the bare-metal-active systemd 69 * target. 70 * If state is Inactive, no-op cause IPMI is enabled by default. 71 */ 72 if (postCompleteState == state) 73 { 74 setUnitStatus(bus, action, host_instance); 75 } 76 }); 77 } 78 79 /* This only gets called once on startup. */ 80 void checkPostCompleteStartup(sdbusplus::asio::connection& bus, 81 const std::string& host_instance) 82 { 83 checkPostComplete(bus, OperatingSystemStateStandby, true, host_instance); 84 } 85 86 /* Gets called when a GPIO state change is detected. */ 87 void checkPostCompleteEvent(sdbusplus::asio::connection& bus, 88 const std::string& host_instance) 89 { 90 checkPostComplete(bus, OperatingSystemStateInactive, false, host_instance); 91 } 92 93 int main(int argc, char** argv) 94 { 95 absl::ParseCommandLine(argc, argv); 96 std::string host_label = absl::GetFlag(FLAGS_host_label); 97 98 try 99 { 100 /* Setup connection to dbus. */ 101 boost::asio::io_context io; 102 auto conn = sdbusplus::asio::connection(io); 103 104 /* check IPMI status at startup */ 105 checkPostCompleteStartup(conn, host_label); 106 /* 107 * Set up an event handler to process Post Complete GPIO state changes. 108 */ 109 boost::asio::steady_timer filterTimer(io); 110 111 auto match = std::make_unique<sdbusplus::bus::match_t>( 112 static_cast<sdbusplus::bus_t&>(conn), 113 std::format( 114 "type='signal',member='PropertiesChanged',path_namespace='" 115 "/xyz/openbmc_project/state/os',arg0namespace='{}'", 116 OperatingSystemStatusInterface), 117 [&](sdbusplus::message_t& message) { 118 if (message.is_method_error()) 119 { 120 lg2::error("eventHandler callback method error"); 121 return; 122 } 123 124 /* 125 * This implicitly cancels the timer, if it's already pending. 126 * If there's a burst of events within a short period, we want 127 * to handle them all at once. So, we will wait this long for no 128 * more events to occur, before processing them. 129 */ 130 filterTimer.expires_from_now(std::chrono::seconds(1)); 131 132 filterTimer.async_wait( 133 [&](const boost::system::error_code& ec) { 134 if (ec == boost::asio::error::operation_aborted) 135 { 136 /* we were canceled */ 137 return; 138 } 139 if (ec) 140 { 141 lg2::error("timer error"); 142 return; 143 } 144 145 /* 146 * Stop the bare metal active target if the post 147 * complete got deasserted. 148 */ 149 checkPostCompleteEvent(conn, host_label); 150 }); 151 }); 152 153 io.run(); 154 return 0; 155 } 156 catch (const std::exception& e) 157 { 158 lg2::error(e.what(), "REDFISH_MESSAGE_ID", 159 std::string("OpenBMC.1.0.ServiceException")); 160 161 return 2; 162 } 163 return 1; 164 } 165