1 #include "config.h"
2 
3 #include <unistd.h>
4 
5 #include <phosphor-logging/elog.hpp>
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <sdbusplus/exception.hpp>
9 #include <xyz/openbmc_project/Logging/Create/client.hpp>
10 #include <xyz/openbmc_project/Logging/Entry/client.hpp>
11 #include <xyz/openbmc_project/State/Boot/Progress/client.hpp>
12 
13 #include <cstdlib>
14 #include <format>
15 #include <fstream>
16 #include <string>
17 
18 namespace phosphor
19 {
20 namespace state
21 {
22 namespace manager
23 {
24 
25 PHOSPHOR_LOG2_USING;
26 
27 using BootProgress =
28     sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>;
29 using LoggingCreate =
30     sdbusplus::client::xyz::openbmc_project::logging::Create<>;
31 using LoggingEntry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>;
32 
33 constexpr auto HOST_STATE_SVC = "xyz.openbmc_project.State.Host";
34 constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
35 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
36 constexpr auto BOOT_PROGRESS_PROP = "BootProgress";
37 
38 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
39 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
40 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
41 constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
42 
wasHostBooting(sdbusplus::bus_t & bus)43 bool wasHostBooting(sdbusplus::bus_t& bus)
44 {
45     try
46     {
47         using ProgressStages = BootProgress::ProgressStages;
48 
49         auto method = bus.new_method_call(HOST_STATE_SVC, HOST_STATE_PATH,
50                                           PROPERTY_INTERFACE, "Get");
51         method.append(BootProgress::interface, BOOT_PROGRESS_PROP);
52 
53         auto response = bus.call(method);
54 
55         std::variant<ProgressStages> bootProgressV;
56         response.read(bootProgressV);
57         auto bootProgress = std::get<ProgressStages>(bootProgressV);
58 
59         if (bootProgress == ProgressStages::Unspecified)
60         {
61             info("Host was not booting before BMC reboot");
62             return false;
63         }
64 
65         info("Host was booting before BMC reboot: {BOOTPROGRESS}",
66              "BOOTPROGRESS", bootProgress);
67     }
68     catch (const sdbusplus::exception_t& e)
69     {
70         error("Error reading BootProgress, error {ERROR}, service {SERVICE}, "
71               "path {PATH}",
72               "ERROR", e, "SERVICE", HOST_STATE_SVC, "PATH", HOST_STATE_PATH);
73 
74         throw;
75     }
76 
77     return true;
78 }
79 
createErrorLog(sdbusplus::bus_t & bus)80 void createErrorLog(sdbusplus::bus_t& bus)
81 {
82     try
83     {
84         // Create interface requires something for additionalData
85         std::map<std::string, std::string> additionalData;
86         additionalData.emplace("_PID", std::to_string(getpid()));
87 
88         static constexpr auto errorMessage =
89             "xyz.openbmc_project.State.Error.HostNotRunning";
90         auto method = bus.new_method_call(LoggingCreate::default_service,
91                                           LoggingCreate::instance_path,
92                                           LoggingCreate::interface, "Create");
93         method.append(errorMessage, LoggingEntry::Level::Error, additionalData);
94         auto resp = bus.call(method);
95     }
96     catch (const sdbusplus::exception_t& e)
97     {
98         error(
99             "sdbusplus D-Bus call exception, error {ERROR}, objpath {OBJPATH}, "
100             "interface {INTERFACE}",
101             "ERROR", e, "OBJPATH", LoggingCreate::instance_path, "INTERFACE",
102             LoggingCreate::interface);
103 
104         throw std::runtime_error(
105             "Error in invoking D-Bus logging create interface");
106     }
107     catch (const std::exception& e)
108     {
109         error("D-bus call exception: {ERROR}", "ERROR", e);
110         throw e;
111     }
112 }
113 
114 // Once CHASSIS_ON_FILE is removed, the obmc-chassis-poweron@.target has
115 // completed and the phosphor-chassis-state-manager code has processed it.
isChassisTargetComplete()116 bool isChassisTargetComplete()
117 {
118     auto chassisFile = std::format(CHASSIS_ON_FILE, 0);
119 
120     std::ifstream f(chassisFile);
121     return !f.good();
122 }
123 
moveToHostQuiesce(sdbusplus::bus_t & bus)124 void moveToHostQuiesce(sdbusplus::bus_t& bus)
125 {
126     try
127     {
128         auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
129                                           SYSTEMD_INTERFACE, "StartUnit");
130 
131         method.append(HOST_STATE_QUIESCE_TGT);
132         method.append("replace");
133 
134         bus.call_noreply(method);
135     }
136     catch (const sdbusplus::exception_t& e)
137     {
138         error("sdbusplus call exception starting quiesce target: {ERROR}",
139               "ERROR", e);
140 
141         throw std::runtime_error(
142             "Error in invoking D-Bus systemd StartUnit method");
143     }
144 }
145 
146 } // namespace manager
147 } // namespace state
148 } // namespace phosphor
149 
main()150 int main()
151 {
152     using namespace phosphor::state::manager;
153     PHOSPHOR_LOG2_USING;
154 
155     auto bus = sdbusplus::bus::new_default();
156 
157     // Chassis power is on if this service starts but need to wait for the
158     // obmc-chassis-poweron@.target to complete before potentially initiating
159     // another systemd target transition (i.e. Quiesce->Reboot)
160     while (!isChassisTargetComplete())
161     {
162         debug("Waiting for chassis on target to complete");
163         std::this_thread::sleep_for(std::chrono::seconds(1));
164 
165         // There is no timeout here, wait until it happens or until system
166         // is powered off and this service is stopped
167     }
168 
169     info("Chassis power on has completed, checking if host is "
170          "still running after the BMC reboot");
171 
172     // Check the last BootProgeress to see if the host was booting before
173     // the BMC reboot occurred
174     if (!wasHostBooting(bus))
175     {
176         return 0;
177     }
178 
179     // Host was booting before the BMC reboot so log an error and go to host
180     // quiesce target
181     createErrorLog(bus);
182     moveToHostQuiesce(bus);
183 
184     return 0;
185 }
186