Index: chrome/installer/setup/setup_main.cc |
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc |
index 966ac436cf6d69fa4edda23d967cb45996b202c9..9a9fe4b7399fd5e971a27000dee3dedfdb345d0e 100644 |
--- a/chrome/installer/setup/setup_main.cc |
+++ b/chrome/installer/setup/setup_main.cc |
@@ -26,6 +26,7 @@ |
#include "base/path_service.h" |
#include "base/process/launch.h" |
#include "base/process/memory.h" |
+#include "base/process/process.h" |
#include "base/process/process_metrics.h" |
#include "base/strings/string16.h" |
#include "base/strings/string_number_conversions.h" |
@@ -56,6 +57,7 @@ |
#include "chrome/installer/setup/uninstall.h" |
#include "chrome/installer/util/browser_distribution.h" |
#include "chrome/installer/util/delete_after_reboot_helper.h" |
+#include "chrome/installer/util/delete_old_versions.h" |
#include "chrome/installer/util/delete_tree_work_item.h" |
#include "chrome/installer/util/google_update_constants.h" |
#include "chrome/installer/util/google_update_settings.h" |
@@ -386,14 +388,79 @@ void AddExistingMultiInstalls(const InstallationState& original_state, |
} |
} |
+void RecordNumDeleteOldVersionsAttempsBeforeAbort(int num_attempts) { |
+ UMA_HISTOGRAM_COUNTS_100( |
+ "Setup.Install.NumDeleteOldVersionsAttemptsBeforeAbort", num_attempts); |
+} |
+ |
+// Repetitively attempts to delete all files that belong to old versions of |
+// Chrome from |install_dir|. Waits 15 seconds before the first attempt and 5 |
+// minutes after each unsuccessful attempt. Returns when no files that belong to |
+// an old version of Chrome remain or when another process tries to acquire the |
+// SetupSingleton. |
+installer::InstallStatus RepeatDeleteOldVersions( |
+ const base::FilePath& install_dir, |
+ const installer::SetupSingleton& setup_singleton) { |
+ constexpr int kMaxNumAttempts = 12; |
+ int num_attempts = 0; |
+ |
+ while (num_attempts < kMaxNumAttempts) { |
+ // Wait 15 seconds before the first attempt because trying to delete old |
+ // files right away is likely to fail. Indeed, this is called in 2 |
+ // occasions: |
+ // - When the installer fails to delete old files after a not-in-use update: |
+ // retrying immediately is likely to fail again. |
+ // - When executables are successfully renamed on Chrome startup or |
+ // shutdown: old files can't be deleted because Chrome is still in use. |
+ // Wait 5 minutes after an unsuccessful attempt because retrying immediately |
+ // is likely to fail again. |
+ const base::TimeDelta max_wait_time = num_attempts == 0 |
+ ? base::TimeDelta::FromSeconds(15) |
+ : base::TimeDelta::FromMinutes(5); |
+ if (setup_singleton.WaitForInterrupt(max_wait_time)) { |
+ VLOG(1) << "Exiting --delete-old-versions process because another " |
+ "process tries to acquire the SetupSingleton."; |
+ RecordNumDeleteOldVersionsAttempsBeforeAbort(num_attempts); |
+ return installer::SETUP_SINGLETON_RELEASED; |
+ } |
+ |
+ const bool was_backgrounded = |
+ base::Process::Current().SetProcessBackgrounded(true); |
+ const bool delete_old_versions_success = |
+ installer::DeleteOldVersions(install_dir); |
+ if (!was_backgrounded) |
+ base::Process::Current().SetProcessBackgrounded(false); |
+ ++num_attempts; |
+ |
+ if (delete_old_versions_success) { |
+ VLOG(1) << "Successfully deleted all old files from " |
+ "--delete-old-versions process."; |
+ UMA_HISTOGRAM_COUNTS_100( |
+ "Setup.Install.NumDeleteOldVersionsAttemptsBeforeSuccess", |
+ num_attempts); |
+ return installer::DELETE_OLD_VERSIONS_SUCCESS; |
+ } else if (num_attempts == 1) { |
+ VLOG(1) << "Failed to delete all old files from --delete-old-versions " |
+ "process. Will retry every five minutes."; |
+ } |
+ } |
+ |
+ VLOG(1) << "Exiting --delete-old-versions process after retrying too many " |
+ "times to delete all old files."; |
+ DCHECK_EQ(num_attempts, kMaxNumAttempts); |
+ RecordNumDeleteOldVersionsAttempsBeforeAbort(num_attempts); |
+ return installer::DELETE_OLD_VERSIONS_TOO_MANY_ATTEMPTS; |
+} |
+ |
// This function is called when --rename-chrome-exe option is specified on |
// setup.exe command line. This function assumes an in-use update has happened |
// for Chrome so there should be a file called new_chrome.exe on the file |
// system and a key called 'opv' in the registry. This function will move |
// new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation. |
// This function also deletes elevation policies associated with the old version |
-// if they exist. |
+// if they exist. |setup_exe| is the path to the current executable. |
installer::InstallStatus RenameChromeExecutables( |
+ const base::FilePath& setup_exe, |
const InstallationState& original_state, |
InstallerState* installer_state) { |
// See what products are already installed in multi mode. When we do the |
@@ -454,7 +521,9 @@ installer::InstallStatus RenameChromeExecutables( |
->set_best_effort(true); |
installer::InstallStatus ret = installer::RENAME_SUCCESSFUL; |
- if (!install_list->Do()) { |
+ if (install_list->Do()) { |
+ installer::LaunchDeleteOldVersionsProcess(setup_exe, *installer_state); |
+ } else { |
LOG(ERROR) << "Renaming of executables failed. Rolling back any changes."; |
install_list->Rollback(); |
ret = installer::RENAME_FAILED; |
@@ -1235,17 +1304,26 @@ bool HandleNonInstallCmdLineOptions(const base::FilePath& setup_exe, |
LOG(DFATAL) << "Can't register browser - Chrome distribution not found"; |
} |
*exit_code = InstallUtil::GetInstallReturnCode(status); |
- } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) { |
- // If --rename-chrome-exe is specified, we want to rename the executables |
- // and exit. |
+ } else if (cmd_line.HasSwitch(installer::switches::kDeleteOldVersions) || |
+ cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) { |
std::unique_ptr<installer::SetupSingleton> setup_singleton( |
installer::SetupSingleton::Acquire( |
cmd_line, MasterPreferences::ForCurrentProcess(), original_state, |
installer_state)); |
- if (!setup_singleton) |
+ if (!setup_singleton) { |
*exit_code = installer::SETUP_SINGLETON_ACQUISITION_FAILED; |
- else |
- *exit_code = RenameChromeExecutables(*original_state, installer_state); |
+ } else if (cmd_line.HasSwitch(installer::switches::kDeleteOldVersions)) { |
+ // In multi-install mode, determine what products are installed to |
+ // populate the target_path() used below. |
+ AddExistingMultiInstalls(*original_state, installer_state); |
+ |
+ *exit_code = RepeatDeleteOldVersions(installer_state->target_path(), |
+ *setup_singleton); |
+ } else { |
+ DCHECK(cmd_line.HasSwitch(installer::switches::kRenameChromeExe)); |
+ *exit_code = |
+ RenameChromeExecutables(setup_exe, *original_state, installer_state); |
+ } |
} else if (cmd_line.HasSwitch( |
installer::switches::kRemoveChromeRegistration)) { |
// This is almost reverse of --register-chrome-browser option above. |