1 #include "bmc_state_manager.hpp"
2 
3 #include "xyz/openbmc_project/Common/error.hpp"
4 
5 #include <sys/sysinfo.h>
6 
7 #include <phosphor-logging/elog-errors.hpp>
8 #include <phosphor-logging/log.hpp>
9 #include <sdbusplus/exception.hpp>
10 
11 #include <cassert>
12 
13 namespace phosphor
14 {
15 namespace state
16 {
17 namespace manager
18 {
19 
20 // When you see server:: you know we're referencing our base class
21 namespace server = sdbusplus::xyz::openbmc_project::State::server;
22 
23 using namespace phosphor::logging;
24 using sdbusplus::exception::SdBusError;
25 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
26 
27 constexpr auto obmcStandbyTarget = "multi-user.target";
28 constexpr auto signalDone = "done";
29 constexpr auto activeState = "active";
30 
31 /* Map a transition to it's systemd target */
32 const std::map<server::BMC::Transition, const char*> SYSTEMD_TABLE = {
33     {server::BMC::Transition::Reboot, "reboot.target"}};
34 
35 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
36 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
37 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
38 constexpr auto SYSTEMD_PRP_INTERFACE = "org.freedesktop.DBus.Properties";
39 
40 void BMC::discoverInitialState()
41 {
42     std::variant<std::string> currentState;
43     sdbusplus::message::object_path unitTargetPath;
44 
45     auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
46                                             SYSTEMD_INTERFACE, "GetUnit");
47 
48     method.append(obmcStandbyTarget);
49 
50     try
51     {
52         auto result = this->bus.call(method);
53         result.read(unitTargetPath);
54     }
55     catch (const SdBusError& e)
56     {
57         log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
58         return;
59     }
60 
61     method = this->bus.new_method_call(
62         SYSTEMD_SERVICE,
63         static_cast<const std::string&>(unitTargetPath).c_str(),
64         SYSTEMD_PRP_INTERFACE, "Get");
65 
66     method.append("org.freedesktop.systemd1.Unit", "ActiveState");
67 
68     try
69     {
70         auto result = this->bus.call(method);
71 
72         // Is obmc-standby.target active or inactive?
73         result.read(currentState);
74     }
75     catch (const SdBusError& e)
76     {
77         log<level::INFO>("Error in ActiveState Get",
78                          entry("ERROR=%s", e.what()));
79         return;
80     }
81 
82     auto currentStateStr = std::get<std::string>(currentState);
83     if (currentStateStr == activeState)
84     {
85         log<level::INFO>("Setting the BMCState field",
86                          entry("CURRENT_BMC_STATE=%s", "BMC_READY"));
87         this->currentBMCState(BMCState::Ready);
88 
89         // Unsubscribe so we stop processing all other signals
90         method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
91                                            SYSTEMD_INTERFACE, "Unsubscribe");
92         try
93         {
94             this->bus.call(method);
95             this->stateSignal.release();
96         }
97         catch (const SdBusError& e)
98         {
99             log<level::INFO>("Error in Unsubscribe",
100                              entry("ERROR=%s", e.what()));
101         }
102     }
103     else
104     {
105         log<level::INFO>("Setting the BMCState field",
106                          entry("CURRENT_BMC_STATE=%s", "BMC_NOTREADY"));
107         this->currentBMCState(BMCState::NotReady);
108     }
109 
110     return;
111 }
112 
113 void BMC::subscribeToSystemdSignals()
114 {
115     auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
116                                             SYSTEMD_INTERFACE, "Subscribe");
117 
118     try
119     {
120         this->bus.call(method);
121     }
122     catch (const SdBusError& e)
123     {
124         log<level::ERR>("Failed to subscribe to systemd signals",
125                         entry("ERR=%s", e.what()));
126         elog<InternalFailure>();
127     }
128 
129     return;
130 }
131 
132 void BMC::executeTransition(const Transition tranReq)
133 {
134     // HardReboot does not shutdown any services and immediately transitions
135     // into the reboot process
136     if (server::BMC::Transition::HardReboot == tranReq)
137     {
138         auto method = this->bus.new_method_call(
139             SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot");
140         try
141         {
142             this->bus.call(method);
143         }
144         catch (const SdBusError& e)
145         {
146             log<level::INFO>("Error in HardReboot",
147                              entry("ERROR=%s", e.what()));
148         }
149     }
150     else
151     {
152         // Check to make sure it can be found
153         auto iter = SYSTEMD_TABLE.find(tranReq);
154         if (iter == SYSTEMD_TABLE.end())
155             return;
156 
157         const auto& sysdUnit = iter->second;
158 
159         auto method = this->bus.new_method_call(
160             SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit");
161         // The only valid transition is reboot and that
162         // needs to be irreversible once started
163 
164         method.append(sysdUnit, "replace-irreversibly");
165 
166         try
167         {
168             this->bus.call(method);
169         }
170         catch (const SdBusError& e)
171         {
172             log<level::INFO>("Error in StartUnit - replace-irreversibly",
173                              entry("ERROR=%s", e.what()));
174         }
175     }
176     return;
177 }
178 
179 int BMC::bmcStateChange(sdbusplus::message::message& msg)
180 {
181     uint32_t newStateID{};
182     sdbusplus::message::object_path newStateObjPath;
183     std::string newStateUnit{};
184     std::string newStateResult{};
185 
186     // Read the msg and populate each variable
187     msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
188 
189     // Caught the signal that indicates the BMC is now BMC_READY
190     if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone))
191     {
192         log<level::INFO>("BMC_READY");
193         this->currentBMCState(BMCState::Ready);
194 
195         // Unsubscribe so we stop processing all other signals
196         auto method =
197             this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
198                                       SYSTEMD_INTERFACE, "Unsubscribe");
199 
200         try
201         {
202             this->bus.call(method);
203             this->stateSignal.release();
204         }
205         catch (const SdBusError& e)
206         {
207             log<level::INFO>("Error in Unsubscribe",
208                              entry("ERROR=%s", e.what()));
209         }
210     }
211 
212     return 0;
213 }
214 
215 BMC::Transition BMC::requestedBMCTransition(Transition value)
216 {
217     log<level::INFO>("Setting the RequestedBMCTransition field",
218                      entry("REQUESTED_BMC_TRANSITION=0x%s",
219                            convertForMessage(value).c_str()));
220 
221     executeTransition(value);
222     return server::BMC::requestedBMCTransition(value);
223 }
224 
225 BMC::BMCState BMC::currentBMCState(BMCState value)
226 {
227     log<level::INFO>(
228         "Setting the BMCState field",
229         entry("CURRENT_BMC_STATE=0x%s", convertForMessage(value).c_str()));
230 
231     return server::BMC::currentBMCState(value);
232 }
233 
234 uint64_t BMC::lastRebootTime() const
235 {
236     using namespace std::chrono;
237     struct sysinfo info;
238 
239     auto rc = sysinfo(&info);
240     assert(rc == 0);
241 
242     // Since uptime is in seconds, also get the current time in seconds.
243     auto now = time_point_cast<seconds>(system_clock::now());
244     auto rebootTime = now - seconds(info.uptime);
245 
246     return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count();
247 }
248 
249 } // namespace manager
250 } // namespace state
251 } // namespace phosphor
252