diff --git a/config/sql/migration/migrate.22.sql b/config/sql/migration/migrate.22.sql new file mode 100644 index 0000000000..2df3fb67d7 --- /dev/null +++ b/config/sql/migration/migrate.22.sql @@ -0,0 +1,9 @@ +-- Don't modify this! Create a new migration instead--see docs/schema-migrations.adoc +SAVEPOINT MIGRATION; + +CREATE TABLE ecu_report_counter(ecu_serial TEXT NOT NULL PRIMARY KEY, counter INTEGER NOT NULL DEFAULT 0); + +DELETE FROM version; +INSERT INTO version VALUES(22); + +RELEASE MIGRATION; diff --git a/config/sql/rollback/rollback.22.sql b/config/sql/rollback/rollback.22.sql new file mode 100644 index 0000000000..7021ccdedb --- /dev/null +++ b/config/sql/rollback/rollback.22.sql @@ -0,0 +1,9 @@ +-- Don't modify this! Create a new migration instead--see docs/schema-migrations.adoc +SAVEPOINT ROLLBACK_MIGRATION; + +DROP TABLE ecu_report_counter; + +DELETE FROM version; +INSERT INTO version VALUES(21); + +RELEASE ROLLBACK_MIGRATION; diff --git a/config/sql/schema.sql b/config/sql/schema.sql index 94e025ba8e..a01c75545e 100644 --- a/config/sql/schema.sql +++ b/config/sql/schema.sql @@ -1,5 +1,5 @@ CREATE TABLE version(version INTEGER); -INSERT INTO version(rowid,version) VALUES(1,21); +INSERT INTO version(rowid,version) VALUES(1,22); CREATE TABLE device_info(unique_mark INTEGER PRIMARY KEY CHECK (unique_mark = 0), device_id TEXT, is_registered INTEGER NOT NULL DEFAULT 0 CHECK (is_registered IN (0,1))); CREATE TABLE ecu_serials(id INTEGER PRIMARY KEY, serial TEXT UNIQUE, hardware_id TEXT NOT NULL, is_primary INTEGER NOT NULL DEFAULT 0 CHECK (is_primary IN (0,1))); CREATE TABLE misconfigured_ecus(serial TEXT UNIQUE, hardware_id TEXT NOT NULL, state INTEGER NOT NULL CHECK (state IN (0,1))); @@ -23,3 +23,4 @@ CREATE TABLE ecu_installation_results(ecu_serial TEXT NOT NULL PRIMARY KEY, succ CREATE TABLE need_reboot(unique_mark INTEGER PRIMARY KEY CHECK (unique_mark = 0), flag INTEGER NOT NULL DEFAULT 0); CREATE TABLE rollback_migrations(version_from INT PRIMARY KEY, migration TEXT NOT NULL); CREATE TABLE delegations(meta BLOB NOT NULL, role_name TEXT NOT NULL, UNIQUE(role_name)); +CREATE TABLE ecu_report_counter(ecu_serial TEXT NOT NULL PRIMARY KEY, counter INTEGER NOT NULL DEFAULT 0); diff --git a/src/libaktualizr/primary/initializer.cc b/src/libaktualizr/primary/initializer.cc index 79c72bbd62..eac47c1c21 100644 --- a/src/libaktualizr/primary/initializer.cc +++ b/src/libaktualizr/primary/initializer.cc @@ -201,11 +201,28 @@ InitRetCode Initializer::initEcuRegister() { return InitRetCode::kOk; } +bool Initializer::initEcuReportCounter() { + std::vector> ecu_cnt; + + if (storage_->loadEcuReportCounter(&ecu_cnt)) { + return true; + } + + EcuSerials ecu_serials; + + if (!storage_->loadEcuSerials(&ecu_serials) || (ecu_serials.size() == 0)) { + return false; + } + + storage_->saveEcuReportCounter(Uptane::EcuSerial(ecu_serials[0].first.ToString()), 0); + + return true; +} // Postcondition: "ECUs registered" flag set in the storage Initializer::Initializer( const ProvisionConfig& config_in, std::shared_ptr storage_in, std::shared_ptr http_client_in, KeyManager& keys_in, - const std::map >& secondary_info_in) + const std::map>& secondary_info_in) : config_(config_in), storage_(std::move(storage_in)), http_client_(std::move(http_client_in)), @@ -241,6 +258,10 @@ Initializer::Initializer( LOG_ERROR << "ECU serial generation failed. Aborting initialization."; return; } + if (!initEcuReportCounter()) { + LOG_ERROR << "ECU report counter init failed. Aborting initialization."; + return; + } ret_code = initEcuRegister(); // if ECUs with same ID have been registered to the server, we don't have a diff --git a/src/libaktualizr/primary/initializer.h b/src/libaktualizr/primary/initializer.h index ae5d1f3df9..3805e50dc3 100644 --- a/src/libaktualizr/primary/initializer.h +++ b/src/libaktualizr/primary/initializer.h @@ -36,6 +36,7 @@ class Initializer { void resetTlsCreds(); InitRetCode initEcuRegister(); bool loadSetTlsCreds(); // TODO -> metadownloader + bool initEcuReportCounter(); }; #endif // INITIALIZER_H_ diff --git a/src/libaktualizr/primary/sotauptaneclient.cc b/src/libaktualizr/primary/sotauptaneclient.cc index 1a8ce801c6..b9d3664ac5 100644 --- a/src/libaktualizr/primary/sotauptaneclient.cc +++ b/src/libaktualizr/primary/sotauptaneclient.cc @@ -190,6 +190,13 @@ Json::Value SotaUptaneClient::AssembleManifest() { Json::Value version_manifest; Json::Value primary_ecu_version = package_manager_->getManifest(primary_ecu_serial); + std::vector> ecu_cnt; + if (!storage->loadEcuReportCounter(&ecu_cnt) || (ecu_cnt.size() == 0)) { + LOG_ERROR << "No ECU version report counter, please check the database!"; + } else { + primary_ecu_version["report_counter"] = std::to_string(ecu_cnt[0].second + 1); + storage->saveEcuReportCounter(ecu_cnt[0].first, ecu_cnt[0].second + 1); + } version_manifest[primary_ecu_serial.ToString()] = uptane_manifest.signManifest(primary_ecu_version); for (auto it = secondaries.begin(); it != secondaries.end(); it++) { diff --git a/src/libaktualizr/storage/invstorage.h b/src/libaktualizr/storage/invstorage.h index 9eee761fb3..80c4d4ec36 100644 --- a/src/libaktualizr/storage/invstorage.h +++ b/src/libaktualizr/storage/invstorage.h @@ -181,6 +181,9 @@ class INvStorage { std::string* correlation_id) = 0; virtual void clearInstallationResults() = 0; + virtual void saveEcuReportCounter(const Uptane::EcuSerial& ecu_serial, int64_t counter) = 0; + virtual bool loadEcuReportCounter(std::vector>* results) = 0; + virtual boost::optional> checkTargetFile(const Uptane::Target& target) const = 0; // Incremental file API diff --git a/src/libaktualizr/storage/sqlstorage.cc b/src/libaktualizr/storage/sqlstorage.cc index b391948e02..968acea42a 100644 --- a/src/libaktualizr/storage/sqlstorage.cc +++ b/src/libaktualizr/storage/sqlstorage.cc @@ -1323,6 +1323,57 @@ bool SQLStorage::loadDeviceInstallationResult(data::InstallationResult* result, return true; } +void SQLStorage::saveEcuReportCounter(const Uptane::EcuSerial& ecu_serial, const int64_t counter) { + SQLite3Guard db = dbConnection(); + + auto statement = db.prepareStatement( + "INSERT OR REPLACE INTO ecu_report_counter (ecu_serial, counter) VALUES " + "(?,?);", + ecu_serial.ToString(), counter); + if (statement.step() != SQLITE_DONE) { + LOG_ERROR << "Can't set ecu counter: " << db.errmsg(); + return; + } +} + +bool SQLStorage::loadEcuReportCounter(std::vector>* results) { + SQLite3Guard db = dbConnection(); + + std::vector> ecu_cnt; + + // keep the same order as in ecu_serials (start with primary) + auto statement = db.prepareStatement( + "SELECT ecu_serial, counter FROM ecu_report_counter INNER JOIN ecu_serials ON " + "ecu_serials.serial = ecu_serial ORDER BY ecu_serials.id;"); + int statement_result = statement.step(); + if (statement_result != SQLITE_DONE && statement_result != SQLITE_ROW) { + LOG_ERROR << "Can't get ecu_report_counter: " << db.errmsg(); + return false; + } + + if (statement_result == SQLITE_DONE) { + // if there are no any record in the DB + return false; + } + + for (; statement_result != SQLITE_DONE; statement_result = statement.step()) { + try { + std::string ecu_serial = statement.get_result_col_str(0).value(); + int64_t counter = statement.get_result_col_int(1); + + ecu_cnt.emplace_back(Uptane::EcuSerial(ecu_serial), counter); + } catch (const boost::bad_optional_access&) { + return false; + } + } + + if (results != nullptr) { + *results = std::move(ecu_cnt); + } + + return true; +} + void SQLStorage::clearInstallationResults() { SQLite3Guard db = dbConnection(); if (!db.beginTransaction()) { diff --git a/src/libaktualizr/storage/sqlstorage.h b/src/libaktualizr/storage/sqlstorage.h index 2b7bfe9082..27b2d8bc00 100644 --- a/src/libaktualizr/storage/sqlstorage.h +++ b/src/libaktualizr/storage/sqlstorage.h @@ -80,6 +80,8 @@ class SQLStorage : public SQLStorageBase, public INvStorage { const std::string& correlation_id) override; bool loadDeviceInstallationResult(data::InstallationResult* result, std::string* raw_report, std::string* correlation_id) override; + void saveEcuReportCounter(const Uptane::EcuSerial& ecu_serial, int64_t counter) override; + bool loadEcuReportCounter(std::vector>* results) override; void clearInstallationResults() override; std::unique_ptr allocateTargetFile(bool from_director, const Uptane::Target& target) override; diff --git a/src/libaktualizr/uptane/uptane_test.cc b/src/libaktualizr/uptane/uptane_test.cc index 33d7f9ed73..9f9ddff4a0 100644 --- a/src/libaktualizr/uptane/uptane_test.cc +++ b/src/libaktualizr/uptane/uptane_test.cc @@ -155,6 +155,13 @@ TEST(Uptane, AssembleManifestGood) { // Manifest should not have an installation result yet. EXPECT_FALSE(manifest["testecuserial"]["signed"].isMember("custom")); EXPECT_FALSE(manifest["secondary_ecu_serial"]["signed"].isMember("custom")); + + std::string counter_str = manifest["testecuserial"]["signed"]["report_counter"].asString(); + int64_t primary_ecu_report_counter = std::stoll(counter_str); + Json::Value manifest2 = sota_client->AssembleManifest()["ecu_version_manifests"]; + std::string counter_str2 = manifest2["testecuserial"]["signed"]["report_counter"].asString(); + int64_t primary_ecu_report_counter2 = std::stoll(counter_str2); + EXPECT_EQ(primary_ecu_report_counter2, primary_ecu_report_counter + 1); } /* Bad signatures are ignored when assembling the manifest. */