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