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