1 #pragma once
2 
3 #include <string>
4 #include <functional>
5 #include <experimental/filesystem>
6 #include <cereal/access.hpp>
7 #include <cereal/cereal.hpp>
8 #include <sdbusplus/bus.hpp>
9 #include <phosphor-logging/log.hpp>
10 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
11 #include <xyz/openbmc_project/Control/Boot/RebootAttempts/server.hpp>
12 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
13 #include "xyz/openbmc_project/State/Host/server.hpp"
14 #include "settings.hpp"
15 #include "config.h"
16 
17 namespace phosphor
18 {
19 namespace state
20 {
21 namespace manager
22 {
23 
24 using HostInherit = sdbusplus::server::object::object<
25     sdbusplus::xyz::openbmc_project::State::server::Host,
26     sdbusplus::xyz::openbmc_project::State::Boot::server::Progress,
27     sdbusplus::xyz::openbmc_project::Control::Boot::server::RebootAttempts,
28     sdbusplus::xyz::openbmc_project::State::OperatingSystem::server::Status>;
29 
30 using namespace phosphor::logging;
31 
32 namespace sdbusRule = sdbusplus::bus::match::rules;
33 namespace fs = std::experimental::filesystem;
34 
35 /** @class Host
36  *  @brief OpenBMC host state management implementation.
37  *  @details A concrete implementation for xyz.openbmc_project.State.Host
38  *  DBus API.
39  */
40 class Host : public HostInherit
41 {
42   public:
43     /** @brief Constructs Host State Manager
44      *
45      * @note This constructor passes 'true' to the base class in order to
46      *       defer dbus object registration until we can run
47      *       determineInitialState() and set our properties
48      *
49      * @param[in] bus       - The Dbus bus object
50      * @param[in] busName   - The Dbus name to own
51      * @param[in] objPath   - The Dbus object path
52      */
53     Host(sdbusplus::bus::bus& bus, const char* busName, const char* objPath) :
54         HostInherit(bus, objPath, true), bus(bus),
55         systemdSignals(
56             bus,
57             sdbusRule::type::signal() + sdbusRule::member("JobRemoved") +
58                 sdbusRule::path("/org/freedesktop/systemd1") +
59                 sdbusRule::interface("org.freedesktop.systemd1.Manager"),
60             std::bind(std::mem_fn(&Host::sysStateChange), this,
61                       std::placeholders::_1)),
62         settings(bus)
63     {
64         // Enable systemd signals
65         subscribeToSystemdSignals();
66 
67         // Will throw exception on fail
68         determineInitialState();
69 
70         attemptsLeft(BOOT_COUNT_MAX_ALLOWED);
71 
72         // We deferred this until we could get our property correct
73         this->emit_object_added();
74     }
75 
76     /** @brief Set value of HostTransition */
77     Transition requestedHostTransition(Transition value) override;
78 
79     /** @brief Set Value for boot progress */
80     ProgressStages bootProgress(ProgressStages value) override;
81 
82     /** @brief Set Value for Operating System Status */
83     OSStatus operatingSystemState(OSStatus value) override;
84 
85     /** @brief Set value of CurrentHostState */
86     HostState currentHostState(HostState value) override;
87 
88     /**
89      * @brief Set host reboot count to default
90      *
91      * OpenBMC software controls the number of allowed reboot attempts so
92      * any external set request of this property will be overridden by
93      * this function and set to the default.
94      *
95      * The only code responsible for decrementing the boot count resides
96      * within this process and that will use the sub class interface
97      * directly
98      *
99      * @param[in] value      - Reboot count value, will be ignored
100      *
101      * @return Default number of reboot attempts left
102      */
103     uint32_t attemptsLeft(uint32_t value) override
104     {
105         log<level::DEBUG>("External request to reset reboot count");
106         return (sdbusplus::xyz::openbmc_project::Control::Boot::server::
107                     RebootAttempts::attemptsLeft(BOOT_COUNT_MAX_ALLOWED));
108     }
109 
110   private:
111     /**
112      * @brief subscribe to the systemd signals
113      *
114      * This object needs to capture when it's systemd targets complete
115      * so it can keep it's state updated
116      *
117      **/
118     void subscribeToSystemdSignals();
119 
120     /**
121      * @brief Determine initial host state and set internally
122      *
123      * @return Will throw exceptions on failure
124      **/
125     void determineInitialState();
126 
127     /** @brief Execute the transition request
128      *
129      * This function assumes the state has been validated and the host
130      * is in an appropriate state for the transition to be started.
131      *
132      * @param[in] tranReq    - Transition requested
133      */
134     void executeTransition(Transition tranReq);
135 
136     /**
137      * @brief Determine if target is active
138      *
139      * This function determines if the target is active and
140      * helps prevent misleading log recorded states.
141      *
142      * @param[in] target - Target string to check on
143      *
144      * @return boolean corresponding to state active
145      **/
146     bool stateActive(const std::string& target);
147 
148     /**
149      * @brief Determine if auto reboot flag is set
150      *
151      * @return boolean corresponding to current auto_reboot setting
152      **/
153     bool isAutoReboot();
154 
155     /** @brief Check if systemd state change is relevant to this object
156      *
157      * Instance specific interface to handle the detected systemd state
158      * change
159      *
160      * @param[in]  msg       - Data associated with subscribed signal
161      *
162      */
163     void sysStateChange(sdbusplus::message::message& msg);
164 
165     /** @brief Decrement reboot count
166      *
167      * This is used internally to this application to decrement the boot
168      * count on each boot attempt. The host will use the external
169      * attemptsLeft() interface to reset the count when a boot is successful
170      *
171      * @return number of reboot count attempts left
172      */
173     uint32_t decrementRebootCount();
174 
175     // Allow cereal class access to allow these next two function to be
176     // private
177     friend class cereal::access;
178 
179     /** @brief Function required by Cereal to perform serialization.
180      *
181      *  @tparam Archive - Cereal archive type (binary in our case).
182      *  @param[in] archive - reference to Cereal archive.
183      *  @param[in] version - Class version that enables handling
184      *                       a serialized data across code levels
185      */
186     template <class Archive>
187     void save(Archive& archive, const std::uint32_t version) const
188     {
189         archive(convertForMessage(sdbusplus::xyz::openbmc_project::State::
190                                       server::Host::requestedHostTransition()),
191                 convertForMessage(sdbusplus::xyz::openbmc_project::State::Boot::
192                                       server::Progress::bootProgress()),
193                 convertForMessage(
194                     sdbusplus::xyz::openbmc_project::State::OperatingSystem::
195                         server::Status::operatingSystemState()));
196     }
197 
198     /** @brief Function required by Cereal to perform deserialization.
199      *
200      *  @tparam Archive - Cereal archive type (binary in our case).
201      *  @param[in] archive - reference to Cereal archive.
202      *  @param[in] version - Class version that enables handling
203      *                       a serialized data across code levels
204      */
205     template <class Archive>
206     void load(Archive& archive, const std::uint32_t version)
207     {
208         std::string reqTranState;
209         std::string bootProgress;
210         std::string osState;
211         archive(reqTranState, bootProgress, osState);
212         auto reqTran = Host::convertTransitionFromString(reqTranState);
213         // When restoring, set the requested state with persistent value
214         // but don't call the override which would execute it
215         sdbusplus::xyz::openbmc_project::State::server::Host::
216             requestedHostTransition(reqTran);
217         sdbusplus::xyz::openbmc_project::State::Boot::server::Progress::
218             bootProgress(Host::convertProgressStagesFromString(bootProgress));
219         sdbusplus::xyz::openbmc_project::State::OperatingSystem::server::
220             Status::operatingSystemState(
221                 Host::convertOSStatusFromString(osState));
222     }
223 
224     /** @brief Serialize and persist requested host state
225      *
226      *  @param[in] dir - pathname of file where the serialized host state will
227      *                   be placed.
228      *
229      *  @return fs::path - pathname of persisted requested host state.
230      */
231     fs::path serialize(const fs::path& dir = fs::path(HOST_STATE_PERSIST_PATH));
232 
233     /** @brief Deserialze a persisted requested host state.
234      *
235      *  @param[in] path - pathname of persisted host state file
236      *
237      *  @return bool - true if the deserialization was successful, false
238      *                 otherwise.
239      */
240     bool deserialize(const fs::path& path);
241 
242     /** @brief Persistent sdbusplus DBus bus connection. */
243     sdbusplus::bus::bus& bus;
244 
245     /** @brief Used to subscribe to dbus systemd signals **/
246     sdbusplus::bus::match_t systemdSignals;
247 
248     // Settings objects of interest
249     settings::Objects settings;
250 };
251 
252 } // namespace manager
253 } // namespace state
254 } // namespace phosphor
255