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