1 #pragma once 2 3 #include "config.h" 4 5 #include "settings.hpp" 6 #include "utils.hpp" 7 #include "xyz/openbmc_project/State/Host/server.hpp" 8 9 #include <cereal/access.hpp> 10 #include <cereal/cereal.hpp> 11 #include <phosphor-logging/lg2.hpp> 12 #include <sdbusplus/bus.hpp> 13 #include <xyz/openbmc_project/Control/Boot/RebootAttempts/server.hpp> 14 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp> 15 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp> 16 17 #include <filesystem> 18 #include <string> 19 20 namespace phosphor 21 { 22 namespace state 23 { 24 namespace manager 25 { 26 27 using HostInherit = sdbusplus::server::object_t< 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_t& bus, const char* objPath, size_t id) : 57 HostInherit(bus, objPath, HostInherit::action::defer_emit), 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 [this](sdbusplus::message_t& m) { sysStateChangeJobRemoved(m); }), 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 [this](sdbusplus::message_t& m) { sysStateChangeJobNew(m); }), 70 settings(bus, id), id(id) 71 { 72 // Enable systemd signals 73 utils::subscribeToSystemdSignals(bus); 74 75 // create map of target name base on host id 76 createSystemdTargetMaps(); 77 78 // Will throw exception on fail 79 determineInitialState(); 80 81 // Sets auto-reboot attempts to max-allowed 82 attemptsLeft(sdbusplus::xyz::openbmc_project::Control::Boot::server:: 83 RebootAttempts::retryAttempts()); 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 value for allowable auto-reboot count 103 * 104 * This override is responsible for ensuring that when external users 105 * set the number of automatic retry attempts that the number of 106 * automatic reboot attempts left will update accordingly. 107 * 108 * @param[in] value - desired Reboot count value 109 * 110 * @return number of reboot attempts allowed. 111 */ 112 uint32_t retryAttempts(uint32_t value) override 113 { 114 if (sdbusplus::xyz::openbmc_project::Control::Boot::server:: 115 RebootAttempts::attemptsLeft() != value) 116 { 117 info("Automatic reboot retry attempts set to: {VALUE} ", "VALUE", 118 value); 119 sdbusplus::xyz::openbmc_project::Control::Boot::server:: 120 RebootAttempts::attemptsLeft(value); 121 } 122 123 return (sdbusplus::xyz::openbmc_project::Control::Boot::server:: 124 RebootAttempts::retryAttempts(value)); 125 } 126 127 /** 128 * @brief Set host reboot count to default 129 * 130 * OpenBMC software controls the number of allowed reboot attempts so 131 * any external set request of this property will be overridden by 132 * this function and set to the number of the allowed auto-reboot 133 * retry attempts found on the system. 134 * 135 * The only code responsible for decrementing the boot count resides 136 * within this process and that will use the sub class interface 137 * directly 138 * 139 * @param[in] value - Reboot count value 140 * 141 * @return number of reboot attempts left(allowed by retry attempts 142 * property) 143 */ 144 uint32_t attemptsLeft(uint32_t value) override 145 { 146 debug("External request to reset reboot count"); 147 auto retryAttempts = sdbusplus::xyz::openbmc_project::Control::Boot:: 148 server::RebootAttempts::retryAttempts(); 149 return ( 150 sdbusplus::xyz::openbmc_project::Control::Boot::server:: 151 RebootAttempts::attemptsLeft(std::min(value, retryAttempts))); 152 } 153 154 private: 155 /** 156 * @brief Determine initial host state and set internally 157 * 158 * @return Will throw exceptions on failure 159 **/ 160 void determineInitialState(); 161 162 /** 163 * create systemd target instance names and mapping table 164 **/ 165 void createSystemdTargetMaps(); 166 167 /** @brief Execute the transition request 168 * 169 * This function assumes the state has been validated and the host 170 * is in an appropriate state for the transition to be started. 171 * 172 * @param[in] tranReq - Transition requested 173 */ 174 void executeTransition(Transition tranReq); 175 176 /** 177 * @brief Determine if target is active 178 * 179 * This function determines if the target is active and 180 * helps prevent misleading log recorded states. 181 * 182 * @param[in] target - Target string to check on 183 * 184 * @return boolean corresponding to state active 185 **/ 186 bool stateActive(const std::string& target); 187 188 /** 189 * @brief Determine if auto reboot flag is set 190 * 191 * @return boolean corresponding to current auto_reboot setting 192 **/ 193 bool isAutoReboot(); 194 195 /** @brief Check if systemd state change is relevant to this object 196 * 197 * Instance specific interface to handle the detected systemd state 198 * change 199 * 200 * @param[in] msg - Data associated with subscribed signal 201 * 202 */ 203 void sysStateChangeJobRemoved(sdbusplus::message_t& msg); 204 205 /** @brief Check if JobNew systemd signal is relevant to this object 206 * 207 * In certain instances phosphor-state-manager needs to monitor for the 208 * entry into a systemd target. This function will be used for these cases. 209 * 210 * Instance specific interface to handle the detected systemd state 211 * change 212 * 213 * @param[in] msg - Data associated with subscribed signal 214 * 215 */ 216 void sysStateChangeJobNew(sdbusplus::message_t& msg); 217 218 /** @brief Decrement reboot count 219 * 220 * This is used internally to this application to decrement the boot 221 * count on each boot attempt. The host will use the external 222 * attemptsLeft() interface to reset the count when a boot is successful 223 * 224 * @return number of reboot count attempts left 225 */ 226 uint32_t decrementRebootCount(); 227 228 // Allow cereal class access to allow these next two function to be 229 // private 230 friend class cereal::access; 231 232 /** @brief Function required by Cereal to perform serialization. 233 * 234 * @tparam Archive - Cereal archive type (binary in our case). 235 * @param[in] archive - reference to Cereal archive. 236 * @param[in] version - Class version that enables handling 237 * a serialized data across code levels 238 */ 239 template <class Archive> 240 void save(Archive& archive, const std::uint32_t version) const 241 { 242 // version is not used currently 243 (void)(version); 244 archive(sdbusplus::xyz::openbmc_project::Control::Boot::server:: 245 RebootAttempts::retryAttempts(), 246 convertForMessage(sdbusplus::xyz::openbmc_project::State:: 247 server::Host::requestedHostTransition()), 248 convertForMessage(sdbusplus::xyz::openbmc_project::State::Boot:: 249 server::Progress::bootProgress()), 250 convertForMessage( 251 sdbusplus::xyz::openbmc_project::State::OperatingSystem:: 252 server::Status::operatingSystemState())); 253 } 254 255 /** @brief Function required by Cereal to perform deserialization. 256 * 257 * @tparam Archive - Cereal archive type (binary in our case). 258 * @param[in] archive - reference to Cereal archive. 259 * @param[in] version - Class version that enables handling 260 * a serialized data across code levels 261 */ 262 template <class Archive> 263 void load(Archive& archive, const std::uint32_t version) 264 { 265 std::string reqTranState; 266 std::string bootProgress; 267 std::string osState; 268 // Older cereal archive without RetryAttempt may be implemented 269 // just set to (BOOT_COUNT_MAX_ALLOWED) 270 uint32_t retryAttempts = BOOT_COUNT_MAX_ALLOWED; 271 switch (version) 272 { 273 case 2: 274 archive(retryAttempts); 275 [[fallthrough]]; 276 case 1: 277 archive(reqTranState, bootProgress, osState); 278 break; 279 } 280 auto reqTran = Host::convertTransitionFromString(reqTranState); 281 // When restoring, set the requested state with persistent value 282 // but don't call the override which would execute it 283 sdbusplus::xyz::openbmc_project::State::server::Host:: 284 requestedHostTransition(reqTran); 285 sdbusplus::xyz::openbmc_project::State::Boot::server::Progress:: 286 bootProgress(Host::convertProgressStagesFromString(bootProgress)); 287 sdbusplus::xyz::openbmc_project::State::OperatingSystem::server:: 288 Status::operatingSystemState( 289 Host::convertOSStatusFromString(osState)); 290 sdbusplus::xyz::openbmc_project::Control::Boot::server::RebootAttempts:: 291 retryAttempts(retryAttempts); 292 } 293 294 /** @brief Serialize and persist requested host state 295 * 296 * @return fs::path - pathname of persisted requested host state. 297 */ 298 fs::path serialize(); 299 300 /** @brief Deserialze a persisted requested host state. 301 * 302 * @return bool - true if the deserialization was successful, false 303 * otherwise. 304 */ 305 bool deserialize(); 306 307 /** 308 * @brief Get target name of a HostState 309 * 310 * @param[in] state - The state of the host 311 * 312 * @return string - systemd target name of the state 313 */ 314 const std::string& getTarget(HostState state); 315 316 /** 317 * @brief Get target name of a TransitionRequest 318 * 319 * @param[in] tranReq - Transition requested 320 * 321 * @return string - systemd target name of Requested transition 322 */ 323 const std::string& getTarget(Transition tranReq); 324 325 /** @brief Persistent sdbusplus DBus bus connection. */ 326 sdbusplus::bus_t& bus; 327 328 /** @brief Used to subscribe to dbus systemd JobRemoved signal **/ 329 sdbusplus::bus::match_t systemdSignalJobRemoved; 330 331 /** @brief Used to subscribe to dbus systemd JobNew signal **/ 332 sdbusplus::bus::match_t systemdSignalJobNew; 333 334 // Settings host objects of interest 335 settings::HostObjects settings; 336 337 /** @brief Host id. **/ 338 const size_t id = 0; 339 340 /** @brief HostState to systemd target mapping table. **/ 341 std::map<HostState, std::string> stateTargetTable; 342 343 /** @brief Requested Transition to systemd target mapping table. **/ 344 std::map<Transition, std::string> transitionTargetTable; 345 346 /** @brief Target called when a host crash occurs **/ 347 std::string hostCrashTarget; 348 }; 349 350 } // namespace manager 351 } // namespace state 352 } // namespace phosphor 353