155 lines
5.3 KiB
C++
155 lines
5.3 KiB
C++
#include "simulation.hpp"
|
|
|
|
#include <stdexcept>
|
|
|
|
namespace ota::installation {
|
|
|
|
const std::string &expected_contents() {
|
|
static const std::string contents = "release-2:kernel+device-tree+rootfs";
|
|
return contents;
|
|
}
|
|
|
|
const char *name(RecoveryAction action) {
|
|
switch (action) {
|
|
case RecoveryAction::RestartPreparation:
|
|
return "restart preparation; keep A";
|
|
case RecoveryAction::ReadyToActivate:
|
|
return "B verified again; wait for activation policy";
|
|
case RecoveryAction::PreserveTrial:
|
|
return "preserve saved B trial and attempts";
|
|
case RecoveryAction::SeparateRecovery:
|
|
return "stop; separate recovery required";
|
|
}
|
|
throw std::logic_error("Unknown recovery action");
|
|
}
|
|
|
|
const char *boot_choice(BootRecord record) {
|
|
switch (record) {
|
|
case BootRecord::ASelected:
|
|
return "A";
|
|
case BootRecord::BTrial:
|
|
return "B trial (attempt handling is outside this model)";
|
|
case BootRecord::Invalid:
|
|
return "separate recovery";
|
|
}
|
|
throw std::logic_error("Unknown boot record");
|
|
}
|
|
|
|
void Installer::require_writable() const {
|
|
if (storage_.boot != BootRecord::ASelected) {
|
|
throw std::logic_error("B is selected or boot state is invalid");
|
|
}
|
|
}
|
|
|
|
void Installer::begin() {
|
|
require_writable();
|
|
verified_ = false;
|
|
storage_.release = "release-2";
|
|
storage_.progress = Progress::Writing;
|
|
}
|
|
|
|
void Installer::write_partial() {
|
|
require_writable();
|
|
if (storage_.progress != Progress::Writing || storage_.release != "release-2") {
|
|
throw std::logic_error("Save matching intent before writing");
|
|
}
|
|
verified_ = false;
|
|
storage_.b_contents = expected_contents().substr(0, 8);
|
|
}
|
|
|
|
void Installer::finish_write() {
|
|
require_writable();
|
|
if (storage_.progress != Progress::Writing || storage_.release != "release-2") {
|
|
throw std::logic_error("Save matching intent before writing");
|
|
}
|
|
verified_ = false;
|
|
storage_.b_contents = expected_contents();
|
|
}
|
|
|
|
void Installer::verify() {
|
|
require_writable();
|
|
verified_ = false;
|
|
if (storage_.release != "release-2" || storage_.b_contents != expected_contents()) {
|
|
throw std::runtime_error("Installed release does not match expected contents");
|
|
}
|
|
verified_ = true;
|
|
}
|
|
|
|
void Installer::save_prepared() {
|
|
require_writable();
|
|
if (!verified_) throw std::logic_error("Verify B before saving prepared state");
|
|
storage_.progress = Progress::Prepared;
|
|
}
|
|
|
|
void Installer::request_trial(WriteResult result) {
|
|
require_writable();
|
|
if (!verified_ || storage_.progress != Progress::Prepared) {
|
|
throw std::logic_error("Verify B and save prepared state before activation");
|
|
}
|
|
// Supplied outcomes stand for the storage contract of boot control.
|
|
// OldRecord leaves all fields intact; NewRecord saves one complete record.
|
|
if (result == WriteResult::OldRecord) {
|
|
throw std::runtime_error("Interrupted request; old record remains");
|
|
}
|
|
if (result == WriteResult::InvalidRecord) {
|
|
storage_.boot = BootRecord::Invalid;
|
|
throw std::runtime_error("Interrupted request; no valid record remains");
|
|
}
|
|
storage_.boot = BootRecord::BTrial;
|
|
storage_.attempts_left = 2;
|
|
}
|
|
|
|
void Installer::save_requested() {
|
|
if (storage_.boot != BootRecord::BTrial) {
|
|
throw std::logic_error("No saved B trial request");
|
|
}
|
|
storage_.progress = Progress::TrialRequested;
|
|
}
|
|
|
|
RecoveryAction Installer::recover() {
|
|
verified_ = false;
|
|
// Boot state takes priority over progress. Never write B or renew attempts
|
|
// when a trial is already selected. Invalid boot state is not confirmation.
|
|
if (storage_.boot == BootRecord::Invalid) return RecoveryAction::SeparateRecovery;
|
|
if (storage_.boot == BootRecord::BTrial) {
|
|
save_requested();
|
|
return RecoveryAction::PreserveTrial;
|
|
}
|
|
if (storage_.release != "release-2" || storage_.b_contents != expected_contents()) {
|
|
return RecoveryAction::RestartPreparation;
|
|
}
|
|
verify();
|
|
save_prepared();
|
|
return RecoveryAction::ReadyToActivate;
|
|
}
|
|
|
|
void perform_step(Installer &installer, int step) {
|
|
switch (step) {
|
|
case 0:
|
|
installer.begin();
|
|
break;
|
|
case 1:
|
|
installer.write_partial();
|
|
break;
|
|
case 2:
|
|
installer.finish_write();
|
|
break;
|
|
case 3:
|
|
installer.verify();
|
|
break;
|
|
case 4:
|
|
installer.save_prepared();
|
|
break;
|
|
case 5:
|
|
installer.request_trial();
|
|
break;
|
|
case 6:
|
|
installer.save_requested();
|
|
break;
|
|
default:
|
|
throw std::logic_error("Unknown installation step");
|
|
}
|
|
}
|
|
|
|
}// namespace ota::installation
|