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