1 #pragma once
2 
3 #include "config.h"
4 
5 #include "settings.hpp"
6 #include "utils.hpp"
7 #include "xyz/openbmc_project/State/Host/server.hpp"
8 
9 #include <cereal/access.hpp>
10 #include <cereal/cereal.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/bus.hpp>
13 #include <xyz/openbmc_project/Control/Boot/RebootAttempts/server.hpp>
14 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
15 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
16 
17 #include <filesystem>
18 #include <string>
19 
20 namespace phosphor
21 {
22 namespace state
23 {
24 namespace manager
25 {
26 
27 using HostInherit = sdbusplus::server::object_t<
28     sdbusplus::xyz::openbmc_project::State::server::Host,
29     sdbusplus::xyz::openbmc_project::State::Boot::server::Progress,
30     sdbusplus::xyz::openbmc_project::Control::Boot::server::RebootAttempts,
31     sdbusplus::xyz::openbmc_project::State::OperatingSystem::server::Status>;
32 
33 PHOSPHOR_LOG2_USING;
34 
35 namespace sdbusRule = sdbusplus::bus::match::rules;
36 namespace fs = std::filesystem;
37 
38 /** @class Host
39  *  @brief OpenBMC host state management implementation.
40  *  @details A concrete implementation for xyz.openbmc_project.State.Host
41  *  DBus API.
42  */
43 class Host : public HostInherit
44 {
45   public:
46     /** @brief Constructs Host State Manager
47      *
48      * @note This constructor passes 'true' to the base class in order to
49      *       defer dbus object registration until we can run
50      *       determineInitialState() and set our properties
51      *
52      * @param[in] bus       - The Dbus bus object
53      * @param[in] objPath   - The Dbus object path
54      * @param[in] id        - The Host id
55      */
56     Host(sdbusplus::bus_t& bus, const char* objPath, size_t id) :
57         HostInherit(bus, objPath, HostInherit::action::defer_emit), bus(bus),
58         systemdSignalJobRemoved(
59             bus,
60             sdbusRule::type::signal() + sdbusRule::member("JobRemoved") +
61                 sdbusRule::path("/org/freedesktop/systemd1") +
62                 sdbusRule::interface("org.freedesktop.systemd1.Manager"),
63             [this](sdbusplus::message_t& m) { sysStateChangeJobRemoved(m); }),
64         systemdSignalJobNew(
65             bus,
66             sdbusRule::type::signal() + sdbusRule::member("JobNew") +
67                 sdbusRule::path("/org/freedesktop/systemd1") +
68                 sdbusRule::interface("org.freedesktop.systemd1.Manager"),
69             [this](sdbusplus::message_t& m) { sysStateChangeJobNew(m); }),
70         settings(bus, id), id(id)
71     {
72         // Enable systemd signals
73         utils::subscribeToSystemdSignals(bus);
74 
75         // create map of target name base on host id
76         createSystemdTargetMaps();
77 
78         // Will throw exception on fail
79         determineInitialState();
80 
81         // Sets auto-reboot attempts to max-allowed
82         attemptsLeft(sdbusplus::xyz::openbmc_project::Control::Boot::server::
83                          RebootAttempts::retryAttempts());
84 
85         // We deferred this until we could get our property correct
86         this->emit_object_added();
87     }
88 
89     /** @brief Set value of HostTransition */
90     Transition requestedHostTransition(Transition value) override;
91 
92     /** @brief Set Value for boot progress */
93     ProgressStages bootProgress(ProgressStages value) override;
94 
95     /** @brief Set Value for Operating System Status */
96     OSStatus operatingSystemState(OSStatus value) override;
97 
98     /** @brief Set value of CurrentHostState */
99     HostState currentHostState(HostState value) override;
100 
101     /**
102      * @brief Set value for allowable auto-reboot count
103      *
104      * This override is responsible for ensuring that when external users
105      * set the number of automatic retry attempts that the number of
106      * automatic reboot attempts left will update accordingly.
107      *
108      * @param[in] value - desired Reboot count value
109      *
110      * @return number of reboot attempts allowed.
111      */
112     uint32_t retryAttempts(uint32_t value) override
113     {
114         if (sdbusplus::xyz::openbmc_project::Control::Boot::server::
115                 RebootAttempts::attemptsLeft() != value)
116         {
117             info("Automatic reboot retry attempts set to: {VALUE} ", "VALUE",
118                  value);
119             sdbusplus::xyz::openbmc_project::Control::Boot::server::
120                 RebootAttempts::attemptsLeft(value);
121         }
122 
123         return (sdbusplus::xyz::openbmc_project::Control::Boot::server::
124                     RebootAttempts::retryAttempts(value));
125     }
126 
127     /**
128      * @brief Set host reboot count to default
129      *
130      * OpenBMC software controls the number of allowed reboot attempts so
131      * any external set request of this property will be overridden by
132      * this function and set to the number of the allowed auto-reboot
133      * retry attempts found on the system.
134      *
135      * The only code responsible for decrementing the boot count resides
136      * within this process and that will use the sub class interface
137      * directly
138      *
139      * @param[in] value  - Reboot count value
140      *
141      * @return number of reboot attempts left(allowed by retry attempts
142      * property)
143      */
144     uint32_t attemptsLeft(uint32_t value) override
145     {
146         debug("External request to reset reboot count");
147         auto retryAttempts = sdbusplus::xyz::openbmc_project::Control::Boot::
148             server::RebootAttempts::retryAttempts();
149         return (
150             sdbusplus::xyz::openbmc_project::Control::Boot::server::
151                 RebootAttempts::attemptsLeft(std::min(value, retryAttempts)));
152     }
153 
154   private:
155     /**
156      * @brief Determine initial host state and set internally
157      *
158      * @return Will throw exceptions on failure
159      **/
160     void determineInitialState();
161 
162     /**
163      * create systemd target instance names and mapping table
164      **/
165     void createSystemdTargetMaps();
166 
167     /** @brief Execute the transition request
168      *
169      * This function assumes the state has been validated and the host
170      * is in an appropriate state for the transition to be started.
171      *
172      * @param[in] tranReq    - Transition requested
173      */
174     void executeTransition(Transition tranReq);
175 
176     /**
177      * @brief Determine if target is active
178      *
179      * This function determines if the target is active and
180      * helps prevent misleading log recorded states.
181      *
182      * @param[in] target - Target string to check on
183      *
184      * @return boolean corresponding to state active
185      **/
186     bool stateActive(const std::string& target);
187 
188     /**
189      * @brief Determine if auto reboot flag is set
190      *
191      * @return boolean corresponding to current auto_reboot setting
192      **/
193     bool isAutoReboot();
194 
195     /** @brief Check if systemd state change is relevant to this object
196      *
197      * Instance specific interface to handle the detected systemd state
198      * change
199      *
200      * @param[in]  msg       - Data associated with subscribed signal
201      *
202      */
203     void sysStateChangeJobRemoved(sdbusplus::message_t& msg);
204 
205     /** @brief Check if JobNew systemd signal is relevant to this object
206      *
207      * In certain instances phosphor-state-manager needs to monitor for the
208      * entry into a systemd target. This function will be used for these cases.
209      *
210      * Instance specific interface to handle the detected systemd state
211      * change
212      *
213      * @param[in]  msg       - Data associated with subscribed signal
214      *
215      */
216     void sysStateChangeJobNew(sdbusplus::message_t& msg);
217 
218     /** @brief Decrement reboot count
219      *
220      * This is used internally to this application to decrement the boot
221      * count on each boot attempt. The host will use the external
222      * attemptsLeft() interface to reset the count when a boot is successful
223      *
224      * @return number of reboot count attempts left
225      */
226     uint32_t decrementRebootCount();
227 
228     // Allow cereal class access to allow these next two function to be
229     // private
230     friend class cereal::access;
231 
232     /** @brief Function required by Cereal to perform serialization.
233      *
234      *  @tparam Archive - Cereal archive type (binary in our case).
235      *  @param[in] archive - reference to Cereal archive.
236      *  @param[in] version - Class version that enables handling
237      *                       a serialized data across code levels
238      */
239     template <class Archive>
240     void save(Archive& archive, const std::uint32_t version) const
241     {
242         // version is not used currently
243         (void)(version);
244         archive(sdbusplus::xyz::openbmc_project::Control::Boot::server::
245                     RebootAttempts::retryAttempts(),
246                 convertForMessage(sdbusplus::xyz::openbmc_project::State::
247                                       server::Host::requestedHostTransition()),
248                 convertForMessage(sdbusplus::xyz::openbmc_project::State::Boot::
249                                       server::Progress::bootProgress()),
250                 convertForMessage(
251                     sdbusplus::xyz::openbmc_project::State::OperatingSystem::
252                         server::Status::operatingSystemState()));
253     }
254 
255     /** @brief Function required by Cereal to perform deserialization.
256      *
257      *  @tparam Archive - Cereal archive type (binary in our case).
258      *  @param[in] archive - reference to Cereal archive.
259      *  @param[in] version - Class version that enables handling
260      *                       a serialized data across code levels
261      */
262     template <class Archive>
263     void load(Archive& archive, const std::uint32_t version)
264     {
265         std::string reqTranState;
266         std::string bootProgress;
267         std::string osState;
268         // Older cereal archive without RetryAttempt may be implemented
269         // just set to (BOOT_COUNT_MAX_ALLOWED)
270         uint32_t retryAttempts = BOOT_COUNT_MAX_ALLOWED;
271         switch (version)
272         {
273             case 2:
274                 archive(retryAttempts);
275                 [[fallthrough]];
276             case 1:
277                 archive(reqTranState, bootProgress, osState);
278                 break;
279         }
280         auto reqTran = Host::convertTransitionFromString(reqTranState);
281         // When restoring, set the requested state with persistent value
282         // but don't call the override which would execute it
283         sdbusplus::xyz::openbmc_project::State::server::Host::
284             requestedHostTransition(reqTran);
285         sdbusplus::xyz::openbmc_project::State::Boot::server::Progress::
286             bootProgress(Host::convertProgressStagesFromString(bootProgress));
287         sdbusplus::xyz::openbmc_project::State::OperatingSystem::server::
288             Status::operatingSystemState(
289                 Host::convertOSStatusFromString(osState));
290         sdbusplus::xyz::openbmc_project::Control::Boot::server::RebootAttempts::
291             retryAttempts(retryAttempts);
292     }
293 
294     /** @brief Serialize and persist requested host state
295      *
296      *  @return fs::path - pathname of persisted requested host state.
297      */
298     fs::path serialize();
299 
300     /** @brief Deserialze a persisted requested host state.
301      *
302      *  @return bool - true if the deserialization was successful, false
303      *                 otherwise.
304      */
305     bool deserialize();
306 
307     /**
308      * @brief Get target name of a HostState
309      *
310      * @param[in] state      -  The state of the host
311      *
312      * @return string - systemd target name of the state
313      */
314     const std::string& getTarget(HostState state);
315 
316     /**
317      * @brief Get target name of a TransitionRequest
318      *
319      * @param[in] tranReq      -  Transition requested
320      *
321      * @return string - systemd target name of Requested transition
322      */
323     const std::string& getTarget(Transition tranReq);
324 
325     /** @brief Persistent sdbusplus DBus bus connection. */
326     sdbusplus::bus_t& bus;
327 
328     /** @brief Used to subscribe to dbus systemd JobRemoved signal **/
329     sdbusplus::bus::match_t systemdSignalJobRemoved;
330 
331     /** @brief Used to subscribe to dbus systemd JobNew signal **/
332     sdbusplus::bus::match_t systemdSignalJobNew;
333 
334     // Settings host objects of interest
335     settings::HostObjects settings;
336 
337     /** @brief Host id. **/
338     const size_t id = 0;
339 
340     /** @brief HostState to systemd target mapping table. **/
341     std::map<HostState, std::string> stateTargetTable;
342 
343     /** @brief Requested Transition to systemd target mapping table. **/
344     std::map<Transition, std::string> transitionTargetTable;
345 
346     /** @brief Target called when a host crash occurs **/
347     std::string hostCrashTarget;
348 };
349 
350 } // namespace manager
351 } // namespace state
352 } // namespace phosphor
353