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