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