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