| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/installer/util/google_update_util.h" |
| |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/path_service.h" |
| #include "base/process/kill.h" |
| #include "base/process/launch.h" |
| #include "base/strings/string16.h" |
| #include "base/time/time.h" |
| #include "base/win/registry.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/win_util.h" |
| #include "base/win/windows_version.h" |
| #include "chrome/installer/util/browser_distribution.h" |
| #include "chrome/installer/util/google_update_constants.h" |
| #include "chrome/installer/util/google_update_settings.h" |
| #include "chrome/installer/util/install_util.h" |
| #include "chrome/installer/util/installation_state.h" |
| #include "chrome/installer/util/product.h" |
| |
| using base::win::RegKey; |
| |
| namespace google_update { |
| |
| namespace { |
| |
| const int kGoogleUpdateTimeoutMs = 20 * 1000; |
| |
| // Returns true if Google Update is present at the given level. |
| bool IsGoogleUpdatePresent(bool system_install) { |
| // Using the existence of version key in the registry to decide. |
| return GoogleUpdateSettings::GetGoogleUpdateVersion(system_install).IsValid(); |
| } |
| |
| // Returns GoogleUpdateSetup.exe's executable path at specified level. |
| // or an empty path if none is found. |
| base::FilePath GetGoogleUpdateSetupExe(bool system_install) { |
| const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; |
| RegKey update_key; |
| |
| if (update_key.Open(root_key, kRegPathGoogleUpdate, KEY_QUERY_VALUE) == |
| ERROR_SUCCESS) { |
| base::string16 path_str; |
| base::string16 version_str; |
| if ((update_key.ReadValue(kRegPathField, &path_str) == ERROR_SUCCESS) && |
| (update_key.ReadValue(kRegGoogleUpdateVersion, &version_str) == |
| ERROR_SUCCESS)) { |
| return base::FilePath(path_str).DirName().Append(version_str). |
| Append(kGoogleUpdateSetupExe); |
| } |
| } |
| return base::FilePath(); |
| } |
| |
| // If Google Update is present at system-level, sets |cmd_string| to the command |
| // line to install Google Update at user-level and returns true. |
| // Otherwise, clears |cmd_string| and returns false. |
| bool GetUserLevelGoogleUpdateInstallCommandLine(base::string16* cmd_string) { |
| cmd_string->clear(); |
| base::FilePath google_update_setup( |
| GetGoogleUpdateSetupExe(true)); // system-level. |
| if (!google_update_setup.empty()) { |
| base::CommandLine cmd(google_update_setup); |
| // Appends "/install runtime=true&needsadmin=false /silent /nomitag". |
| // NB: /nomitag needs to be at the end. |
| // Constants are found in code.google.com/p/omaha/common/const_cmd_line.h. |
| cmd.AppendArg("/install"); |
| // The "&" can be used in base::LaunchProcess() without quotation |
| // (this is problematic only if run from command prompt). |
| cmd.AppendArg("runtime=true&needsadmin=false"); |
| cmd.AppendArg("/silent"); |
| cmd.AppendArg("/nomitag"); |
| *cmd_string = cmd.GetCommandLineString(); |
| } |
| return !cmd_string->empty(); |
| } |
| |
| // Launches command |cmd_string|, and waits for |timeout| milliseconds before |
| // timing out. To wait indefinitely, one can set |
| // |timeout| to be base::TimeDelta::FromMilliseconds(INFINITE). |
| // Returns true if this executes successfully. |
| // Returns false if command execution fails to execute, or times out. |
| bool LaunchProcessAndWaitWithTimeout(const base::string16& cmd_string, |
| base::TimeDelta timeout) { |
| bool success = false; |
| int exit_code = 0; |
| VLOG(0) << "Launching: " << cmd_string; |
| base::Process process = |
| base::LaunchProcess(cmd_string, base::LaunchOptions()); |
| if (!process.IsValid()) { |
| PLOG(ERROR) << "Failed to launch (" << cmd_string << ")"; |
| } else if (!process.WaitForExitWithTimeout(timeout, &exit_code)) { |
| // The GetExitCodeProcess failed or timed-out. |
| LOG(ERROR) <<"Command (" << cmd_string << ") is taking more than " |
| << timeout.InMilliseconds() << " milliseconds to complete."; |
| } else if (exit_code != 0) { |
| LOG(ERROR) << "Command (" << cmd_string << ") exited with code " |
| << exit_code; |
| } else { |
| success = true; |
| } |
| return success; |
| } |
| |
| } // namespace |
| |
| bool EnsureUserLevelGoogleUpdatePresent() { |
| VLOG(0) << "Ensuring Google Update is present at user-level."; |
| |
| bool success = false; |
| if (IsGoogleUpdatePresent(false)) { |
| success = true; |
| } else { |
| base::string16 cmd_string; |
| if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string)) { |
| LOG(ERROR) << "Cannot find Google Update at system-level."; |
| // Ideally we should return false. However, this case should not be |
| // encountered by regular users, and developers (who often installs |
| // Chrome without Google Update) may be unduly impeded by this case. |
| // Therefore we return true. |
| success = true; |
| } else { |
| success = LaunchProcessAndWaitWithTimeout(cmd_string, |
| base::TimeDelta::FromMilliseconds(INFINITE)); |
| } |
| } |
| return success; |
| } |
| |
| bool UninstallGoogleUpdate(bool system_install) { |
| bool success = false; |
| base::string16 cmd_string( |
| GoogleUpdateSettings::GetUninstallCommandLine(system_install)); |
| if (cmd_string.empty()) { |
| success = true; // Nothing to; vacuous success. |
| } else { |
| success = LaunchProcessAndWaitWithTimeout(cmd_string, |
| base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs)); |
| } |
| return success; |
| } |
| |
| void ElevateIfNeededToReenableUpdates() { |
| base::FilePath chrome_exe; |
| if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { |
| NOTREACHED(); |
| return; |
| } |
| installer::ProductState product_state; |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| const bool system_install = !InstallUtil::IsPerUserInstall(chrome_exe); |
| if (!product_state.Initialize(system_install, dist)) |
| return; |
| base::FilePath exe_path(product_state.GetSetupPath()); |
| if (exe_path.empty() || !base::PathExists(exe_path)) { |
| LOG(ERROR) << "Could not find setup.exe to reenable updates."; |
| return; |
| } |
| |
| base::CommandLine cmd(exe_path); |
| cmd.AppendSwitch(installer::switches::kReenableAutoupdates); |
| installer::Product product(dist); |
| product.InitializeFromUninstallCommand(product_state.uninstall_command()); |
| product.AppendProductFlags(&cmd); |
| if (system_install) |
| cmd.AppendSwitch(installer::switches::kSystemLevel); |
| if (product_state.uninstall_command().HasSwitch( |
| installer::switches::kVerboseLogging)) { |
| cmd.AppendSwitch(installer::switches::kVerboseLogging); |
| } |
| |
| base::LaunchOptions launch_options; |
| launch_options.force_breakaway_from_job_ = true; |
| |
| if (base::win::GetVersion() >= base::win::VERSION_VISTA && |
| base::win::UserAccountControlIsEnabled()) { |
| base::LaunchElevatedProcess(cmd, launch_options); |
| } else { |
| base::LaunchProcess(cmd, launch_options); |
| } |
| } |
| |
| } // namespace google_update |