1 #include "sotauptaneclient.h"
8 #include "campaign/campaign.h"
9 #include "crypto/crypto.h"
10 #include "crypto/keymanager.h"
11 #include "initializer.h"
12 #include "logging/logging.h"
13 #include "uptane/exceptions.h"
15 #include "utilities/fault_injection.h"
16 #include "utilities/utils.h"
18 static void report_progress_cb(event::Channel *channel,
const Uptane::Target &target,
const std::string &description,
19 unsigned int progress) {
20 if (channel ==
nullptr) {
23 auto event = std::make_shared<event::DownloadProgressReport>(target, description, progress);
27 void SotaUptaneClient::addNewSecondary(
const std::shared_ptr<Uptane::SecondaryInterface> &sec) {
28 if (storage->loadEcuRegistered()) {
30 storage->loadEcuSerials(&serials);
32 if (std::find_if(serials.cbegin(), serials.cend(), secondary_comp) == serials.cend()) {
33 throw std::logic_error(
"Add new secondaries for provisioned device is not implemented yet");
39 void SotaUptaneClient::addSecondary(
const std::shared_ptr<Uptane::SecondaryInterface> &sec) {
42 std::map<Uptane::EcuSerial, std::shared_ptr<Uptane::SecondaryInterface>>::const_iterator map_it =
43 secondaries.find(sec_serial);
44 if (map_it != secondaries.end()) {
45 throw std::runtime_error(std::string(
"Multiple secondaries found with the same serial: ") + sec_serial.ToString());
47 secondaries.insert(std::make_pair(sec_serial, sec));
48 hw_ids.insert(std::make_pair(sec_serial, sec_hw_id));
51 bool SotaUptaneClient::isInstalledOnPrimary(
const Uptane::Target &target) {
52 if (target.ecus().find(primaryEcuSerial()) != target.ecus().end()) {
53 return target.MatchTarget(package_manager_->getCurrent());
58 std::vector<Uptane::Target> SotaUptaneClient::findForEcu(
const std::vector<Uptane::Target> &targets,
60 std::vector<Uptane::Target>
result;
61 for (
auto it = targets.begin(); it != targets.end(); ++it) {
62 if (it->ecus().find(ecu_id) != it->ecus().end()) {
70 LOG_INFO <<
"Installing package using " << package_manager_->name() <<
" package manager";
72 return package_manager_->install(target);
73 }
catch (std::exception &ex) {
78 void SotaUptaneClient::finalizeAfterReboot() {
79 if (!package_manager_->rebootDetected()) {
84 LOG_INFO <<
"Device has been rebooted after an update";
86 std::vector<Uptane::Target> updates;
87 unsigned int ecus_count = 0;
88 if (uptaneOfflineIteration(&updates, &ecus_count)) {
91 std::vector<Uptane::Target> installed_versions;
92 boost::optional<Uptane::Target> pending_target;
93 storage->loadInstalledVersions(primary_ecu_serial.ToString(),
nullptr, &pending_target);
95 if (!!pending_target && pending_target->IsForEcu(primary_ecu_serial)) {
96 const std::string correlation_id = pending_target->correlation_id();
99 storage->saveEcuInstallationResult(primary_ecu_serial, install_res);
100 if (install_res.success) {
101 storage->saveInstalledVersion(primary_ecu_serial.ToString(), *pending_target,
102 InstalledVersionUpdateMode::kCurrent);
103 report_queue->enqueue(
104 std_::make_unique<EcuInstallationCompletedReport>(primary_ecu_serial, correlation_id,
true));
109 storage->saveInstalledVersion(primary_ecu_serial.ToString(), *pending_target,
110 InstalledVersionUpdateMode::kNone);
111 report_queue->enqueue(
112 std_::make_unique<EcuInstallationCompletedReport>(primary_ecu_serial, correlation_id,
false));
113 director_repo.dropTargets(*storage);
116 computeDeviceInstallationResult(
nullptr, correlation_id);
120 LOG_ERROR <<
"No any pending update for Primary is found";
123 LOG_ERROR <<
"Invalid Uptane metadata in storage.";
126 package_manager_->rebootFlagClear();
141 storage->saveInstalledVersion(ecu_serial.ToString(), target, InstalledVersionUpdateMode::kNone);
143 result = PackageInstall(target);
144 if (
result.result_code.num_code == data::ResultCode::Numeric::kOk) {
146 storage->saveInstalledVersion(ecu_serial.ToString(), target, InstalledVersionUpdateMode::kCurrent);
147 }
else if (
result.result_code.num_code == data::ResultCode::Numeric::kNeedCompletion) {
149 storage->saveInstalledVersion(ecu_serial.ToString(), target, InstalledVersionUpdateMode::kPending);
151 storage->saveEcuInstallationResult(ecu_serial,
result);
155 void SotaUptaneClient::reportHwInfo() {
156 Json::Value hw_info = Utils::getHardwareInfo();
157 if (!hw_info.empty()) {
158 if (hw_info != last_hw_info_reported) {
159 if (http->put(config.tls.server +
"/system_info", hw_info).isOk()) {
160 last_hw_info_reported = hw_info;
164 LOG_WARNING <<
"Unable to fetch hardware information from host system.";
168 void SotaUptaneClient::reportInstalledPackages() {
169 http->put(config.tls.server +
"/core/installed", package_manager_->getInstalledPackages());
172 void SotaUptaneClient::reportNetworkInfo() {
174 LOG_DEBUG <<
"Reporting network information";
175 Json::Value network_info = Utils::getNetworkInfo();
176 if (network_info != last_network_info_reported) {
177 HttpResponse response = http->put(config.tls.server +
"/system_info/network", network_info);
178 if (response.isOk()) {
179 last_network_info_reported = network_info;
184 LOG_DEBUG <<
"Not reporting network information because telemetry is disabled";
188 void SotaUptaneClient::reportAktualizrConfiguration() {
189 if (!config.telemetry.report_config) {
190 LOG_DEBUG <<
"Not reporting network information because telemetry is disabled";
194 LOG_DEBUG <<
"Reporting libaktualizr configuration";
195 std::stringstream conf_ss;
196 config.writeToStream(conf_ss);
197 http->post(config.tls.server +
"/system_info/config",
"application/toml", conf_ss.str());
200 Json::Value SotaUptaneClient::AssembleManifest() {
201 Json::Value manifest;
203 manifest[
"primary_ecu_serial"] = primary_ecu_serial.ToString();
206 Json::Value version_manifest;
208 Json::Value primary_manifest = uptane_manifest->assembleManifest(package_manager_->getCurrent());
209 std::vector<std::pair<Uptane::EcuSerial, int64_t>> ecu_cnt;
210 std::string report_counter;
211 if (!storage->loadEcuReportCounter(&ecu_cnt) || (ecu_cnt.size() == 0)) {
212 LOG_ERROR <<
"No ECU version report counter, please check the database!";
215 report_counter = std::to_string(ecu_cnt[0].second + 1);
216 storage->saveEcuReportCounter(ecu_cnt[0].first, ecu_cnt[0].second + 1);
218 version_manifest[primary_ecu_serial.ToString()] = uptane_manifest->sign(primary_manifest, report_counter);
220 for (
auto it = secondaries.begin(); it != secondaries.end(); it++) {
223 if (secmanifest.verifySignature(it->second->getPublicKey())) {
224 version_manifest[it->first.ToString()] = secmanifest;
227 LOG_ERROR <<
"Secondary manifest is corrupted or not signed, or signature is invalid manifest: " << secmanifest;
230 manifest[
"ecu_version_manifests"] = version_manifest;
233 Json::Value installation_report;
236 std::string raw_report;
237 std::string correlation_id;
238 bool has_results = storage->loadDeviceInstallationResult(&dev_result, &raw_report, &correlation_id);
240 installation_report[
"result"] = dev_result.toJson();
241 installation_report[
"raw_report"] = raw_report;
242 installation_report[
"correlation_id"] = correlation_id;
243 installation_report[
"items"] = Json::arrayValue;
245 std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_results;
246 storage->loadEcuInstallationResults(&ecu_results);
247 for (
const auto &r : ecu_results) {
252 item[
"ecu"] = serial.ToString();
253 item[
"result"] = res.toJson();
255 installation_report[
"items"].append(item);
258 manifest[
"installation_report"][
"content_type"] =
"application/vnd.com.here.otac.installationReport.v1";
259 manifest[
"installation_report"][
"report"] = installation_report;
261 LOG_DEBUG <<
"No installation result to report in manifest";
267 bool SotaUptaneClient::hasPendingUpdates()
const {
return storage->hasPendingInstall(); }
269 void SotaUptaneClient::initialize() {
270 LOG_DEBUG <<
"Checking if device is provisioned...";
271 auto keys = std::make_shared<KeyManager>(storage, config.keymanagerConfig());
272 Initializer initializer(config.provision, storage, http, *keys, secondaries);
274 if (!initializer.isSuccessful()) {
275 throw std::runtime_error(
"Fatal error during provisioning or ECU device registration.");
279 if (!storage->loadEcuSerials(&serials) || serials.size() == 0) {
280 throw std::runtime_error(
"Unable to load ECU serials after device registration.");
283 uptane_manifest = std::make_shared<Uptane::ManifestIssuer>(keys, serials[0].first);
284 primary_ecu_serial_ = serials[0].first;
285 hw_ids.insert(serials[0]);
288 LOG_DEBUG <<
"... provisioned OK";
290 finalizeAfterReboot();
293 bool SotaUptaneClient::updateDirectorMeta() {
294 if (!director_repo.updateMeta(*storage, *uptane_fetcher)) {
295 last_exception = director_repo.getLastException();
301 bool SotaUptaneClient::updateImagesMeta() {
302 if (!images_repo.updateMeta(*storage, *uptane_fetcher)) {
303 last_exception = images_repo.getLastException();
309 bool SotaUptaneClient::checkDirectorMetaOffline() {
310 if (!director_repo.checkMetaOffline(*storage)) {
311 last_exception = director_repo.getLastException();
317 bool SotaUptaneClient::checkImagesMetaOffline() {
318 if (!images_repo.checkMetaOffline(*storage)) {
319 last_exception = images_repo.getLastException();
326 const std::string &correlation_id) {
329 std::string raw_installation_report =
"Installation succesful";
332 std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_results;
334 if (!storage->loadEcuInstallationResults(&ecu_results)) {
337 "Unable to get installation results from ecus");
338 raw_installation_report =
"Failed to load ECUs' installation result";
343 std::string result_code_err_str;
345 for (
const auto &r : ecu_results) {
346 auto ecu_serial = r.first;
347 auto installation_res = r.second;
349 if (hw_ids.find(ecu_serial) == hw_ids.end()) {
352 "Unable to get installation results from ecus");
354 raw_installation_report =
"Couldn't find any ECU with the given serial: " + ecu_serial.ToString();
359 if (installation_res.needCompletion()) {
361 device_installation_result =
363 "ECU needs completion/finalization to be installed: " + ecu_serial.ToString());
364 raw_installation_report =
"ECU needs completion/finalization to be installed: " + ecu_serial.ToString();
371 if (!installation_res.isSuccess()) {
372 std::string ecu_code_str = hw_ids.at(ecu_serial).ToString() +
":" + installation_res.result_code.toString();
373 result_code_err_str += (result_code_err_str !=
"" ?
"|" :
"") + ecu_code_str;
377 if (!result_code_err_str.empty()) {
379 device_installation_result =
381 "Installation failed on at least one of ECUs");
382 raw_installation_report =
"Installation failed on at least one of ECUs";
390 *
result = device_installation_result;
394 storage->storeDeviceInstallationResult(device_installation_result, raw_installation_report, correlation_id);
397 bool SotaUptaneClient::getNewTargets(std::vector<Uptane::Target> *new_targets,
unsigned int *ecus_count) {
398 std::vector<Uptane::Target> targets = director_repo.getTargets().targets;
400 if (ecus_count !=
nullptr) {
405 for (
const auto &ecu : target.ecus()) {
409 auto hwid_it = hw_ids.find(ecu_serial);
410 if (hwid_it == hw_ids.end()) {
411 LOG_ERROR <<
"Unknown ECU ID in director targets metadata: " << ecu_serial.ToString();
416 if (hwid_it->second != hw_id) {
417 LOG_ERROR <<
"Wrong hardware identifier for ECU " << ecu_serial.ToString();
422 boost::optional<Uptane::Target> current_version;
423 if (!storage->loadInstalledVersions(ecu_serial.ToString(), ¤t_version,
nullptr)) {
424 LOG_WARNING <<
"Could not load currently installed version for ECU ID: " << ecu_serial.ToString();
428 if (!current_version) {
429 LOG_WARNING <<
"Current version for ECU ID: " << ecu_serial.ToString() <<
" is unknown";
431 }
else if (current_version->filename() != target.filename()) {
435 if (primary_ecu_serial == ecu_serial) {
437 (config.pacman.type == PackageManager::kOstree || config.pacman.type == PackageManager::kOstreeDockerApp)) {
438 LOG_ERROR <<
"Cannot install a non-OSTree package on an OSTree system";
444 if (is_new && ecus_count !=
nullptr) {
450 new_targets->push_back(target);
456 std::unique_ptr<Uptane::Target> SotaUptaneClient::findTargetHelper(
const Uptane::Targets &cur_targets,
458 const int level,
const bool terminating,
459 const bool offline) {
461 const auto it = std::find_if(cur_targets.targets.cbegin(), cur_targets.targets.cend(), target_comp);
462 if (it != cur_targets.targets.cend()) {
463 return std_::make_unique<Uptane::Target>(*it);
466 if (terminating || level >= Uptane::kDelegationsMaxDepth) {
467 return std::unique_ptr<Uptane::Target>(
nullptr);
470 for (
const auto &delegate_name : cur_targets.delegated_role_names_) {
471 Uptane::Role delegate_role = Uptane::Role::Delegation(delegate_name);
472 auto patterns = cur_targets.paths_for_role_.find(delegate_role);
473 if (patterns == cur_targets.paths_for_role_.end()) {
478 for (
const auto &pattern : patterns->second) {
479 if (fnmatch(pattern.c_str(), queried_target.filename().c_str(), 0) == 0) {
491 Uptane::getTrustedDelegation(delegate_role, cur_targets, images_repo, *storage, *uptane_fetcher, offline);
492 if (delegation.isExpired(TimeStamp::Now())) {
496 auto is_terminating = cur_targets.terminating_role_.find(delegate_role);
497 if (is_terminating == cur_targets.terminating_role_.end()) {
501 auto found_target = findTargetHelper(delegation, queried_target, level + 1, is_terminating->second, offline);
502 if (found_target !=
nullptr) {
507 return std::unique_ptr<Uptane::Target>(
nullptr);
510 std::unique_ptr<Uptane::Target> SotaUptaneClient::findTargetInDelegationTree(
const Uptane::Target &target,
511 const bool offline) {
512 auto toplevel_targets = images_repo.getTargets();
513 if (toplevel_targets ==
nullptr) {
514 return std::unique_ptr<Uptane::Target>(
nullptr);
517 return findTargetHelper(*toplevel_targets, target, 0,
false, offline);
520 result::Download SotaUptaneClient::downloadImages(
const std::vector<Uptane::Target> &targets,
524 std::lock_guard<std::mutex> guard(download_mutex);
526 std::vector<Uptane::Target> downloaded_targets;
529 if (update_status != result::UpdateStatus::kUpdatesAvailable) {
530 if (update_status == result::UpdateStatus::kNoUpdatesAvailable) {
534 result::Download(downloaded_targets, result::DownloadStatus::kError,
"Error rechecking stored metadata.");
535 storeInstallationFailure(
538 sendEvent<event::AllDownloadsComplete>(
result);
542 for (
const auto &target : targets) {
543 auto res = downloadImage(target, token);
545 downloaded_targets.push_back(res.second);
549 if (targets.size() == downloaded_targets.size()) {
552 if (downloaded_targets.size() == 0) {
553 LOG_ERROR <<
"None of " << targets.size() <<
" targets were successfully downloaded.";
554 result =
result::Download(downloaded_targets, result::DownloadStatus::kError,
"Each target download has failed");
556 LOG_ERROR <<
"Only " << downloaded_targets.size() <<
" of " << targets.size() <<
" were successfully downloaded.";
559 storeInstallationFailure(
563 sendEvent<event::AllDownloadsComplete>(
result);
567 void SotaUptaneClient::reportPause() {
568 const std::string &correlation_id = director_repo.getCorrelationId();
569 report_queue->enqueue(std_::make_unique<DevicePausedReport>(correlation_id));
572 void SotaUptaneClient::reportResume() {
573 const std::string &correlation_id = director_repo.getCorrelationId();
574 report_queue->enqueue(std_::make_unique<DeviceResumedReport>(correlation_id));
577 std::pair<bool, Uptane::Target> SotaUptaneClient::downloadImage(
const Uptane::Target &target,
581 const std::string &correlation_id = director_repo.getCorrelationId();
583 for (
const auto &ecu : target.ecus()) {
584 report_queue->enqueue(std_::make_unique<EcuDownloadStartedReport>(ecu.first, correlation_id));
587 KeyManager keys(storage, config.keymanagerConfig());
589 auto prog_cb = [
this](
const Uptane::Target &t,
const std::string description,
unsigned int progress) {
590 report_progress_cb(events_channel.get(), t, description, progress);
593 bool success =
false;
596 if (target.IsForEcu(primary_ecu_serial) || !target.
IsOstree()) {
598 const int max_tries = 3;
600 std::chrono::milliseconds wait(500);
602 for (; tries < max_tries; tries++) {
603 success = package_manager_->fetchTarget(target, *uptane_fetcher, keys, prog_cb, token);
606 }
else if (tries < max_tries - 1) {
607 std::this_thread::sleep_for(wait);
612 LOG_ERROR <<
"Download unsuccessful after " << tries <<
" attempts.";
621 for (
const auto &ecu : target.ecus()) {
622 report_queue->enqueue(std_::make_unique<EcuDownloadCompletedReport>(ecu.first, correlation_id, success));
625 sendEvent<event::DownloadTargetComplete>(target, success);
626 return {success, target};
629 bool SotaUptaneClient::uptaneIteration(std::vector<Uptane::Target> *targets,
unsigned int *ecus_count) {
630 if (!updateDirectorMeta()) {
631 LOG_ERROR <<
"Failed to update director metadata: " << last_exception.what();
634 std::vector<Uptane::Target> tmp_targets;
636 if (!getNewTargets(&tmp_targets, &ecus)) {
637 LOG_ERROR <<
"Inconsistency between director metadata and existent ECUs";
641 if (tmp_targets.empty()) {
642 if (targets !=
nullptr) {
643 *targets = std::move(tmp_targets);
645 if (ecus_count !=
nullptr) {
651 LOG_INFO <<
"got new updates";
653 if (!updateImagesMeta()) {
654 LOG_ERROR <<
"Failed to update images metadata: " << last_exception.what();
658 if (targets !=
nullptr) {
659 *targets = std::move(tmp_targets);
661 if (ecus_count !=
nullptr) {
667 bool SotaUptaneClient::uptaneOfflineIteration(std::vector<Uptane::Target> *targets,
unsigned int *ecus_count) {
668 if (!checkDirectorMetaOffline()) {
669 LOG_ERROR <<
"Failed to check director metadata: " << last_exception.what();
672 std::vector<Uptane::Target> tmp_targets;
674 if (!getNewTargets(&tmp_targets, &ecus)) {
675 LOG_ERROR <<
"Inconsistency between director metadata and existent ECUs";
679 if (tmp_targets.empty()) {
680 *targets = std::move(tmp_targets);
681 if (ecus_count !=
nullptr) {
687 if (!checkImagesMetaOffline()) {
688 LOG_ERROR <<
"Failed to check images metadata: " << last_exception.what();
692 *targets = std::move(tmp_targets);
693 if (ecus_count !=
nullptr) {
699 void SotaUptaneClient::sendDeviceData() {
701 reportInstalledPackages();
703 reportAktualizrConfiguration();
705 sendEvent<event::SendDeviceDataComplete>();
713 if (hasPendingUpdates()) {
715 LOG_INFO <<
"The current update is pending. Check if pending ECUs has been already updated";
716 checkAndUpdatePendingSecondaries();
719 if (hasPendingUpdates()) {
722 LOG_INFO <<
"An update is pending. Skipping check for update until installation is complete.";
724 "There are pending updates, no new updates are checked");
728 if (!putManifestSimple()) {
729 LOG_ERROR <<
"Error sending manifest!";
732 sendEvent<event::UpdateCheckComplete>(
result);
740 std::vector<Uptane::Target> updates;
741 unsigned int ecus_count = 0;
742 if (!uptaneIteration(&updates, &ecus_count)) {
747 std::string director_targets;
748 storage->loadNonRoot(&director_targets, Uptane::RepositoryType::Director(), Uptane::Role::Targets());
750 if (updates.empty()) {
751 LOG_DEBUG <<
"No new updates found in Uptane metadata.";
753 result::UpdateCheck({}, 0, result::UpdateStatus::kNoUpdatesAvailable, Utils::parseJSON(director_targets),
"");
761 for (
auto &target : updates) {
762 auto images_target = findTargetInDelegationTree(target,
false);
763 if (images_target ==
nullptr) {
766 LOG_ERROR <<
"No matching target in images targets metadata for " << target;
769 storeInstallationFailure(
775 if (target.uri().empty() && !images_target->uri().empty()) {
776 target.setUri(images_target->uri());
781 Utils::parseJSON(director_targets),
"");
782 if (updates.size() == 1) {
783 LOG_INFO <<
"1 new update found in Uptane metadata.";
785 LOG_INFO << updates.size() <<
" new updates found in Uptane metadata.";
790 result::UpdateStatus SotaUptaneClient::checkUpdatesOffline(
const std::vector<Uptane::Target> &targets) {
791 if (hasPendingUpdates()) {
793 LOG_INFO <<
"An update is pending. Skipping stored metadata check until installation is complete.";
794 return result::UpdateStatus::kError;
797 if (targets.empty()) {
798 LOG_WARNING <<
"Requested targets vector is empty. Nothing to do.";
799 return result::UpdateStatus::kError;
802 std::vector<Uptane::Target> director_targets;
803 unsigned int ecus_count = 0;
804 if (!uptaneOfflineIteration(&director_targets, &ecus_count)) {
805 LOG_ERROR <<
"Invalid Uptane metadata in storage.";
806 return result::UpdateStatus::kError;
809 if (director_targets.empty()) {
810 LOG_ERROR <<
"No new updates found in Uptane metadata, but expected " << targets.size() <<
".";
811 return result::UpdateStatus::kNoUpdatesAvailable;
817 for (
const auto &target : targets) {
819 const auto it = std::find_if(director_targets.cbegin(), director_targets.cend(), target_comp);
820 if (it == director_targets.cend()) {
821 LOG_ERROR <<
"No matching target in director targets metadata for " << target;
822 return result::UpdateStatus::kError;
825 const auto images_target = findTargetInDelegationTree(target,
true);
826 if (images_target ==
nullptr) {
827 LOG_ERROR <<
"No matching target in images targets metadata for " << target;
828 return result::UpdateStatus::kError;
832 return result::UpdateStatus::kUpdatesAvailable;
835 result::Install SotaUptaneClient::uptaneInstall(
const std::vector<Uptane::Target> &updates) {
837 const std::string &correlation_id = director_repo.getCorrelationId();
840 storage->clearInstallationResults();
845 if (update_status != result::UpdateStatus::kUpdatesAvailable) {
846 if (update_status == result::UpdateStatus::kNoUpdatesAvailable) {
851 storage->storeDeviceInstallationResult(
result.dev_report,
"Stored Uptane metadata is invalid", correlation_id);
852 sendEvent<event::AllInstallsComplete>(
result);
858 for (
const auto &update : updates) {
859 if (update.IsForEcu(primary_ecu_serial) || !update.IsOstree()) {
865 if (package_manager_->verifyTarget(update) != TargetStatus::kGood) {
867 storage->storeDeviceInstallationResult(
result.dev_report,
"Downloaded target is invalid", correlation_id);
868 sendEvent<event::AllInstallsComplete>(
result);
875 std::vector<Uptane::Target> primary_updates = findForEcu(updates, primary_ecu_serial);
877 sendMetadataToEcus(updates);
880 if (primary_updates.size() != 0u) {
883 primary_update.setCorrelationId(correlation_id);
885 report_queue->enqueue(std_::make_unique<EcuInstallationStartedReport>(primary_ecu_serial, correlation_id));
886 sendEvent<event::InstallStarted>(primary_ecu_serial);
889 if (!isInstalledOnPrimary(primary_update)) {
892 package_manager_->updateNotify();
893 install_res = PackageInstallSetResult(primary_update);
894 if (install_res.result_code.num_code == data::ResultCode::Numeric::kNeedCompletion) {
896 report_queue->enqueue(std_::make_unique<EcuInstallationAppliedReport>(primary_ecu_serial, correlation_id));
897 sendEvent<event::InstallTargetComplete>(primary_ecu_serial,
true);
898 }
else if (install_res.result_code.num_code == data::ResultCode::Numeric::kOk) {
899 storage->saveEcuInstallationResult(primary_ecu_serial, install_res);
900 report_queue->enqueue(
901 std_::make_unique<EcuInstallationCompletedReport>(primary_ecu_serial, correlation_id,
true));
902 sendEvent<event::InstallTargetComplete>(primary_ecu_serial,
true);
905 storage->saveEcuInstallationResult(primary_ecu_serial, install_res);
906 report_queue->enqueue(
907 std_::make_unique<EcuInstallationCompletedReport>(primary_ecu_serial, correlation_id,
false));
908 sendEvent<event::InstallTargetComplete>(primary_ecu_serial,
false);
912 storage->saveEcuInstallationResult(primary_ecu_serial, install_res);
915 report_queue->enqueue(
916 std_::make_unique<EcuInstallationCompletedReport>(primaryEcuSerial(), correlation_id,
false));
917 sendEvent<event::InstallTargetComplete>(primaryEcuSerial(),
false);
919 result.ecu_reports.emplace(
result.ecu_reports.begin(), primary_update, primaryEcuSerial(), install_res);
922 LOG_INFO <<
"No update to install on primary";
925 auto sec_reports = sendImagesToEcus(updates);
926 result.ecu_reports.insert(
result.ecu_reports.end(), sec_reports.begin(), sec_reports.end());
927 computeDeviceInstallationResult(&
result.dev_report, correlation_id);
928 sendEvent<event::AllInstallsComplete>(
result);
930 if (!(
result.dev_report.isSuccess() ||
result.dev_report.needCompletion())) {
931 director_repo.dropTargets(*storage);
938 auto campaigns = campaign::Campaign::fetchAvailableCampaigns(*http, config.tls.server);
939 for (
const auto &c : campaigns) {
940 LOG_INFO <<
"Campaign: " << c.name;
941 LOG_INFO <<
"Campaign id: " << c.id;
942 LOG_INFO <<
"Campaign size: " << c.size;
943 LOG_INFO <<
"CampaignAccept required: " << (c.autoAccept ?
"no" :
"yes");
944 LOG_INFO <<
"Message: " << c.description;
947 sendEvent<event::CampaignCheckComplete>(
result);
951 void SotaUptaneClient::campaignAccept(
const std::string &campaign_id) {
952 sendEvent<event::CampaignAcceptComplete>();
953 report_queue->enqueue(std_::make_unique<CampaignAcceptedReport>(campaign_id));
956 void SotaUptaneClient::campaignDecline(
const std::string &campaign_id) {
957 sendEvent<event::CampaignDeclineComplete>();
958 report_queue->enqueue(std_::make_unique<CampaignDeclinedReport>(campaign_id));
961 void SotaUptaneClient::campaignPostpone(
const std::string &campaign_id) {
962 sendEvent<event::CampaignPostponeComplete>();
963 report_queue->enqueue(std_::make_unique<CampaignPostponedReport>(campaign_id));
966 bool SotaUptaneClient::isInstallCompletionRequired()
const {
967 bool force_install_completion = (hasPendingUpdates() && config.uptane.force_install_completion);
968 return force_install_completion;
971 void SotaUptaneClient::completeInstall()
const {
972 if (isInstallCompletionRequired()) {
973 package_manager_->completeInstall();
977 bool SotaUptaneClient::putManifestSimple(
const Json::Value &custom) {
979 if (hasPendingUpdates()) {
982 LOG_DEBUG <<
"An update is pending. Skipping manifest upload until installation is complete.";
986 static bool connected =
true;
987 auto manifest = AssembleManifest();
988 if (custom != Json::nullValue) {
989 manifest[
"custom"] = custom;
991 auto signed_manifest = uptane_manifest->sign(manifest);
992 HttpResponse response = http->put(config.uptane.director_server +
"/manifest", signed_manifest);
993 if (response.isOk()) {
995 LOG_INFO <<
"Connectivity is restored.";
998 storage->clearInstallationResults();
1004 LOG_WARNING <<
"Put manifest request failed: " << response.getStatusStr();
1008 bool SotaUptaneClient::putManifest(
const Json::Value &custom) {
1009 bool success = putManifestSimple(custom);
1010 sendEvent<event::PutManifestComplete>(success);
1015 void SotaUptaneClient::verifySecondaries() {
1016 storage->clearMisconfiguredEcus();
1018 if (!storage->loadEcuSerials(&serials) || serials.empty()) {
1019 LOG_ERROR <<
"No ECU serials found in storage!";
1023 std::vector<MisconfiguredEcu> misconfigured_ecus;
1024 std::vector<bool> found(serials.size(),
false);
1026 EcuSerials::const_iterator store_it;
1027 store_it = std::find_if(serials.cbegin(), serials.cend(), primary_comp);
1028 if (store_it == serials.cend()) {
1029 LOG_ERROR <<
"Primary ECU serial " << primaryEcuSerial() <<
" not found in storage!";
1032 found[static_cast<size_t>(std::distance(serials.cbegin(), store_it))] =
true;
1035 std::map<Uptane::EcuSerial, std::shared_ptr<Uptane::SecondaryInterface>>::const_iterator it;
1036 for (it = secondaries.cbegin(); it != secondaries.cend(); ++it) {
1038 store_it = std::find_if(serials.cbegin(), serials.cend(), secondary_comp);
1039 if (store_it == serials.cend()) {
1040 LOG_ERROR <<
"Secondary ECU serial " << it->second->getSerial() <<
" (hardware ID " << it->second->getHwId()
1041 <<
") not found in storage!";
1042 misconfigured_ecus.emplace_back(it->second->getSerial(), it->second->getHwId(), EcuState::kNotRegistered);
1043 }
else if (found[static_cast<size_t>(std::distance(serials.cbegin(), store_it))]) {
1044 LOG_ERROR <<
"Secondary ECU serial " << it->second->getSerial() <<
" (hardware ID " << it->second->getHwId()
1045 <<
") has a duplicate entry in storage!";
1047 found[static_cast<size_t>(std::distance(serials.cbegin(), store_it))] =
true;
1051 std::vector<bool>::iterator found_it;
1052 for (found_it = found.begin(); found_it != found.end(); ++found_it) {
1054 auto not_registered = serials[static_cast<size_t>(std::distance(found.begin(), found_it))];
1055 LOG_WARNING <<
"ECU serial " << not_registered.first <<
" in storage was not reported to aktualizr!";
1056 misconfigured_ecus.emplace_back(not_registered.first, not_registered.second, EcuState::kOld);
1060 storage->storeMisconfiguredEcus(misconfigured_ecus);
1066 const std::string &correlation_id = director_repo.getCorrelationId();
1067 storage->storeDeviceInstallationResult(
result,
"", correlation_id);
1069 director_repo.dropTargets(*storage);
1073 std::string latest_root;
1075 if (!storage->loadLatestRoot(&latest_root, repo)) {
1076 LOG_ERROR <<
"No root metadata to send";
1080 int last_root_version = Uptane::extractVersionUntrusted(latest_root);
1082 int sec_root_version = secondary.getRootVersion((repo == Uptane::RepositoryType::Director()));
1083 if (sec_root_version >= 0) {
1084 for (
int v = sec_root_version + 1; v <= last_root_version; v++) {
1087 LOG_WARNING <<
"Couldn't find root meta in the storage, trying remote repo";
1088 if (!uptane_fetcher->fetchRole(&root, Uptane::kMaxRootSize, repo, Uptane::Role::Root(),
Uptane::Version(v))) {
1090 LOG_ERROR <<
"Root metadata could not be fetched, skipping to the next secondary";
1094 if (!secondary.putRoot(root, repo == Uptane::RepositoryType::Director())) {
1095 LOG_ERROR <<
"Sending metadata to " << secondary.getSerial() <<
" failed";
1104 void SotaUptaneClient::sendMetadataToEcus(
const std::vector<Uptane::Target> &targets) {
1106 if (!storage->loadLatestRoot(&meta.director_root, Uptane::RepositoryType::Director())) {
1107 LOG_ERROR <<
"No director root metadata to send";
1110 if (!storage->loadNonRoot(&meta.director_targets, Uptane::RepositoryType::Director(), Uptane::Role::Targets())) {
1111 LOG_ERROR <<
"No director targets metadata to send";
1114 if (!storage->loadLatestRoot(&meta.image_root, Uptane::RepositoryType::Image())) {
1115 LOG_ERROR <<
"No images root metadata to send";
1118 if (!storage->loadNonRoot(&meta.image_timestamp, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp())) {
1119 LOG_ERROR <<
"No images timestamp metadata to send";
1122 if (!storage->loadNonRoot(&meta.image_snapshot, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot())) {
1123 LOG_ERROR <<
"No images snapshot metadata to send";
1126 if (!storage->loadNonRoot(&meta.image_targets, Uptane::RepositoryType::Image(), Uptane::Role::Targets())) {
1127 LOG_ERROR <<
"No images targets metadata to send";
1132 for (
auto targets_it = targets.cbegin(); targets_it != targets.cend(); ++targets_it) {
1133 for (
auto ecus_it = targets_it->ecus().cbegin(); ecus_it != targets_it->ecus().cend(); ++ecus_it) {
1136 auto sec = secondaries.find(ecu_serial);
1137 if (sec != secondaries.end()) {
1139 rotateSecondaryRoot(Uptane::RepositoryType::Director(), *(sec->second));
1140 rotateSecondaryRoot(Uptane::RepositoryType::Image(), *(sec->second));
1141 if (!sec->second->putMetadata(meta)) {
1142 LOG_ERROR <<
"Sending metadata to " << sec->second->getSerial() <<
" failed";
1151 auto f = [
this, &secondary, target]() {
1152 const std::string &correlation_id = director_repo.getCorrelationId();
1154 sendEvent<event::InstallStarted>(secondary.getSerial());
1155 report_queue->enqueue(std_::make_unique<EcuInstallationStartedReport>(secondary.getSerial(), correlation_id));
1157 std::string data_to_send;
1158 bool send_firmware_result =
false;
1162 data_to_send = secondaryTreehubCredentials();
1164 std::stringstream sstr;
1165 sstr << *storage->openTargetFile(target);
1166 data_to_send = sstr.str();
1169 if (!data_to_send.empty()) {
1170 send_firmware_result = secondary.sendFirmware(data_to_send);
1176 if (send_firmware_result) {
1177 result = secondary.install(target.filename());
1180 if (
result == data::ResultCode::Numeric::kNeedCompletion) {
1181 report_queue->enqueue(std_::make_unique<EcuInstallationAppliedReport>(secondary.getSerial(), correlation_id));
1183 report_queue->enqueue(std_::make_unique<EcuInstallationCompletedReport>(
1184 secondary.getSerial(), correlation_id,
result == data::ResultCode::Numeric::kOk));
1187 sendEvent<event::InstallTargetComplete>(secondary.getSerial(),
result == data::ResultCode::Numeric::kOk);
1191 return std::async(std::launch::async, f);
1194 std::vector<result::Install::EcuReport> SotaUptaneClient::sendImagesToEcus(
const std::vector<Uptane::Target> &targets) {
1195 std::vector<result::Install::EcuReport> reports;
1196 std::vector<std::pair<result::Install::EcuReport, std::future<data::ResultCode::Numeric>>> firmwareFutures;
1200 for (
auto targets_it = targets.cbegin(); targets_it != targets.cend(); ++targets_it) {
1201 for (
auto ecus_it = targets_it->ecus().cbegin(); ecus_it != targets_it->ecus().cend(); ++ecus_it) {
1204 if (primary_ecu_serial == ecu_serial) {
1208 auto f = secondaries.find(ecu_serial);
1209 if (f == secondaries.end()) {
1210 LOG_ERROR <<
"Target " << *targets_it <<
" has unknown ECU ID";
1217 sendFirmwareAsync(sec, *targets_it));
1221 for (
auto &f : firmwareFutures) {
1224 fut_result = f.second.get();
1227 if (fiu_fail((std::string(
"secondary_install_") + f.first.serial.ToString()).c_str()) != 0) {
1240 if (fut_result == data::ResultCode::Numeric::kOk || fut_result == data::ResultCode::Numeric::kNeedCompletion) {
1241 f.first.update.setCorrelationId(director_repo.getCorrelationId());
1242 auto update_mode = fut_result == data::ResultCode::Numeric::kOk ? InstalledVersionUpdateMode::kCurrent
1243 : InstalledVersionUpdateMode::kPending;
1244 storage->saveInstalledVersion(f.first.serial.ToString(), f.first.update, update_mode);
1247 storage->saveEcuInstallationResult(f.first.serial, f.first.install_res);
1248 reports.push_back(f.first);
1253 std::string SotaUptaneClient::secondaryTreehubCredentials()
const {
1254 if (config.tls.pkey_source != CryptoSource::kFile || config.tls.cert_source != CryptoSource::kFile ||
1255 config.tls.ca_source != CryptoSource::kFile) {
1256 LOG_ERROR <<
"Cannot send OSTree update to a secondary when not using file as credential sources";
1259 std::string ca, cert, pkey;
1260 if (!storage->loadTlsCreds(&ca, &cert, &pkey)) {
1261 LOG_ERROR <<
"Could not load tls credentials from storage";
1265 std::string treehub_url = config.pacman.ostree_server;
1266 std::map<std::string, std::string> archive_map = {
1267 {
"ca.pem", ca}, {
"client.pem", cert}, {
"pkey.pem", pkey}, {
"server.url", treehub_url}};
1270 std::stringstream as;
1271 Utils::writeArchive(archive_map, as);
1274 }
catch (std::runtime_error &exc) {
1275 LOG_ERROR <<
"Could not create credentials archive: " << exc.what();
1284 void SotaUptaneClient::checkAndUpdatePendingSecondaries() {
1286 std::vector<std::pair<Uptane::EcuSerial, Uptane::Hash>> pending_ecus;
1287 storage->getPendingEcus(&pending_ecus);
1289 for (
const auto &pending_ecu : pending_ecus) {
1290 if (primaryEcuSerial() == pending_ecu.first) {
1293 auto &sec = secondaries[pending_ecu.first];
1294 const auto &manifest = sec->getManifest();
1295 if (!manifest.verifySignature(sec->getPublicKey())) {
1296 LOG_ERROR <<
"Invalid signature of the manifest reported by secondary: "
1297 <<
" serial: " << pending_ecu.first <<
" manifest: " << manifest;
1301 auto current_ecu_hash = manifest.installedImageHash();
1302 if (pending_ecu.second == current_ecu_hash) {
1303 LOG_INFO <<
"The pending update " << current_ecu_hash <<
" has been installed on " << pending_ecu.first;
1304 boost::optional<Uptane::Target> pending_version;
1305 if (storage->loadInstalledVersions(pending_ecu.first.ToString(),
nullptr, &pending_version)) {
1306 storage->saveEcuInstallationResult(pending_ecu.first,
1309 storage->saveInstalledVersion(pending_ecu.first.ToString(), *pending_version,
1310 InstalledVersionUpdateMode::kCurrent);
1312 report_queue->enqueue(std_::make_unique<EcuInstallationCompletedReport>(
1313 pending_ecu.first, pending_version->correlation_id(),
true));
1314 computeDeviceInstallationResult(
nullptr, pending_version->correlation_id());