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
setUnitStatus(sdbusplus::asio::connection & bus,bool status,const std::string & host_instance)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
checkPostComplete(sdbusplus::asio::connection & bus,const std::string & state,bool action,const std::string & host_instance)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. */
checkPostCompleteStartup(sdbusplus::asio::connection & bus,const std::string & host_instance)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. */
checkPostCompleteEvent(sdbusplus::asio::connection & bus,const std::string & host_instance)87 void checkPostCompleteEvent(sdbusplus::asio::connection& bus,
88 const std::string& host_instance)
89 {
90 checkPostComplete(bus, OperatingSystemStateInactive, false, host_instance);
91 }
92
main(int argc,char ** argv)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