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