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 "win8/delegate_execute/chrome_util.h" 6 7 #include <windows.h> 8 #include <atlbase.h> 9 #include <shlobj.h> 10 11 #include <algorithm> 12 #include <limits> 13 #include <string> 14 15 #include "base/files/file_path.h" 16 #include "base/files/file_util.h" 17 #include "base/md5.h" 18 #include "base/process/kill.h" 19 #include "base/process/launch.h" 20 #include "base/process/process_handle.h" 21 #include "base/strings/string_util.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/win/registry.h" 24 #include "base/win/scoped_comptr.h" 25 #include "base/win/scoped_handle.h" 26 #include "base/win/win_util.h" 27 #include "chrome/installer/util/browser_distribution.h" 28 #include "chrome/installer/util/install_util.h" 29 #include "chrome/installer/util/util_constants.h" 30 #include "google_update/google_update_idl.h" 31 32 namespace { 33 34 #if defined(GOOGLE_CHROME_BUILD) 35 36 // TODO(grt): These constants live in installer_util. Consider moving them 37 // into common_constants to allow for reuse. 38 const base::FilePath::CharType kNewChromeExe[] = 39 FILE_PATH_LITERAL("new_chrome.exe"); 40 const wchar_t kRenameCommandValue[] = L"cmd"; 41 const wchar_t kRegPathChromeClientBase[] = 42 L"Software\\Google\\Update\\Clients\\"; 43 44 // Returns the name of the global event used to detect if |chrome_exe| is in 45 // use by a browser process. 46 // TODO(grt): Move this somewhere central so it can be used by both this 47 // IsBrowserRunning (below) and IsBrowserAlreadyRunning (browser_util_win.cc). 48 base::string16 GetEventName(const base::FilePath& chrome_exe) { 49 static wchar_t const kEventPrefix[] = L"Global\\"; 50 const size_t prefix_len = arraysize(kEventPrefix) - 1; 51 base::string16 name; 52 name.reserve(prefix_len + chrome_exe.value().size()); 53 name.assign(kEventPrefix, prefix_len); 54 name.append(chrome_exe.value()); 55 std::replace(name.begin() + prefix_len, name.end(), '\\', '!'); 56 std::transform(name.begin() + prefix_len, name.end(), 57 name.begin() + prefix_len, tolower); 58 return name; 59 } 60 61 // Returns true if |chrome_exe| is in use by a browser process. In this case, 62 // "in use" means past ChromeBrowserMainParts::PreMainMessageLoopRunImpl. 63 bool IsBrowserRunning(const base::FilePath& chrome_exe) { 64 base::win::ScopedHandle handle(::OpenEvent( 65 SYNCHRONIZE, FALSE, GetEventName(chrome_exe).c_str())); 66 if (handle.IsValid()) 67 return true; 68 DWORD last_error = ::GetLastError(); 69 if (last_error != ERROR_FILE_NOT_FOUND) { 70 AtlTrace("%hs. Failed to open browser event; error %u.\n", __FUNCTION__, 71 last_error); 72 } 73 return false; 74 } 75 76 // Returns true if the file new_chrome.exe exists in the same directory as 77 // |chrome_exe|. 78 bool NewChromeExeExists(const base::FilePath& chrome_exe) { 79 base::FilePath new_chrome_exe(chrome_exe.DirName().Append(kNewChromeExe)); 80 return base::PathExists(new_chrome_exe); 81 } 82 83 bool GetUpdateCommand(bool is_per_user, base::string16* update_command) { 84 const HKEY root = is_per_user ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 85 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 86 base::string16 reg_path_chrome_client = kRegPathChromeClientBase; 87 reg_path_chrome_client.append(dist->GetAppGuid()); 88 base::win::RegKey key(root, reg_path_chrome_client.c_str(), KEY_QUERY_VALUE); 89 90 return key.ReadValue(kRenameCommandValue, update_command) == ERROR_SUCCESS; 91 } 92 93 #endif // GOOGLE_CHROME_BUILD 94 95 } // namespace 96 97 namespace delegate_execute { 98 99 void UpdateChromeIfNeeded(const base::FilePath& chrome_exe) { 100 #if defined(GOOGLE_CHROME_BUILD) 101 // Nothing to do if a browser is already running or if there's no 102 // new_chrome.exe. 103 if (IsBrowserRunning(chrome_exe) || !NewChromeExeExists(chrome_exe)) 104 return; 105 106 base::win::ScopedHandle process_handle; 107 108 if (InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) { 109 // Read the update command from the registry. 110 base::string16 update_command; 111 if (!GetUpdateCommand(true, &update_command)) { 112 AtlTrace("%hs. Failed to read update command from registry.\n", 113 __FUNCTION__); 114 } else { 115 // Run the update command. 116 base::LaunchOptions launch_options; 117 launch_options.start_hidden = true; 118 if (!base::LaunchProcess(update_command, launch_options, 119 &process_handle)) { 120 AtlTrace("%hs. Failed to launch command to finalize update; " 121 "error %u.\n", __FUNCTION__, ::GetLastError()); 122 } 123 } 124 } else { 125 // Run the update command via Google Update. 126 HRESULT hr = S_OK; 127 base::win::ScopedComPtr<IProcessLauncher> process_launcher; 128 hr = process_launcher.CreateInstance(__uuidof(ProcessLauncherClass)); 129 if (FAILED(hr)) { 130 AtlTrace("%hs. Failed to Create ProcessLauncher; hr=0x%X.\n", 131 __FUNCTION__, hr); 132 } else { 133 ULONG_PTR handle = 0; 134 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 135 hr = process_launcher->LaunchCmdElevated( 136 dist->GetAppGuid().c_str(), kRenameCommandValue, 137 GetCurrentProcessId(), &handle); 138 if (FAILED(hr)) { 139 AtlTrace("%hs. Failed to launch command to finalize update; " 140 "hr=0x%X.\n", __FUNCTION__, hr); 141 } else { 142 process_handle.Set(reinterpret_cast<base::ProcessHandle>(handle)); 143 } 144 } 145 } 146 147 // Wait for the update to complete and report the results. 148 if (process_handle.IsValid()) { 149 int exit_code = 0; 150 // WaitForExitCode will close the handle in all cases. 151 if (!base::WaitForExitCode(process_handle.Take(), &exit_code)) { 152 AtlTrace("%hs. Failed to get result when finalizing update.\n", 153 __FUNCTION__); 154 } else if (exit_code != installer::RENAME_SUCCESSFUL) { 155 AtlTrace("%hs. Failed to finalize update with exit code %d.\n", 156 __FUNCTION__, exit_code); 157 } else { 158 AtlTrace("%hs. Finalized pending update.\n", __FUNCTION__); 159 } 160 } 161 #endif 162 } 163 164 } // delegate_execute 165