1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/installer/util/google_update_util.h" 6 7 #include "base/command_line.h" 8 #include "base/files/file_path.h" 9 #include "base/files/file_util.h" 10 #include "base/logging.h" 11 #include "base/path_service.h" 12 #include "base/process/kill.h" 13 #include "base/process/launch.h" 14 #include "base/strings/string16.h" 15 #include "base/time/time.h" 16 #include "base/win/registry.h" 17 #include "base/win/scoped_handle.h" 18 #include "base/win/win_util.h" 19 #include "base/win/windows_version.h" 20 #include "chrome/installer/util/browser_distribution.h" 21 #include "chrome/installer/util/google_update_constants.h" 22 #include "chrome/installer/util/google_update_settings.h" 23 #include "chrome/installer/util/install_util.h" 24 #include "chrome/installer/util/installation_state.h" 25 #include "chrome/installer/util/product.h" 26 27 using base::win::RegKey; 28 29 namespace google_update { 30 31 namespace { 32 33 const int kGoogleUpdateTimeoutMs = 20 * 1000; 34 35 // Returns true if Google Update is present at the given level. 36 bool IsGoogleUpdatePresent(bool system_install) { 37 // Using the existence of version key in the registry to decide. 38 return GoogleUpdateSettings::GetGoogleUpdateVersion(system_install).IsValid(); 39 } 40 41 // Returns GoogleUpdateSetup.exe's executable path at specified level. 42 // or an empty path if none is found. 43 base::FilePath GetGoogleUpdateSetupExe(bool system_install) { 44 const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 45 RegKey update_key; 46 47 if (update_key.Open(root_key, kRegPathGoogleUpdate, KEY_QUERY_VALUE) == 48 ERROR_SUCCESS) { 49 base::string16 path_str; 50 base::string16 version_str; 51 if ((update_key.ReadValue(kRegPathField, &path_str) == ERROR_SUCCESS) && 52 (update_key.ReadValue(kRegGoogleUpdateVersion, &version_str) == 53 ERROR_SUCCESS)) { 54 return base::FilePath(path_str).DirName().Append(version_str). 55 Append(kGoogleUpdateSetupExe); 56 } 57 } 58 return base::FilePath(); 59 } 60 61 // If Google Update is present at system-level, sets |cmd_string| to the command 62 // line to install Google Update at user-level and returns true. 63 // Otherwise, clears |cmd_string| and returns false. 64 bool GetUserLevelGoogleUpdateInstallCommandLine(base::string16* cmd_string) { 65 cmd_string->clear(); 66 base::FilePath google_update_setup( 67 GetGoogleUpdateSetupExe(true)); // system-level. 68 if (!google_update_setup.empty()) { 69 CommandLine cmd(google_update_setup); 70 // Appends "/install runtime=true&needsadmin=false /silent /nomitag". 71 // NB: /nomitag needs to be at the end. 72 // Constants are found in code.google.com/p/omaha/common/const_cmd_line.h. 73 cmd.AppendArg("/install"); 74 // The "&" can be used in base::LaunchProcess() without quotation 75 // (this is problematic only if run from command prompt). 76 cmd.AppendArg("runtime=true&needsadmin=false"); 77 cmd.AppendArg("/silent"); 78 cmd.AppendArg("/nomitag"); 79 *cmd_string = cmd.GetCommandLineString(); 80 } 81 return !cmd_string->empty(); 82 } 83 84 // Launches command |cmd_string|, and waits for |timeout| milliseconds before 85 // timing out. To wait indefinitely, one can set 86 // |timeout| to be base::TimeDelta::FromMilliseconds(INFINITE). 87 // Returns true if this executes successfully. 88 // Returns false if command execution fails to execute, or times out. 89 bool LaunchProcessAndWaitWithTimeout(const base::string16& cmd_string, 90 base::TimeDelta timeout) { 91 bool success = false; 92 base::win::ScopedHandle process; 93 int exit_code = 0; 94 VLOG(0) << "Launching: " << cmd_string; 95 if (!base::LaunchProcess(cmd_string, base::LaunchOptions(), 96 &process)) { 97 PLOG(ERROR) << "Failed to launch (" << cmd_string << ")"; 98 } else if (!base::WaitForExitCodeWithTimeout(process.Get(), &exit_code, 99 timeout)) { 100 // The GetExitCodeProcess failed or timed-out. 101 LOG(ERROR) <<"Command (" << cmd_string << ") is taking more than " 102 << timeout.InMilliseconds() << " milliseconds to complete."; 103 } else if (exit_code != 0) { 104 LOG(ERROR) << "Command (" << cmd_string << ") exited with code " 105 << exit_code; 106 } else { 107 success = true; 108 } 109 return success; 110 } 111 112 } // namespace 113 114 bool EnsureUserLevelGoogleUpdatePresent() { 115 VLOG(0) << "Ensuring Google Update is present at user-level."; 116 117 bool success = false; 118 if (IsGoogleUpdatePresent(false)) { 119 success = true; 120 } else { 121 base::string16 cmd_string; 122 if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string)) { 123 LOG(ERROR) << "Cannot find Google Update at system-level."; 124 // Ideally we should return false. However, this case should not be 125 // encountered by regular users, and developers (who often installs 126 // Chrome without Google Update) may be unduly impeded by this case. 127 // Therefore we return true. 128 success = true; 129 } else { 130 success = LaunchProcessAndWaitWithTimeout(cmd_string, 131 base::TimeDelta::FromMilliseconds(INFINITE)); 132 } 133 } 134 return success; 135 } 136 137 bool UninstallGoogleUpdate(bool system_install) { 138 bool success = false; 139 base::string16 cmd_string( 140 GoogleUpdateSettings::GetUninstallCommandLine(system_install)); 141 if (cmd_string.empty()) { 142 success = true; // Nothing to; vacuous success. 143 } else { 144 success = LaunchProcessAndWaitWithTimeout(cmd_string, 145 base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs)); 146 } 147 return success; 148 } 149 150 void ElevateIfNeededToReenableUpdates() { 151 base::FilePath chrome_exe; 152 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 153 NOTREACHED(); 154 return; 155 } 156 installer::ProductState product_state; 157 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 158 const bool system_install = !InstallUtil::IsPerUserInstall( 159 chrome_exe.value().c_str()); 160 if (!product_state.Initialize(system_install, dist)) 161 return; 162 base::FilePath exe_path(product_state.GetSetupPath()); 163 if (exe_path.empty() || !base::PathExists(exe_path)) { 164 LOG(ERROR) << "Could not find setup.exe to reenable updates."; 165 return; 166 } 167 168 CommandLine cmd(exe_path); 169 cmd.AppendSwitch(installer::switches::kReenableAutoupdates); 170 installer::Product product(dist); 171 product.InitializeFromUninstallCommand(product_state.uninstall_command()); 172 product.AppendProductFlags(&cmd); 173 if (system_install) 174 cmd.AppendSwitch(installer::switches::kSystemLevel); 175 if (product_state.uninstall_command().HasSwitch( 176 installer::switches::kVerboseLogging)) { 177 cmd.AppendSwitch(installer::switches::kVerboseLogging); 178 } 179 180 base::LaunchOptions launch_options; 181 launch_options.force_breakaway_from_job_ = true; 182 183 if (base::win::GetVersion() >= base::win::VERSION_VISTA && 184 base::win::UserAccountControlIsEnabled()) { 185 base::LaunchElevatedProcess(cmd, launch_options, NULL); 186 } else { 187 base::LaunchProcess(cmd, launch_options, NULL); 188 } 189 } 190 191 } // namespace google_update 192