blob: adcaa335ad50982059dbb616cee0f0b59468b5ac [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2012 The Chromium Authors
[email protected]bc38c252011-04-12 21:46:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Daniel Cheng7d9e3d52022-02-26 09:03:245#include "chrome/browser/first_run/upgrade_util_win.h"
[email protected]bc38c252011-04-12 21:46:576
Nico Webereaa08412019-08-14 01:24:377// Must be first.
[email protected]770c6d82012-09-06 22:21:328#include <windows.h>
Nico Webereaa08412019-08-14 01:24:379
robliaoa872e992017-05-18 06:36:1910#include <objbase.h>
[email protected]fdbea98d2014-05-16 19:29:2011#include <psapi.h>
[email protected]770c6d82012-09-06 22:21:3212#include <shellapi.h>
Robert Liaob2bc703d2017-10-17 20:52:3513#include <wrl/client.h>
[email protected]770c6d82012-09-06 22:21:3214
[email protected]bc38c252011-04-12 21:46:5715#include <algorithm>
Greg Thompsona1f95122019-01-31 22:46:4516#include <ios>
[email protected]bc38c252011-04-12 21:46:5717#include <string>
18
19#include "base/base_paths.h"
Etienne Bergeron7a0ef612023-09-15 18:28:0820#include "base/check.h"
[email protected]bc38c252011-04-12 21:46:5721#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5222#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0423#include "base/files/file_util.h"
[email protected]bc38c252011-04-12 21:46:5724#include "base/logging.h"
Gabriel Charette12b58ac2021-09-22 18:08:5525#include "base/metrics/histogram_macros.h"
[email protected]bc38c252011-04-12 21:46:5726#include "base/path_service.h"
[email protected]d09a4ce1c2013-07-24 17:37:0227#include "base/process/launch.h"
28#include "base/process/process_handle.h"
[email protected]3ea1b182013-02-08 22:38:4129#include "base/strings/string_number_conversions.h"
[email protected]d8830562013-06-10 22:01:5430#include "base/strings/string_util.h"
Gabriel Charette12b58ac2021-09-22 18:08:5531#include "base/time/time.h"
32#include "base/trace_event/trace_event.h"
[email protected]bc38c252011-04-12 21:46:5733#include "base/win/registry.h"
[email protected]770c6d82012-09-06 22:21:3234#include "base/win/windows_version.h"
Nico Webereaa08412019-08-14 01:24:3735#include "build/branding_buildflags.h"
ananta069fc882014-09-13 01:22:1236#include "chrome/browser/browser_process.h"
Etienne Bergeron7a0ef612023-09-15 18:28:0837#include "chrome/browser/chrome_process_singleton.h"
Daniel Cheng7d9e3d52022-02-26 09:03:2438#include "chrome/browser/first_run/upgrade_util.h"
[email protected]3f69d6e612012-08-03 18:52:2739#include "chrome/browser/shell_integration.h"
Javier Flores Assad9c26f9b92022-05-21 01:20:4340#include "chrome/browser/win/browser_util.h"
[email protected]80274b92011-07-15 17:20:3841#include "chrome/common/chrome_switches.h"
ananta069fc882014-09-13 01:22:1242#include "chrome/common/pref_names.h"
grt4474dad2017-02-27 21:00:4643#include "chrome/install_static/install_util.h"
S. Ganeshf77e2a52022-11-22 18:40:1944#include "chrome/installer/util/app_command.h"
[email protected]bc38c252011-04-12 21:46:5745#include "chrome/installer/util/util_constants.h"
brettwb1fc1b82016-02-02 00:19:0846#include "components/prefs/pref_service.h"
ananta069fc882014-09-13 01:22:1247#include "ui/base/ui_base_switches.h"
[email protected]bc38c252011-04-12 21:46:5748
Nico Webereaa08412019-08-14 01:24:3749#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
S. Ganesh6adf04c2023-06-07 19:34:5550#include "chrome/updater/app/server/win/updater_legacy_idl.h"
grt235b3f092015-05-27 21:42:4851#endif
52
[email protected]bc38c252011-04-12 21:46:5753namespace {
54
[email protected]650b2d52013-02-10 03:41:4555bool GetNewerChromeFile(base::FilePath* path) {
Avi Drissman9098f9002018-05-04 00:11:5256 if (!base::PathService::Get(base::DIR_EXE, path))
[email protected]bc38c252011-04-12 21:46:5757 return false;
58 *path = path->Append(installer::kChromeNewExe);
59 return true;
60}
61
62bool InvokeGoogleUpdateForRename() {
Nico Webereaa08412019-08-14 01:24:3763#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
Gabriel Charettef7e85712021-12-10 20:46:2264 // This has been identified as very slow on some startups. Detailed trace
65 // events below try to shine a light on each steps. crbug.com/1252004
Gabriel Charette12b58ac2021-09-22 18:08:5566 TRACE_EVENT0("startup", "upgrade_util::InvokeGoogleUpdateForRename");
67
S. Ganeshea7ef0872024-03-21 16:28:3268 // Chrome queries for the SxS IIDs first, with a fallback to the legacy IID,
69 // to make sure that marshaling loads the proxy/stub from the correct (HKLM)
70 // hive.
Robert Liaob2bc703d2017-10-17 20:52:3571 Microsoft::WRL::ComPtr<IProcessLauncher> ipl;
Gabriel Charettef7e85712021-12-10 20:46:2272 {
73 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename CoCreateInstance");
S. Ganeshea7ef0872024-03-21 16:28:3274 Microsoft::WRL::ComPtr<IUnknown> unknown;
Gabriel Charettef7e85712021-12-10 20:46:2275 HRESULT hr = ::CoCreateInstance(__uuidof(ProcessLauncherClass), nullptr,
S. Ganeshea7ef0872024-03-21 16:28:3276 CLSCTX_ALL, IID_PPV_ARGS(&unknown));
Gabriel Charettef7e85712021-12-10 20:46:2277 if (FAILED(hr)) {
78 TRACE_EVENT0("startup",
79 "InvokeGoogleUpdateForRename CoCreateInstance failed");
80 LOG(ERROR) << "CoCreate ProcessLauncherClass failed; hr = " << std::hex
81 << hr;
82 return false;
83 }
S. Ganeshea7ef0872024-03-21 16:28:3284 hr = unknown.CopyTo(__uuidof(IProcessLauncherSystem),
85 IID_PPV_ARGS_Helper(&ipl));
86 if (FAILED(hr)) {
87 hr = unknown.As(&ipl);
88 }
89 if (FAILED(hr)) {
90 TRACE_EVENT0("startup",
91 "InvokeGoogleUpdateForRename QueryInterface failed");
92 LOG(ERROR) << "QueryInterface failed; hr = " << std::hex << hr;
93 return false;
94 }
[email protected]bc38c252011-04-12 21:46:5795 }
Greg Thompsona1f95122019-01-31 22:46:4596
S. Ganesh74432c12022-11-02 06:35:3497 ULONG_PTR process_handle = 0;
Gabriel Charettef7e85712021-12-10 20:46:2298 {
99 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename LaunchCmdElevated");
Dominique Fauteux-Chapleau269cabf2021-12-10 22:10:31100 HRESULT hr = ipl->LaunchCmdElevated(
S. Ganeshf77e2a52022-11-22 18:40:19101 install_static::GetAppGuid(), installer::kCmdRenameChromeExe,
Dominique Fauteux-Chapleau269cabf2021-12-10 22:10:31102 ::GetCurrentProcessId(), &process_handle);
Gabriel Charettef7e85712021-12-10 20:46:22103 if (FAILED(hr)) {
104 TRACE_EVENT0("startup",
105 "InvokeGoogleUpdateForRename LaunchCmdElevated failed");
106 LOG(ERROR) << "IProcessLauncher::LaunchCmdElevated failed; hr = "
107 << std::hex << hr;
108 return false;
109 }
Greg Thompsona1f95122019-01-31 22:46:45110 }
111
112 base::Process rename_process(
113 reinterpret_cast<base::ProcessHandle>(process_handle));
114 int exit_code;
Gabriel Charettef7e85712021-12-10 20:46:22115 {
116 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename WaitForExit");
117 if (!rename_process.WaitForExit(&exit_code)) {
118 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename WaitForExit failed");
119 PLOG(ERROR) << "WaitForExit of rename process failed";
120 return false;
121 }
Greg Thompsona1f95122019-01-31 22:46:45122 }
123
124 if (exit_code != installer::RENAME_SUCCESSFUL) {
Gabriel Charettef7e85712021-12-10 20:46:22125 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename !RENAME_SUCCESSFUL");
Greg Thompsona1f95122019-01-31 22:46:45126 LOG(ERROR) << "Rename process failed with exit code " << exit_code;
127 return false;
128 }
129
Gabriel Charette334775a2021-12-13 18:57:26130 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename RENAME_SUCCESSFUL");
131
Greg Thompsona1f95122019-01-31 22:46:45132 return true;
Nico Webereaa08412019-08-14 01:24:37133#else // BUILDFLAG(GOOGLE_CHROME_BRANDING)
[email protected]bc38c252011-04-12 21:46:57134 return false;
Nico Webereaa08412019-08-14 01:24:37135#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
[email protected]bc38c252011-04-12 21:46:57136}
137
138} // namespace
139
[email protected]815856722011-04-13 17:19:19140namespace upgrade_util {
141
Greg Thompson87ee38f2019-08-09 06:00:23142bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55143 TRACE_EVENT0("startup", "upgrade_util::RelaunchChromeBrowserImpl");
144
[email protected]650b2d52013-02-10 03:41:45145 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52146 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]3f69d6e612012-08-03 18:52:27147 NOTREACHED();
148 return false;
149 }
150
[email protected]fdbea98d2014-05-16 19:29:20151 // Explicitly make sure to relaunch chrome.exe rather than old_chrome.exe.
152 // This can happen when old_chrome.exe is launched by a user.
avi556c05022014-12-22 23:31:43153 base::CommandLine chrome_exe_command_line = command_line;
[email protected]fdbea98d2014-05-16 19:29:20154 chrome_exe_command_line.SetProgram(
155 chrome_exe.DirName().Append(installer::kChromeExe));
156
scottmg20920cb2016-04-07 16:23:59157 // Set the working directory to the exe's directory. This avoids a handle to
158 // the version directory being kept open in the relaunched child process.
159 base::LaunchOptions launch_options;
160 launch_options.current_directory = chrome_exe.DirName();
Greg Thompson47faf202018-05-18 20:59:03161 // Give the new process the right to bring its windows to the foreground.
162 launch_options.grant_foreground_privilege = true;
scottmg20920cb2016-04-07 16:23:59163 return base::LaunchProcess(chrome_exe_command_line, launch_options).IsValid();
[email protected]815856722011-04-13 17:19:19164}
165
166bool IsUpdatePendingRestart() {
Gabriel Charette12b58ac2021-09-22 18:08:55167 TRACE_EVENT0("startup", "upgrade_util::IsUpdatePendingRestart");
[email protected]650b2d52013-02-10 03:41:45168 base::FilePath new_chrome_exe;
[email protected]815856722011-04-13 17:19:19169 if (!GetNewerChromeFile(&new_chrome_exe))
170 return false;
[email protected]7567484142013-07-11 17:36:07171 return base::PathExists(new_chrome_exe);
[email protected]815856722011-04-13 17:19:19172}
173
[email protected]815856722011-04-13 17:19:19174bool SwapNewChromeExeIfPresent() {
grtb35a90b2016-08-30 05:34:21175 if (!IsUpdatePendingRestart())
[email protected]bc38c252011-04-12 21:46:57176 return false;
grtb35a90b2016-08-30 05:34:21177
Gabriel Charette12b58ac2021-09-22 18:08:55178 TRACE_EVENT0("startup", "upgrade_util::SwapNewChromeExeIfPresent");
179
Etienne Bergeron7a0ef612023-09-15 18:28:08180 // Renaming the chrome executable requires the process singleton to avoid
181 // any race condition.
182 CHECK(ChromeProcessSingleton::IsSingletonInstance());
183
fdoraye9c97a02016-10-04 12:05:16184 // If this is a system-level install, ask Google Update to launch an elevated
185 // process to rename Chrome executables.
Greg Thompsond2efb1f2018-08-29 06:12:54186 if (install_static::IsSystemInstall())
fdoraye9c97a02016-10-04 12:05:16187 return InvokeGoogleUpdateForRename();
[email protected]bc38c252011-04-12 21:46:57188
fdoraye9c97a02016-10-04 12:05:16189 // If this is a user-level install, directly launch a process to rename Chrome
190 // executables. Obtain the command to launch the process from the registry.
S. Ganeshf77e2a52022-11-22 18:40:19191 installer::AppCommand rename_cmd(installer::kCmdRenameChromeExe, {});
192 if (!rename_cmd.Initialize(HKEY_CURRENT_USER))
Greg Thompsona1f95122019-01-31 22:46:45193 return false;
Greg Thompsona1f95122019-01-31 22:46:45194
195 base::LaunchOptions options;
196 options.wait = true;
197 options.start_hidden = true;
198 ::SetLastError(ERROR_SUCCESS);
S. Ganeshf77e2a52022-11-22 18:40:19199 base::Process process =
200 base::LaunchProcess(rename_cmd.command_line(), options);
Greg Thompsona1f95122019-01-31 22:46:45201 if (!process.IsValid()) {
202 PLOG(ERROR) << "Launch rename process failed";
203 return false;
204 }
205
206 DWORD exit_code;
207 if (!::GetExitCodeProcess(process.Handle(), &exit_code)) {
208 PLOG(ERROR) << "GetExitCodeProcess of rename process failed";
209 return false;
210 }
211
212 if (exit_code != installer::RENAME_SUCCESSFUL) {
213 LOG(ERROR) << "Rename process failed with exit code " << exit_code;
214 return false;
215 }
216
217 return true;
[email protected]bc38c252011-04-12 21:46:57218}
219
[email protected]fdbea98d2014-05-16 19:29:20220bool IsRunningOldChrome() {
Gabriel Charette12b58ac2021-09-22 18:08:55221 TRACE_EVENT0("startup", "upgrade_util::IsRunningOldChrome");
[email protected]fdbea98d2014-05-16 19:29:20222 // This figures out the actual file name that the section containing the
223 // mapped exe refers to. This is used instead of GetModuleFileName because the
224 // .exe may have been renamed out from under us while we've been running which
225 // GetModuleFileName won't notice.
226 wchar_t mapped_file_name[MAX_PATH * 2] = {};
227
228 if (!::GetMappedFileName(::GetCurrentProcess(),
229 reinterpret_cast<void*>(::GetModuleHandle(NULL)),
Daniel Cheng7d9e3d52022-02-26 09:03:24230 mapped_file_name, std::size(mapped_file_name))) {
[email protected]fdbea98d2014-05-16 19:29:20231 return false;
232 }
233
234 base::FilePath file_name(base::FilePath(mapped_file_name).BaseName());
235 return base::FilePath::CompareEqualIgnoreCase(file_name.value(),
236 installer::kChromeOldExe);
237}
238
avi556c05022014-12-22 23:31:43239bool DoUpgradeTasks(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55240 TRACE_EVENT0("startup", "upgrade_util::DoUpgradeTasks");
Javier Flores Assad9c26f9b92022-05-21 01:20:43241 // If there is no other instance already running then check if there is a
242 // pending update and complete it by performing the swap and then relaunch.
Etienne Bergeron7a0ef612023-09-15 18:28:08243
244 // Upgrade tasks require the process singleton to avoid any race condition.
245 CHECK(ChromeProcessSingleton::IsSingletonInstance());
246
Javier Flores Assad9c26f9b92022-05-21 01:20:43247 bool did_swap = false;
248 if (!browser_util::IsBrowserAlreadyRunning())
249 did_swap = SwapNewChromeExeIfPresent();
250
251 // We don't need to relaunch if we didn't swap and we aren't running stale
252 // binaries.
253 if (!did_swap && !IsRunningOldChrome()) {
[email protected]bc38c252011-04-12 21:46:57254 return false;
Gabriel Charette12b58ac2021-09-22 18:08:55255 }
Javier Flores Assad9c26f9b92022-05-21 01:20:43256
[email protected]bc38c252011-04-12 21:46:57257 // At this point the chrome.exe has been swapped with the new one.
Wissem Gamra8e67a95d2023-03-17 17:41:21258 if (!RelaunchChromeBrowser(command_line)) {
Javier Flores Assad9c26f9b92022-05-21 01:20:43259 // The relaunch failed. Feel free to panic now.
[email protected]bc38c252011-04-12 21:46:57260 NOTREACHED();
[email protected]bc38c252011-04-12 21:46:57261 }
262 return true;
263}
264
[email protected]815856722011-04-13 17:19:19265} // namespace upgrade_util