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