// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "update_engine/update_attempter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "update_engine/common/boot_control_interface.h" #include "update_engine/common/certificate_checker.h" #include "update_engine/common/clock_interface.h" #include "update_engine/common/constants.h" #include "update_engine/common/hardware_interface.h" #include "update_engine/common/libcurl_http_fetcher.h" #include "update_engine/common/multi_range_http_fetcher.h" #include "update_engine/common/platform_constants.h" #include "update_engine/common/prefs_interface.h" #include "update_engine/common/subprocess.h" #include "update_engine/common/utils.h" #include "update_engine/dbus_service.h" #include "update_engine/metrics.h" #include "update_engine/omaha_request_action.h" #include "update_engine/omaha_request_params.h" #include "update_engine/omaha_response_handler_action.h" #include "update_engine/p2p_manager.h" #include "update_engine/payload_consumer/download_action.h" #include "update_engine/payload_consumer/filesystem_verifier_action.h" #include "update_engine/payload_consumer/postinstall_runner_action.h" #include "update_engine/payload_state_interface.h" #include "update_engine/system_state.h" #include "update_engine/update_manager/policy.h" #include "update_engine/update_manager/update_manager.h" #include "update_engine/update_status_utils.h" using base::Bind; using base::Callback; using base::Time; using base::TimeDelta; using base::TimeTicks; using brillo::MessageLoop; using chromeos_update_manager::EvalStatus; using chromeos_update_manager::Policy; using chromeos_update_manager::UpdateCheckParams; using std::set; using std::shared_ptr; using std::string; using std::vector; namespace chromeos_update_engine { const int UpdateAttempter::kMaxDeltaUpdateFailures = 3; namespace { const int kMaxConsecutiveObeyProxyRequests = 20; // Minimum threshold to broadcast an status update in progress and time. const double kBroadcastThresholdProgress = 0.01; // 1% const int kBroadcastThresholdSeconds = 10; // By default autest bypasses scattering. If we want to test scattering, // use kScheduledAUTestURLRequest. The URL used is same in both cases, but // different params are passed to CheckForUpdate(). const char kAUTestURLRequest[] = "autest"; const char kScheduledAUTestURLRequest[] = "autest-scheduled"; } // namespace // Turns a generic ErrorCode::kError to a generic error code specific // to |action| (e.g., ErrorCode::kFilesystemVerifierError). If |code| is // not ErrorCode::kError, or the action is not matched, returns |code| // unchanged. ErrorCode GetErrorCodeForAction(AbstractAction* action, ErrorCode code) { if (code != ErrorCode::kError) return code; const string type = action->Type(); if (type == OmahaRequestAction::StaticType()) return ErrorCode::kOmahaRequestError; if (type == OmahaResponseHandlerAction::StaticType()) return ErrorCode::kOmahaResponseHandlerError; if (type == FilesystemVerifierAction::StaticType()) return ErrorCode::kFilesystemVerifierError; if (type == PostinstallRunnerAction::StaticType()) return ErrorCode::kPostinstallRunnerError; return code; } UpdateAttempter::UpdateAttempter( SystemState* system_state, CertificateChecker* cert_checker, LibCrosProxy* libcros_proxy, org::chromium::debugdProxyInterface* debugd_proxy) : processor_(new ActionProcessor()), system_state_(system_state), cert_checker_(cert_checker), #if USE_LIBCROS chrome_proxy_resolver_(libcros_proxy), #endif // USE_LIBCROS debugd_proxy_(debugd_proxy) { } UpdateAttempter::~UpdateAttempter() { // CertificateChecker might not be initialized in unittests. if (cert_checker_) cert_checker_->SetObserver(nullptr); // Release ourselves as the ActionProcessor's delegate to prevent // re-scheduling the updates due to the processing stopped. processor_->set_delegate(nullptr); } void UpdateAttempter::Init() { // Pulling from the SystemState can only be done after construction, since // this is an aggregate of various objects (such as the UpdateAttempter), // which requires them all to be constructed prior to it being used. prefs_ = system_state_->prefs(); omaha_request_params_ = system_state_->request_params(); if (cert_checker_) cert_checker_->SetObserver(this); // In case of update_engine restart without a reboot we need to restore the // reboot needed state. if (GetBootTimeAtUpdate(nullptr)) status_ = UpdateStatus::UPDATED_NEED_REBOOT; else status_ = UpdateStatus::IDLE; #if USE_LIBCROS chrome_proxy_resolver_.Init(); #endif // USE_LIBCROS } void UpdateAttempter::ScheduleUpdates() { if (IsUpdateRunningOrScheduled()) return; chromeos_update_manager::UpdateManager* const update_manager = system_state_->update_manager(); CHECK(update_manager); Callback callback = Bind( &UpdateAttempter::OnUpdateScheduled, base::Unretained(this)); // We limit the async policy request to a reasonably short time, to avoid a // starvation due to a transient bug. update_manager->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed); waiting_for_scheduled_check_ = true; } void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check, CertificateCheckResult result) { metrics::ReportCertificateCheckMetrics(system_state_, server_to_check, result); } bool UpdateAttempter::CheckAndReportDailyMetrics() { int64_t stored_value; Time now = system_state_->clock()->GetWallclockTime(); if (system_state_->prefs()->Exists(kPrefsDailyMetricsLastReportedAt) && system_state_->prefs()->GetInt64(kPrefsDailyMetricsLastReportedAt, &stored_value)) { Time last_reported_at = Time::FromInternalValue(stored_value); TimeDelta time_reported_since = now - last_reported_at; if (time_reported_since.InSeconds() < 0) { LOG(WARNING) << "Last reported daily metrics " << utils::FormatTimeDelta(time_reported_since) << " ago " << "which is negative. Either the system clock is wrong or " << "the kPrefsDailyMetricsLastReportedAt state variable " << "is wrong."; // In this case, report daily metrics to reset. } else { if (time_reported_since.InSeconds() < 24*60*60) { LOG(INFO) << "Last reported daily metrics " << utils::FormatTimeDelta(time_reported_since) << " ago."; return false; } LOG(INFO) << "Last reported daily metrics " << utils::FormatTimeDelta(time_reported_since) << " ago, " << "which is more than 24 hours ago."; } } LOG(INFO) << "Reporting daily metrics."; system_state_->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt, now.ToInternalValue()); ReportOSAge(); return true; } void UpdateAttempter::ReportOSAge() { struct stat sb; if (system_state_ == nullptr) return; if (stat("/etc/lsb-release", &sb) != 0) { PLOG(ERROR) << "Error getting file status for /etc/lsb-release " << "(Note: this may happen in some unit tests)"; return; } Time lsb_release_timestamp = utils::TimeFromStructTimespec(&sb.st_ctim); Time now = system_state_->clock()->GetWallclockTime(); TimeDelta age = now - lsb_release_timestamp; if (age.InSeconds() < 0) { LOG(ERROR) << "The OS age (" << utils::FormatTimeDelta(age) << ") is negative. Maybe the clock is wrong? " << "(Note: this may happen in some unit tests.)"; return; } metrics::ReportDailyMetrics(system_state_, age); } void UpdateAttempter::Update(const string& app_version, const string& omaha_url, const string& target_channel, const string& target_version_prefix, bool obey_proxies, bool interactive) { // This is normally called frequently enough so it's appropriate to use as a // hook for reporting daily metrics. // TODO(garnold) This should be hooked to a separate (reliable and consistent) // timeout event. CheckAndReportDailyMetrics(); // Notify of the new update attempt, clearing prior interactive requests. if (forced_update_pending_callback_.get()) forced_update_pending_callback_->Run(false, false); fake_update_success_ = false; if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) { // Although we have applied an update, we still want to ping Omaha // to ensure the number of active statistics is accurate. // // Also convey to the UpdateEngine.Check.Result metric that we're // not performing an update check because of this. LOG(INFO) << "Not updating b/c we already updated and we're waiting for " << "reboot, we'll ping Omaha instead"; metrics::ReportUpdateCheckMetrics(system_state_, metrics::CheckResult::kRebootPending, metrics::CheckReaction::kUnset, metrics::DownloadErrorCode::kUnset); PingOmaha(); return; } if (status_ != UpdateStatus::IDLE) { // Update in progress. Do nothing return; } if (!CalculateUpdateParams(app_version, omaha_url, target_channel, target_version_prefix, obey_proxies, interactive)) { return; } BuildUpdateActions(interactive); SetStatusAndNotify(UpdateStatus::CHECKING_FOR_UPDATE); // Update the last check time here; it may be re-updated when an Omaha // response is received, but this will prevent us from repeatedly scheduling // checks in the case where a response is not received. UpdateLastCheckedTime(); // Just in case we didn't update boot flags yet, make sure they're updated // before any update processing starts. start_action_processor_ = true; UpdateBootFlags(); } void UpdateAttempter::RefreshDevicePolicy() { // Lazy initialize the policy provider, or reload the latest policy data. if (!policy_provider_.get()) policy_provider_.reset(new policy::PolicyProvider()); policy_provider_->Reload(); const policy::DevicePolicy* device_policy = nullptr; if (policy_provider_->device_policy_is_loaded()) device_policy = &policy_provider_->GetDevicePolicy(); if (device_policy) LOG(INFO) << "Device policies/settings present"; else LOG(INFO) << "No device policies/settings present."; system_state_->set_device_policy(device_policy); system_state_->p2p_manager()->SetDevicePolicy(device_policy); } void UpdateAttempter::CalculateP2PParams(bool interactive) { bool use_p2p_for_downloading = false; bool use_p2p_for_sharing = false; // Never use p2p for downloading in interactive checks unless the // developer has opted in for it via a marker file. // // (Why would a developer want to opt in? If he's working on the // update_engine or p2p codebases so he can actually test his // code.). if (system_state_ != nullptr) { if (!system_state_->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "p2p is not enabled - disallowing p2p for both" << " downloading and sharing."; } else { // Allow p2p for sharing, even in interactive checks. use_p2p_for_sharing = true; if (!interactive) { LOG(INFO) << "Non-interactive check - allowing p2p for downloading"; use_p2p_for_downloading = true; } else { LOG(INFO) << "Forcibly disabling use of p2p for downloading " << "since this update attempt is interactive."; } } } PayloadStateInterface* const payload_state = system_state_->payload_state(); payload_state->SetUsingP2PForDownloading(use_p2p_for_downloading); payload_state->SetUsingP2PForSharing(use_p2p_for_sharing); } bool UpdateAttempter::CalculateUpdateParams(const string& app_version, const string& omaha_url, const string& target_channel, const string& target_version_prefix, bool obey_proxies, bool interactive) { http_response_code_ = 0; PayloadStateInterface* const payload_state = system_state_->payload_state(); // Refresh the policy before computing all the update parameters. RefreshDevicePolicy(); // Set the target version prefix, if provided. if (!target_version_prefix.empty()) omaha_request_params_->set_target_version_prefix(target_version_prefix); CalculateScatteringParams(interactive); CalculateP2PParams(interactive); if (payload_state->GetUsingP2PForDownloading() || payload_state->GetUsingP2PForSharing()) { // OK, p2p is to be used - start it and perform housekeeping. if (!StartP2PAndPerformHousekeeping()) { // If this fails, disable p2p for this attempt LOG(INFO) << "Forcibly disabling use of p2p since starting p2p or " << "performing housekeeping failed."; payload_state->SetUsingP2PForDownloading(false); payload_state->SetUsingP2PForSharing(false); } } if (!omaha_request_params_->Init(app_version, omaha_url, interactive)) { LOG(ERROR) << "Unable to initialize Omaha request params."; return false; } // Set the target channel, if one was provided. if (target_channel.empty()) { LOG(INFO) << "No target channel mandated by policy."; } else { LOG(INFO) << "Setting target channel as mandated: " << target_channel; // Pass in false for powerwash_allowed until we add it to the policy // protobuf. string error_message; if (!omaha_request_params_->SetTargetChannel(target_channel, false, &error_message)) { LOG(ERROR) << "Setting the channel failed: " << error_message; } // Notify observers the target channel change. BroadcastChannel(); // Since this is the beginning of a new attempt, update the download // channel. The download channel won't be updated until the next attempt, // even if target channel changes meanwhile, so that how we'll know if we // should cancel the current download attempt if there's such a change in // target channel. omaha_request_params_->UpdateDownloadChannel(); } LOG(INFO) << "target_version_prefix = " << omaha_request_params_->target_version_prefix() << ", scatter_factor_in_seconds = " << utils::FormatSecs(scatter_factor_.InSeconds()); LOG(INFO) << "Wall Clock Based Wait Enabled = " << omaha_request_params_->wall_clock_based_wait_enabled() << ", Update Check Count Wait Enabled = " << omaha_request_params_->update_check_count_wait_enabled() << ", Waiting Period = " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); LOG(INFO) << "Use p2p For Downloading = " << payload_state->GetUsingP2PForDownloading() << ", Use p2p For Sharing = " << payload_state->GetUsingP2PForSharing(); obeying_proxies_ = true; if (obey_proxies || proxy_manual_checks_ == 0) { LOG(INFO) << "forced to obey proxies"; // If forced to obey proxies, every 20th request will not use proxies proxy_manual_checks_++; LOG(INFO) << "proxy manual checks: " << proxy_manual_checks_; if (proxy_manual_checks_ >= kMaxConsecutiveObeyProxyRequests) { proxy_manual_checks_ = 0; obeying_proxies_ = false; } } else if (base::RandInt(0, 4) == 0) { obeying_proxies_ = false; } LOG_IF(INFO, !obeying_proxies_) << "To help ensure updates work, this update " "check we are ignoring the proxy settings and using " "direct connections."; DisableDeltaUpdateIfNeeded(); return true; } void UpdateAttempter::CalculateScatteringParams(bool interactive) { // Take a copy of the old scatter value before we update it, as // we need to update the waiting period if this value changes. TimeDelta old_scatter_factor = scatter_factor_; const policy::DevicePolicy* device_policy = system_state_->device_policy(); if (device_policy) { int64_t new_scatter_factor_in_secs = 0; device_policy->GetScatterFactorInSeconds(&new_scatter_factor_in_secs); if (new_scatter_factor_in_secs < 0) // sanitize input, just in case. new_scatter_factor_in_secs = 0; scatter_factor_ = TimeDelta::FromSeconds(new_scatter_factor_in_secs); } bool is_scatter_enabled = false; if (scatter_factor_.InSeconds() == 0) { LOG(INFO) << "Scattering disabled since scatter factor is set to 0"; } else if (interactive) { LOG(INFO) << "Scattering disabled as this is an interactive update check"; } else if (!system_state_->hardware()->IsOOBEComplete(nullptr)) { LOG(INFO) << "Scattering disabled since OOBE is not complete yet"; } else { is_scatter_enabled = true; LOG(INFO) << "Scattering is enabled"; } if (is_scatter_enabled) { // This means the scattering policy is turned on. // Now check if we need to update the waiting period. The two cases // in which we'd need to update the waiting period are: // 1. First time in process or a scheduled check after a user-initiated one. // (omaha_request_params_->waiting_period will be zero in this case). // 2. Admin has changed the scattering policy value. // (new scattering value will be different from old one in this case). int64_t wait_period_in_secs = 0; if (omaha_request_params_->waiting_period().InSeconds() == 0) { // First case. Check if we have a suitable value to set for // the waiting period. if (prefs_->GetInt64(kPrefsWallClockWaitPeriod, &wait_period_in_secs) && wait_period_in_secs > 0 && wait_period_in_secs <= scatter_factor_.InSeconds()) { // This means: // 1. There's a persisted value for the waiting period available. // 2. And that persisted value is still valid. // So, in this case, we should reuse the persisted value instead of // generating a new random value to improve the chances of a good // distribution for scattering. omaha_request_params_->set_waiting_period( TimeDelta::FromSeconds(wait_period_in_secs)); LOG(INFO) << "Using persisted wall-clock waiting period: " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); } else { // This means there's no persisted value for the waiting period // available or its value is invalid given the new scatter_factor value. // So, we should go ahead and regenerate a new value for the // waiting period. LOG(INFO) << "Persisted value not present or not valid (" << utils::FormatSecs(wait_period_in_secs) << ") for wall-clock waiting period."; GenerateNewWaitingPeriod(); } } else if (scatter_factor_ != old_scatter_factor) { // This means there's already a waiting period value, but we detected // a change in the scattering policy value. So, we should regenerate the // waiting period to make sure it's within the bounds of the new scatter // factor value. GenerateNewWaitingPeriod(); } else { // Neither the first time scattering is enabled nor the scattering value // changed. Nothing to do. LOG(INFO) << "Keeping current wall-clock waiting period: " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); } // The invariant at this point is that omaha_request_params_->waiting_period // is non-zero no matter which path we took above. LOG_IF(ERROR, omaha_request_params_->waiting_period().InSeconds() == 0) << "Waiting Period should NOT be zero at this point!!!"; // Since scattering is enabled, wall clock based wait will always be // enabled. omaha_request_params_->set_wall_clock_based_wait_enabled(true); // If we don't have any issues in accessing the file system to update // the update check count value, we'll turn that on as well. bool decrement_succeeded = DecrementUpdateCheckCount(); omaha_request_params_->set_update_check_count_wait_enabled( decrement_succeeded); } else { // This means the scattering feature is turned off or disabled for // this particular update check. Make sure to disable // all the knobs and artifacts so that we don't invoke any scattering // related code. omaha_request_params_->set_wall_clock_based_wait_enabled(false); omaha_request_params_->set_update_check_count_wait_enabled(false); omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds(0)); prefs_->Delete(kPrefsWallClockWaitPeriod); prefs_->Delete(kPrefsUpdateCheckCount); // Don't delete the UpdateFirstSeenAt file as we don't want manual checks // that result in no-updates (e.g. due to server side throttling) to // cause update starvation by having the client generate a new // UpdateFirstSeenAt for each scheduled check that follows a manual check. } } void UpdateAttempter::GenerateNewWaitingPeriod() { omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds( base::RandInt(1, scatter_factor_.InSeconds()))); LOG(INFO) << "Generated new wall-clock waiting period: " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); // Do a best-effort to persist this in all cases. Even if the persistence // fails, we'll still be able to scatter based on our in-memory value. // The persistence only helps in ensuring a good overall distribution // across multiple devices if they tend to reboot too often. system_state_->payload_state()->SetScatteringWaitPeriod( omaha_request_params_->waiting_period()); } void UpdateAttempter::BuildPostInstallActions( InstallPlanAction* previous_action) { shared_ptr postinstall_runner_action( new PostinstallRunnerAction(system_state_->boot_control())); postinstall_runner_action->set_delegate(this); actions_.push_back(shared_ptr(postinstall_runner_action)); BondActions(previous_action, postinstall_runner_action.get()); } void UpdateAttempter::BuildUpdateActions(bool interactive) { CHECK(!processor_->IsRunning()); processor_->set_delegate(this); // Actions: std::unique_ptr update_check_fetcher( new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware())); update_check_fetcher->set_server_to_check(ServerToCheck::kUpdate); // Try harder to connect to the network, esp when not interactive. // See comment in libcurl_http_fetcher.cc. update_check_fetcher->set_no_network_max_retries(interactive ? 1 : 3); shared_ptr update_check_action( new OmahaRequestAction(system_state_, nullptr, std::move(update_check_fetcher), false)); shared_ptr response_handler_action( new OmahaResponseHandlerAction(system_state_)); shared_ptr src_filesystem_verifier_action( new FilesystemVerifierAction(system_state_->boot_control(), VerifierMode::kComputeSourceHash)); shared_ptr download_started_action( new OmahaRequestAction(system_state_, new OmahaEvent( OmahaEvent::kTypeUpdateDownloadStarted), brillo::make_unique_ptr(new LibcurlHttpFetcher( GetProxyResolver(), system_state_->hardware())), false)); LibcurlHttpFetcher* download_fetcher = new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware()); download_fetcher->set_server_to_check(ServerToCheck::kDownload); shared_ptr download_action(new DownloadAction( prefs_, system_state_->boot_control(), system_state_->hardware(), system_state_, new MultiRangeHttpFetcher(download_fetcher))); // passes ownership shared_ptr download_finished_action( new OmahaRequestAction( system_state_, new OmahaEvent(OmahaEvent::kTypeUpdateDownloadFinished), brillo::make_unique_ptr( new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware())), false)); shared_ptr dst_filesystem_verifier_action( new FilesystemVerifierAction(system_state_->boot_control(), VerifierMode::kVerifyTargetHash)); shared_ptr update_complete_action( new OmahaRequestAction( system_state_, new OmahaEvent(OmahaEvent::kTypeUpdateComplete), brillo::make_unique_ptr( new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware())), false)); download_action->set_delegate(this); response_handler_action_ = response_handler_action; download_action_ = download_action; actions_.push_back(shared_ptr(update_check_action)); actions_.push_back(shared_ptr(response_handler_action)); actions_.push_back(shared_ptr( src_filesystem_verifier_action)); actions_.push_back(shared_ptr(download_started_action)); actions_.push_back(shared_ptr(download_action)); actions_.push_back(shared_ptr(download_finished_action)); actions_.push_back(shared_ptr( dst_filesystem_verifier_action)); // Bond them together. We have to use the leaf-types when calling // BondActions(). BondActions(update_check_action.get(), response_handler_action.get()); BondActions(response_handler_action.get(), src_filesystem_verifier_action.get()); BondActions(src_filesystem_verifier_action.get(), download_action.get()); BondActions(download_action.get(), dst_filesystem_verifier_action.get()); BuildPostInstallActions(dst_filesystem_verifier_action.get()); actions_.push_back(shared_ptr(update_complete_action)); // Enqueue the actions for (const shared_ptr& action : actions_) { processor_->EnqueueAction(action.get()); } } bool UpdateAttempter::Rollback(bool powerwash) { if (!CanRollback()) { return false; } // Extra check for enterprise-enrolled devices since they don't support // powerwash. if (powerwash) { // Enterprise-enrolled devices have an empty owner in their device policy. string owner; RefreshDevicePolicy(); const policy::DevicePolicy* device_policy = system_state_->device_policy(); if (device_policy && (!device_policy->GetOwner(&owner) || owner.empty())) { LOG(ERROR) << "Enterprise device detected. " << "Cannot perform a powerwash for enterprise devices."; return false; } } processor_->set_delegate(this); // Initialize the default request params. if (!omaha_request_params_->Init("", "", true)) { LOG(ERROR) << "Unable to initialize Omaha request params."; return false; } LOG(INFO) << "Setting rollback options."; InstallPlan install_plan; install_plan.target_slot = GetRollbackSlot(); install_plan.source_slot = system_state_->boot_control()->GetCurrentSlot(); TEST_AND_RETURN_FALSE( install_plan.LoadPartitionsFromSlots(system_state_->boot_control())); install_plan.powerwash_required = powerwash; LOG(INFO) << "Using this install plan:"; install_plan.Dump(); shared_ptr install_plan_action( new InstallPlanAction(install_plan)); actions_.push_back(shared_ptr(install_plan_action)); BuildPostInstallActions(install_plan_action.get()); // Enqueue the actions for (const shared_ptr& action : actions_) { processor_->EnqueueAction(action.get()); } // Update the payload state for Rollback. system_state_->payload_state()->Rollback(); SetStatusAndNotify(UpdateStatus::ATTEMPTING_ROLLBACK); // Just in case we didn't update boot flags yet, make sure they're updated // before any update processing starts. This also schedules the start of the // actions we just posted. start_action_processor_ = true; UpdateBootFlags(); return true; } bool UpdateAttempter::CanRollback() const { // We can only rollback if the update_engine isn't busy and we have a valid // rollback partition. return (status_ == UpdateStatus::IDLE && GetRollbackSlot() != BootControlInterface::kInvalidSlot); } BootControlInterface::Slot UpdateAttempter::GetRollbackSlot() const { LOG(INFO) << "UpdateAttempter::GetRollbackSlot"; const unsigned int num_slots = system_state_->boot_control()->GetNumSlots(); const BootControlInterface::Slot current_slot = system_state_->boot_control()->GetCurrentSlot(); LOG(INFO) << " Installed slots: " << num_slots; LOG(INFO) << " Booted from slot: " << BootControlInterface::SlotName(current_slot); if (current_slot == BootControlInterface::kInvalidSlot || num_slots < 2) { LOG(INFO) << "Device is not updateable."; return BootControlInterface::kInvalidSlot; } vector bootable_slots; for (BootControlInterface::Slot slot = 0; slot < num_slots; slot++) { if (slot != current_slot && system_state_->boot_control()->IsSlotBootable(slot)) { LOG(INFO) << "Found bootable slot " << BootControlInterface::SlotName(slot); return slot; } } LOG(INFO) << "No other bootable slot found."; return BootControlInterface::kInvalidSlot; } void UpdateAttempter::CheckForUpdate(const string& app_version, const string& omaha_url, bool interactive) { LOG(INFO) << "Forced update check requested."; forced_app_version_.clear(); forced_omaha_url_.clear(); // Certain conditions must be met to allow setting custom version and update // server URLs. However, kScheduledAUTestURLRequest and kAUTestURLRequest are // always allowed regardless of device state. if (IsAnyUpdateSourceAllowed()) { forced_app_version_ = app_version; forced_omaha_url_ = omaha_url; } if (omaha_url == kScheduledAUTestURLRequest) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; // Pretend that it's not user-initiated even though it is, // so as to test scattering logic, etc. which get kicked off // only in scheduled update checks. interactive = false; } else if (omaha_url == kAUTestURLRequest) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; } if (forced_update_pending_callback_.get()) { // Make sure that a scheduling request is made prior to calling the forced // update pending callback. ScheduleUpdates(); forced_update_pending_callback_->Run(true, interactive); } } bool UpdateAttempter::RebootIfNeeded() { if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) { LOG(INFO) << "Reboot requested, but status is " << UpdateStatusToString(status_) << ", so not rebooting."; return false; } if (USE_POWER_MANAGEMENT && RequestPowerManagerReboot()) return true; return RebootDirectly(); } void UpdateAttempter::WriteUpdateCompletedMarker() { string boot_id; if (!utils::GetBootId(&boot_id)) return; prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id); int64_t value = system_state_->clock()->GetBootTime().ToInternalValue(); prefs_->SetInt64(kPrefsUpdateCompletedBootTime, value); } bool UpdateAttempter::RequestPowerManagerReboot() { org::chromium::PowerManagerProxyInterface* power_manager_proxy = system_state_->power_manager_proxy(); if (!power_manager_proxy) { LOG(WARNING) << "No PowerManager proxy defined, skipping reboot."; return false; } LOG(INFO) << "Calling " << power_manager::kPowerManagerInterface << "." << power_manager::kRequestRestartMethod; brillo::ErrorPtr error; return power_manager_proxy->RequestRestart( power_manager::REQUEST_RESTART_FOR_UPDATE, &error); } bool UpdateAttempter::RebootDirectly() { vector command; command.push_back("/sbin/shutdown"); command.push_back("-r"); command.push_back("now"); LOG(INFO) << "Running \"" << base::JoinString(command, " ") << "\""; int rc = 0; Subprocess::SynchronousExec(command, &rc, nullptr); return rc == 0; } void UpdateAttempter::OnUpdateScheduled(EvalStatus status, const UpdateCheckParams& params) { waiting_for_scheduled_check_ = false; if (status == EvalStatus::kSucceeded) { if (!params.updates_enabled) { LOG(WARNING) << "Updates permanently disabled."; // Signal disabled status, then switch right back to idle. This is // necessary for ensuring that observers waiting for a signal change will // actually notice one on subsequent calls. Note that we don't need to // re-schedule a check in this case as updates are permanently disabled; // further (forced) checks may still initiate a scheduling call. SetStatusAndNotify(UpdateStatus::DISABLED); SetStatusAndNotify(UpdateStatus::IDLE); return; } LOG(INFO) << "Running " << (params.is_interactive ? "interactive" : "periodic") << " update."; string app_version, omaha_url; if (params.is_interactive) { app_version = forced_app_version_; omaha_url = forced_omaha_url_; } Update(app_version, omaha_url, params.target_channel, params.target_version_prefix, false, params.is_interactive); } else { LOG(WARNING) << "Update check scheduling failed (possibly timed out); retrying."; ScheduleUpdates(); } // This check ensures that future update checks will be or are already // scheduled. The check should never fail. A check failure means that there's // a bug that will most likely prevent further automatic update checks. It // seems better to crash in such cases and restart the update_engine daemon // into, hopefully, a known good state. CHECK(IsUpdateRunningOrScheduled()); } void UpdateAttempter::UpdateLastCheckedTime() { last_checked_time_ = system_state_->clock()->GetWallclockTime().ToTimeT(); } // Delegate methods: void UpdateAttempter::ProcessingDone(const ActionProcessor* processor, ErrorCode code) { LOG(INFO) << "Processing Done."; actions_.clear(); // Reset cpu shares back to normal. cpu_limiter_.StopLimiter(); if (status_ == UpdateStatus::REPORTING_ERROR_EVENT) { LOG(INFO) << "Error event sent."; // Inform scheduler of new status; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); if (!fake_update_success_) { return; } LOG(INFO) << "Booted from FW B and tried to install new firmware, " "so requesting reboot from user."; } if (code == ErrorCode::kSuccess) { WriteUpdateCompletedMarker(); prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0); prefs_->SetString(kPrefsPreviousVersion, omaha_request_params_->app_version()); DeltaPerformer::ResetUpdateProgress(prefs_, false); system_state_->payload_state()->UpdateSucceeded(); // Since we're done with scattering fully at this point, this is the // safest point delete the state files, as we're sure that the status is // set to reboot (which means no more updates will be applied until reboot) // This deletion is required for correctness as we want the next update // check to re-create a new random number for the update check count. // Similarly, we also delete the wall-clock-wait period that was persisted // so that we start with a new random value for the next update check // after reboot so that the same device is not favored or punished in any // way. prefs_->Delete(kPrefsUpdateCheckCount); system_state_->payload_state()->SetScatteringWaitPeriod(TimeDelta()); prefs_->Delete(kPrefsUpdateFirstSeenAt); SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT); ScheduleUpdates(); LOG(INFO) << "Update successfully applied, waiting to reboot."; // This pointer is null during rollback operations, and the stats // don't make much sense then anyway. if (response_handler_action_) { const InstallPlan& install_plan = response_handler_action_->install_plan(); // Generate an unique payload identifier. const string target_version_uid = install_plan.payload_hash + ":" + install_plan.metadata_signature; // Expect to reboot into the new version to send the proper metric during // next boot. system_state_->payload_state()->ExpectRebootInNewVersion( target_version_uid); } else { // If we just finished a rollback, then we expect to have no Omaha // response. Otherwise, it's an error. if (system_state_->payload_state()->GetRollbackVersion().empty()) { LOG(ERROR) << "Can't send metrics because expected " "response_handler_action_ missing."; } } return; } if (ScheduleErrorEventAction()) { return; } LOG(INFO) << "No update."; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); } void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) { // Reset cpu shares back to normal. cpu_limiter_.StopLimiter(); download_progress_ = 0.0; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); actions_.clear(); error_event_.reset(nullptr); } // Called whenever an action has finished processing, either successfully // or otherwise. void UpdateAttempter::ActionCompleted(ActionProcessor* processor, AbstractAction* action, ErrorCode code) { // Reset download progress regardless of whether or not the download // action succeeded. Also, get the response code from HTTP request // actions (update download as well as the initial update check // actions). const string type = action->Type(); if (type == DownloadAction::StaticType()) { download_progress_ = 0.0; DownloadAction* download_action = static_cast(action); http_response_code_ = download_action->GetHTTPResponseCode(); } else if (type == OmahaRequestAction::StaticType()) { OmahaRequestAction* omaha_request_action = static_cast(action); // If the request is not an event, then it's the update-check. if (!omaha_request_action->IsEvent()) { http_response_code_ = omaha_request_action->GetHTTPResponseCode(); // Record the number of consecutive failed update checks. if (http_response_code_ == kHttpResponseInternalServerError || http_response_code_ == kHttpResponseServiceUnavailable) { consecutive_failed_update_checks_++; } else { consecutive_failed_update_checks_ = 0; } // Store the server-dictated poll interval, if any. server_dictated_poll_interval_ = std::max(0, omaha_request_action->GetOutputObject().poll_interval); } } if (code != ErrorCode::kSuccess) { // If the current state is at or past the download phase, count the failure // in case a switch to full update becomes necessary. Ignore network // transfer timeouts and failures. if (status_ >= UpdateStatus::DOWNLOADING && code != ErrorCode::kDownloadTransferError) { MarkDeltaUpdateFailure(); } // On failure, schedule an error event to be sent to Omaha. CreatePendingErrorEvent(action, code); return; } // Find out which action completed. if (type == OmahaResponseHandlerAction::StaticType()) { // Note that the status will be updated to DOWNLOADING when some bytes get // actually downloaded from the server and the BytesReceived callback is // invoked. This avoids notifying the user that a download has started in // cases when the server and the client are unable to initiate the download. CHECK(action == response_handler_action_.get()); const InstallPlan& plan = response_handler_action_->install_plan(); UpdateLastCheckedTime(); new_version_ = plan.version; new_payload_size_ = plan.payload_size; SetupDownload(); cpu_limiter_.StartLimiter(); SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE); } else if (type == DownloadAction::StaticType()) { SetStatusAndNotify(UpdateStatus::FINALIZING); } } void UpdateAttempter::BytesReceived(uint64_t bytes_progressed, uint64_t bytes_received, uint64_t total) { // The PayloadState keeps track of how many bytes were actually downloaded // from a given URL for the URL skipping logic. system_state_->payload_state()->DownloadProgress(bytes_progressed); double progress = 0; if (total) progress = static_cast(bytes_received) / static_cast(total); if (status_ != UpdateStatus::DOWNLOADING || bytes_received == total) { download_progress_ = progress; SetStatusAndNotify(UpdateStatus::DOWNLOADING); } else { ProgressUpdate(progress); } } void UpdateAttempter::DownloadComplete() { system_state_->payload_state()->DownloadComplete(); } bool UpdateAttempter::OnCheckForUpdates(brillo::ErrorPtr* error) { CheckForUpdate( "" /* app_version */, "" /* omaha_url */, true /* interactive */); return true; } bool UpdateAttempter::OnTrackChannel(const string& channel, brillo::ErrorPtr* error) { LOG(INFO) << "Setting destination channel to: " << channel; string error_message; if (!system_state_->request_params()->SetTargetChannel( channel, false /* powerwash_allowed */, &error_message)) { brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, "set_target_error", error_message); return false; } // Notify observers the target channel change. BroadcastChannel(); return true; } bool UpdateAttempter::GetWeaveState(int64_t* last_checked_time, double* progress, UpdateStatus* update_status, string* current_channel, string* tracking_channel) { *last_checked_time = last_checked_time_; *progress = download_progress_; *update_status = status_; OmahaRequestParams* rp = system_state_->request_params(); *current_channel = rp->current_channel(); *tracking_channel = rp->target_channel(); return true; } void UpdateAttempter::ProgressUpdate(double progress) { // Self throttle based on progress. Also send notifications if progress is // too slow. if (progress == 1.0 || progress - download_progress_ >= kBroadcastThresholdProgress || TimeTicks::Now() - last_notify_time_ >= TimeDelta::FromSeconds(kBroadcastThresholdSeconds)) { download_progress_ = progress; BroadcastStatus(); } } bool UpdateAttempter::ResetStatus() { LOG(INFO) << "Attempting to reset state from " << UpdateStatusToString(status_) << " to UpdateStatus::IDLE"; switch (status_) { case UpdateStatus::IDLE: // no-op. return true; case UpdateStatus::UPDATED_NEED_REBOOT: { bool ret_value = true; status_ = UpdateStatus::IDLE; // Remove the reboot marker so that if the machine is rebooted // after resetting to idle state, it doesn't go back to // UpdateStatus::UPDATED_NEED_REBOOT state. ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId) && ret_value; ret_value = prefs_->Delete(kPrefsUpdateCompletedBootTime) && ret_value; // Update the boot flags so the current slot has higher priority. BootControlInterface* boot_control = system_state_->boot_control(); if (!boot_control->SetActiveBootSlot(boot_control->GetCurrentSlot())) ret_value = false; // Notify the PayloadState that the successful payload was canceled. system_state_->payload_state()->ResetUpdateStatus(); // The previous version is used to report back to omaha after reboot that // we actually rebooted into the new version from this "prev-version". We // need to clear out this value now to prevent it being sent on the next // updatecheck request. ret_value = prefs_->SetString(kPrefsPreviousVersion, "") && ret_value; LOG(INFO) << "Reset status " << (ret_value ? "successful" : "failed"); return ret_value; } default: LOG(ERROR) << "Reset not allowed in this state."; return false; } } bool UpdateAttempter::GetStatus(int64_t* last_checked_time, double* progress, string* current_operation, string* new_version, int64_t* new_payload_size) { *last_checked_time = last_checked_time_; *progress = download_progress_; *current_operation = UpdateStatusToString(status_); *new_version = new_version_; *new_payload_size = new_payload_size_; return true; } void UpdateAttempter::UpdateBootFlags() { if (update_boot_flags_running_) { LOG(INFO) << "Update boot flags running, nothing to do."; return; } if (updated_boot_flags_) { LOG(INFO) << "Already updated boot flags. Skipping."; if (start_action_processor_) { ScheduleProcessingStart(); } return; } // This is purely best effort. Failures should be logged by Subprocess. Run // the script asynchronously to avoid blocking the event loop regardless of // the script runtime. update_boot_flags_running_ = true; LOG(INFO) << "Marking booted slot as good."; if (!system_state_->boot_control()->MarkBootSuccessfulAsync(Bind( &UpdateAttempter::CompleteUpdateBootFlags, base::Unretained(this)))) { LOG(ERROR) << "Failed to mark current boot as successful."; CompleteUpdateBootFlags(false); } } void UpdateAttempter::CompleteUpdateBootFlags(bool successful) { update_boot_flags_running_ = false; updated_boot_flags_ = true; if (start_action_processor_) { ScheduleProcessingStart(); } } void UpdateAttempter::BroadcastStatus() { for (const auto& observer : service_observers_) { observer->SendStatusUpdate(last_checked_time_, download_progress_, status_, new_version_, new_payload_size_); } last_notify_time_ = TimeTicks::Now(); } void UpdateAttempter::BroadcastChannel() { for (const auto& observer : service_observers_) { observer->SendChannelChangeUpdate( system_state_->request_params()->target_channel()); } } uint32_t UpdateAttempter::GetErrorCodeFlags() { uint32_t flags = 0; if (!system_state_->hardware()->IsNormalBootMode()) flags |= static_cast(ErrorCode::kDevModeFlag); if (response_handler_action_.get() && response_handler_action_->install_plan().is_resume) flags |= static_cast(ErrorCode::kResumedFlag); if (!system_state_->hardware()->IsOfficialBuild()) flags |= static_cast(ErrorCode::kTestImageFlag); if (omaha_request_params_->update_url() != constants::kOmahaDefaultProductionURL) { flags |= static_cast(ErrorCode::kTestOmahaUrlFlag); } return flags; } bool UpdateAttempter::ShouldCancel(ErrorCode* cancel_reason) { // Check if the channel we're attempting to update to is the same as the // target channel currently chosen by the user. OmahaRequestParams* params = system_state_->request_params(); if (params->download_channel() != params->target_channel()) { LOG(ERROR) << "Aborting download as target channel: " << params->target_channel() << " is different from the download channel: " << params->download_channel(); *cancel_reason = ErrorCode::kUpdateCanceledByChannelChange; return true; } return false; } void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) { status_ = status; BroadcastStatus(); } void UpdateAttempter::CreatePendingErrorEvent(AbstractAction* action, ErrorCode code) { if (error_event_.get()) { // This shouldn't really happen. LOG(WARNING) << "There's already an existing pending error event."; return; } // For now assume that a generic Omaha response action failure means that // there's no update so don't send an event. Also, double check that the // failure has not occurred while sending an error event -- in which case // don't schedule another. This shouldn't really happen but just in case... if ((action->Type() == OmahaResponseHandlerAction::StaticType() && code == ErrorCode::kError) || status_ == UpdateStatus::REPORTING_ERROR_EVENT) { return; } // Classify the code to generate the appropriate result so that // the Borgmon charts show up the results correctly. // Do this before calling GetErrorCodeForAction which could potentially // augment the bit representation of code and thus cause no matches for // the switch cases below. OmahaEvent::Result event_result; switch (code) { case ErrorCode::kOmahaUpdateIgnoredPerPolicy: case ErrorCode::kOmahaUpdateDeferredPerPolicy: case ErrorCode::kOmahaUpdateDeferredForBackoff: event_result = OmahaEvent::kResultUpdateDeferred; break; default: event_result = OmahaEvent::kResultError; break; } code = GetErrorCodeForAction(action, code); fake_update_success_ = code == ErrorCode::kPostinstallBootedFromFirmwareB; // Compute the final error code with all the bit flags to be sent to Omaha. code = static_cast( static_cast(code) | GetErrorCodeFlags()); error_event_.reset(new OmahaEvent(OmahaEvent::kTypeUpdateComplete, event_result, code)); } bool UpdateAttempter::ScheduleErrorEventAction() { if (error_event_.get() == nullptr) return false; LOG(ERROR) << "Update failed."; system_state_->payload_state()->UpdateFailed(error_event_->error_code); // Send it to Omaha. LOG(INFO) << "Reporting the error event"; shared_ptr error_event_action( new OmahaRequestAction(system_state_, error_event_.release(), // Pass ownership. brillo::make_unique_ptr(new LibcurlHttpFetcher( GetProxyResolver(), system_state_->hardware())), false)); actions_.push_back(shared_ptr(error_event_action)); processor_->EnqueueAction(error_event_action.get()); SetStatusAndNotify(UpdateStatus::REPORTING_ERROR_EVENT); processor_->StartProcessing(); return true; } void UpdateAttempter::ScheduleProcessingStart() { LOG(INFO) << "Scheduling an action processor start."; start_action_processor_ = false; MessageLoop::current()->PostTask( FROM_HERE, Bind([this] { this->processor_->StartProcessing(); })); } void UpdateAttempter::DisableDeltaUpdateIfNeeded() { int64_t delta_failures; if (omaha_request_params_->delta_okay() && prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) && delta_failures >= kMaxDeltaUpdateFailures) { LOG(WARNING) << "Too many delta update failures, forcing full update."; omaha_request_params_->set_delta_okay(false); } } void UpdateAttempter::MarkDeltaUpdateFailure() { // Don't try to resume a failed delta update. DeltaPerformer::ResetUpdateProgress(prefs_, false); int64_t delta_failures; if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) || delta_failures < 0) { delta_failures = 0; } prefs_->SetInt64(kPrefsDeltaUpdateFailures, ++delta_failures); } void UpdateAttempter::SetupDownload() { MultiRangeHttpFetcher* fetcher = static_cast(download_action_->http_fetcher()); fetcher->ClearRanges(); if (response_handler_action_->install_plan().is_resume) { // Resuming an update so fetch the update manifest metadata first. int64_t manifest_metadata_size = 0; int64_t manifest_signature_size = 0; prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size); prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size); fetcher->AddRange(0, manifest_metadata_size + manifest_signature_size); // If there're remaining unprocessed data blobs, fetch them. Be careful not // to request data beyond the end of the payload to avoid 416 HTTP response // error codes. int64_t next_data_offset = 0; prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset); uint64_t resume_offset = manifest_metadata_size + manifest_signature_size + next_data_offset; if (resume_offset < response_handler_action_->install_plan().payload_size) { fetcher->AddRange(resume_offset); } } else { fetcher->AddRange(0); } } void UpdateAttempter::PingOmaha() { if (!processor_->IsRunning()) { shared_ptr ping_action(new OmahaRequestAction( system_state_, nullptr, brillo::make_unique_ptr(new LibcurlHttpFetcher( GetProxyResolver(), system_state_->hardware())), true)); actions_.push_back(shared_ptr(ping_action)); processor_->set_delegate(nullptr); processor_->EnqueueAction(ping_action.get()); // Call StartProcessing() synchronously here to avoid any race conditions // caused by multiple outstanding ping Omaha requests. If we call // StartProcessing() asynchronously, the device can be suspended before we // get a chance to callback to StartProcessing(). When the device resumes // (assuming the device sleeps longer than the next update check period), // StartProcessing() is called back and at the same time, the next update // check is fired which eventually invokes StartProcessing(). A crash // can occur because StartProcessing() checks to make sure that the // processor is idle which it isn't due to the two concurrent ping Omaha // requests. processor_->StartProcessing(); } else { LOG(WARNING) << "Action processor running, Omaha ping suppressed."; } // Update the last check time here; it may be re-updated when an Omaha // response is received, but this will prevent us from repeatedly scheduling // checks in the case where a response is not received. UpdateLastCheckedTime(); // Update the status which will schedule the next update check SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT); ScheduleUpdates(); } bool UpdateAttempter::DecrementUpdateCheckCount() { int64_t update_check_count_value; if (!prefs_->Exists(kPrefsUpdateCheckCount)) { // This file does not exist. This means we haven't started our update // check count down yet, so nothing more to do. This file will be created // later when we first satisfy the wall-clock-based-wait period. LOG(INFO) << "No existing update check count. That's normal."; return true; } if (prefs_->GetInt64(kPrefsUpdateCheckCount, &update_check_count_value)) { // Only if we're able to read a proper integer value, then go ahead // and decrement and write back the result in the same file, if needed. LOG(INFO) << "Update check count = " << update_check_count_value; if (update_check_count_value == 0) { // It could be 0, if, for some reason, the file didn't get deleted // when we set our status to waiting for reboot. so we just leave it // as is so that we can prevent another update_check wait for this client. LOG(INFO) << "Not decrementing update check count as it's already 0."; return true; } if (update_check_count_value > 0) update_check_count_value--; else update_check_count_value = 0; // Write out the new value of update_check_count_value. if (prefs_->SetInt64(kPrefsUpdateCheckCount, update_check_count_value)) { // We successfully wrote out te new value, so enable the // update check based wait. LOG(INFO) << "New update check count = " << update_check_count_value; return true; } } LOG(INFO) << "Deleting update check count state due to read/write errors."; // We cannot read/write to the file, so disable the update check based wait // so that we don't get stuck in this OS version by any chance (which could // happen if there's some bug that causes to read/write incorrectly). // Also attempt to delete the file to do our best effort to cleanup. prefs_->Delete(kPrefsUpdateCheckCount); return false; } void UpdateAttempter::UpdateEngineStarted() { // If we just booted into a new update, keep the previous OS version // in case we rebooted because of a crash of the old version, so we // can do a proper crash report with correct information. // This must be done before calling // system_state_->payload_state()->UpdateEngineStarted() since it will // delete SystemUpdated marker file. if (system_state_->system_rebooted() && prefs_->Exists(kPrefsSystemUpdatedMarker)) { if (!prefs_->GetString(kPrefsPreviousVersion, &prev_version_)) { // If we fail to get the version string, make sure it stays empty. prev_version_.clear(); } } system_state_->payload_state()->UpdateEngineStarted(); StartP2PAtStartup(); } bool UpdateAttempter::StartP2PAtStartup() { if (system_state_ == nullptr || !system_state_->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "Not starting p2p at startup since it's not enabled."; return false; } if (system_state_->p2p_manager()->CountSharedFiles() < 1) { LOG(INFO) << "Not starting p2p at startup since our application " << "is not sharing any files."; return false; } return StartP2PAndPerformHousekeeping(); } bool UpdateAttempter::StartP2PAndPerformHousekeeping() { if (system_state_ == nullptr) return false; if (!system_state_->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "Not starting p2p since it's not enabled."; return false; } LOG(INFO) << "Ensuring that p2p is running."; if (!system_state_->p2p_manager()->EnsureP2PRunning()) { LOG(ERROR) << "Error starting p2p."; return false; } LOG(INFO) << "Performing p2p housekeeping."; if (!system_state_->p2p_manager()->PerformHousekeeping()) { LOG(ERROR) << "Error performing housekeeping for p2p."; return false; } LOG(INFO) << "Done performing p2p housekeeping."; return true; } bool UpdateAttempter::GetBootTimeAtUpdate(Time *out_boot_time) { // In case of an update_engine restart without a reboot, we stored the boot_id // when the update was completed by setting a pref, so we can check whether // the last update was on this boot or a previous one. string boot_id; TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id)); string update_completed_on_boot_id; if (!prefs_->Exists(kPrefsUpdateCompletedOnBootId) || !prefs_->GetString(kPrefsUpdateCompletedOnBootId, &update_completed_on_boot_id) || update_completed_on_boot_id != boot_id) return false; // Short-circuit avoiding the read in case out_boot_time is nullptr. if (out_boot_time) { int64_t boot_time = 0; // Since the kPrefsUpdateCompletedOnBootId was correctly set, this pref // should not fail. TEST_AND_RETURN_FALSE( prefs_->GetInt64(kPrefsUpdateCompletedBootTime, &boot_time)); *out_boot_time = Time::FromInternalValue(boot_time); } return true; } bool UpdateAttempter::IsUpdateRunningOrScheduled() { return ((status_ != UpdateStatus::IDLE && status_ != UpdateStatus::UPDATED_NEED_REBOOT) || waiting_for_scheduled_check_); } bool UpdateAttempter::IsAnyUpdateSourceAllowed() { // We allow updates from any source if either of these are true: // * The device is running an unofficial (dev/test) image. // * The debugd dev features are accessible (i.e. in devmode with no owner). // This protects users running a base image, while still allowing a specific // window (gated by the debug dev features) where `cros flash` is usable. if (!system_state_->hardware()->IsOfficialBuild()) { LOG(INFO) << "Non-official build; allowing any update source."; return true; } // Even though the debugd tools are also gated on devmode, checking here can // save us a D-Bus call so it's worth doing explicitly. if (system_state_->hardware()->IsNormalBootMode()) { LOG(INFO) << "Not in devmode; disallowing custom update sources."; return false; } // Official images in devmode are allowed a custom update source iff the // debugd dev tools are enabled. if (!debugd_proxy_) return false; int32_t dev_features = debugd::DEV_FEATURES_DISABLED; brillo::ErrorPtr error; bool success = debugd_proxy_->QueryDevFeatures(&dev_features, &error); // Some boards may not include debugd so it's expected that this may fail, // in which case we default to disallowing custom update sources. if (success && !(dev_features & debugd::DEV_FEATURES_DISABLED)) { LOG(INFO) << "Debugd dev tools enabled; allowing any update source."; return true; } LOG(INFO) << "Debugd dev tools disabled; disallowing custom update sources."; return false; } } // namespace chromeos_update_engine