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