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 
setUnitStatus(sdbusplus::asio::connection & bus,bool status)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 
checkPostComplete(sdbusplus::asio::connection & bus,const std::string & state,bool action)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",
52                       postCompleteState);
53 
54             /*
55              * If state is Standby, enable the bare-metal-active systemd
56              * target.
57              * If state is Inactive, no-op cause IPMI is enabled by default.
58              */
59             if (postCompleteState == state)
60             {
61                 setUnitStatus(bus, action);
62             }
63         });
64 }
65 
66 /* This only gets called once on startup. */
checkPostCompleteStartup(sdbusplus::asio::connection & bus)67 void checkPostCompleteStartup(sdbusplus::asio::connection& bus)
68 {
69     checkPostComplete(bus, OperatingSystemStateStandby, true);
70 }
71 
72 /* Gets called when a GPIO state change is detected. */
checkPostCompleteEvent(sdbusplus::asio::connection & bus)73 void checkPostCompleteEvent(sdbusplus::asio::connection& bus)
74 {
75     checkPostComplete(bus, OperatingSystemStateInactive, false);
76 }
77 
main()78 int main()
79 {
80     try
81     {
82         /* Setup connection to dbus. */
83         boost::asio::io_context io;
84         auto conn = sdbusplus::asio::connection(io);
85 
86         /* check IPMI status at startup */
87         checkPostCompleteStartup(conn);
88         /*
89          * Set up an event handler to process Post Complete GPIO state changes.
90          */
91         boost::asio::steady_timer filterTimer(io);
92 
93         auto match = std::make_unique<sdbusplus::bus::match_t>(
94             static_cast<sdbusplus::bus_t&>(conn),
95             std::format(
96                 "type='signal',member='PropertiesChanged',path_namespace='"
97                 "/xyz/openbmc_project/state/os',arg0namespace='{}'",
98                 OperatingSystemStatusInterface),
99             [&](sdbusplus::message_t& message) {
100                 if (message.is_method_error())
101                 {
102                     lg2::error("eventHandler callback method error");
103                     return;
104                 }
105 
106                 /*
107                  * This implicitly cancels the timer, if it's already pending.
108                  * If there's a burst of events within a short period, we want
109                  * to handle them all at once. So, we will wait this long for no
110                  * more events to occur, before processing them.
111                  */
112                 filterTimer.expires_from_now(std::chrono::seconds(1));
113 
114                 filterTimer.async_wait(
115                     [&](const boost::system::error_code& ec) {
116                         if (ec == boost::asio::error::operation_aborted)
117                         {
118                             /* we were canceled */
119                             return;
120                         }
121                         if (ec)
122                         {
123                             lg2::error("timer error");
124                             return;
125                         }
126 
127                         /*
128                          * Stop the bare metal active target if the post
129                          * complete got deasserted.
130                          */
131                         checkPostCompleteEvent(conn);
132                     });
133             });
134 
135         io.run();
136         return 0;
137     }
138     catch (const std::exception& e)
139     {
140         lg2::error(e.what(), "REDFISH_MESSAGE_ID",
141                    std::string("OpenBMC.1.0.ServiceException"));
142 
143         return 2;
144     }
145     return 1;
146 }
147