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