Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(231)

Side by Side Diff: chrome/app/client_util.cc

Issue 1402353011: Expose a function to get the path to a DLL. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <windows.h> 5 #include <windows.h>
6 #include <shlwapi.h>
7 6
8 #include "base/base_paths.h" 7 #include "chrome/app/client_util.h"
9 #include "base/base_switches.h" 8
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/environment.h"
13 #include "base/file_version_info.h" 9 #include "base/file_version_info.h"
14 #include "base/lazy_instance.h" 10 #include "base/files/file.h"
15 #include "base/logging.h" 11 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
17 #include "base/path_service.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h"
22 #include "base/trace_event/trace_event.h"
23 #include "base/version.h" 14 #include "base/version.h"
24 #include "base/win/scoped_handle.h"
25 #include "base/win/windows_version.h"
26 #include "chrome/app/chrome_crash_reporter_client.h"
27 #include "chrome/app/chrome_watcher_client_win.h"
28 #include "chrome/app/chrome_watcher_command_line_win.h"
29 #include "chrome/app/client_util.h"
30 #include "chrome/app/image_pre_reader_win.h"
31 #include "chrome/app/kasko_client.h"
32 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
33 #include "chrome/common/chrome_constants.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/chrome_result_codes.h"
36 #include "chrome/common/chrome_switches.h"
37 #include "chrome/common/env_vars.h"
38 #include "chrome/installer/util/google_update_constants.h"
39 #include "chrome/installer/util/google_update_settings.h"
40 #include "chrome/installer/util/install_util.h"
41 #include "chrome/installer/util/util_constants.h"
42 #include "components/crash/content/app/breakpad_win.h"
43 #include "components/crash/content/app/crash_reporter_client.h"
44 #include "content/public/app/sandbox_helper_win.h"
45 #include "content/public/common/content_switches.h"
46 #include "sandbox/win/src/sandbox.h"
47 15
48 namespace { 16 namespace {
49 // The entry point signature of chrome.dll.
50 typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*);
51
52 typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
53
54 base::LazyInstance<ChromeCrashReporterClient>::Leaky g_chrome_crash_client =
55 LAZY_INSTANCE_INITIALIZER;
56
57 // Loads |module| after setting the CWD to |module|'s directory. Returns a
58 // reference to the loaded module on success, or null on error.
59 HMODULE LoadModuleWithDirectory(const base::FilePath& module, bool pre_read) {
60 ::SetCurrentDirectoryW(module.DirName().value().c_str());
61
62 if (pre_read) {
63 // We pre-read the binary to warm the memory caches (fewer hard faults to
64 // page parts of the binary in).
65 const size_t kStepSize = 1024 * 1024;
66 size_t percent = 100;
67 ImagePreReader::PartialPreReadImage(module.value().c_str(), percent,
68 kStepSize);
69 }
70
71 return ::LoadLibraryExW(module.value().c_str(), nullptr,
72 LOAD_WITH_ALTERED_SEARCH_PATH);
73 }
74
75 void RecordDidRun(const base::FilePath& dll_path) {
76 bool system_level = !InstallUtil::IsPerUserInstall(dll_path);
77 GoogleUpdateSettings::UpdateDidRunState(true, system_level);
78 }
79
80 void ClearDidRun(const base::FilePath& dll_path) {
81 bool system_level = !InstallUtil::IsPerUserInstall(dll_path);
82 GoogleUpdateSettings::UpdateDidRunState(false, system_level);
83 }
84
85 bool InMetroMode() {
86 return (wcsstr(
87 ::GetCommandLineW(), L" -ServerName:DefaultBrowserServer") != nullptr);
88 }
89
90 typedef int (*InitMetro)();
91 17
92 // Returns the directory in which the currently running executable resides. 18 // Returns the directory in which the currently running executable resides.
93 base::FilePath GetExecutableDir() { 19 base::FilePath GetExecutableDir() {
94 base::char16 path[MAX_PATH]; 20 base::char16 path[MAX_PATH];
95 ::GetModuleFileNameW(nullptr, path, MAX_PATH); 21 ::GetModuleFileNameW(nullptr, path, MAX_PATH);
96 return base::FilePath(path).DirName(); 22 return base::FilePath(path).DirName();
97 } 23 }
98 24
99 } // namespace 25 // Returns the version in the current module's version resource or the empty
100 26 // string if none found.
101 base::string16 GetCurrentModuleVersion() { 27 base::string16 GetCurrentModuleVersion() {
102 scoped_ptr<FileVersionInfo> file_version_info( 28 scoped_ptr<FileVersionInfo> file_version_info(
103 CREATE_FILE_VERSION_INFO_FOR_CURRENT_MODULE()); 29 CREATE_FILE_VERSION_INFO_FOR_CURRENT_MODULE());
104 if (file_version_info.get()) { 30 if (file_version_info.get()) {
105 base::string16 version_string(file_version_info->file_version()); 31 base::string16 version_string(file_version_info->file_version());
106 if (Version(base::UTF16ToASCII(version_string)).IsValid()) 32 if (Version(base::UTF16ToASCII(version_string)).IsValid())
107 return version_string; 33 return version_string;
108 } 34 }
109 return base::string16(); 35 return base::string16();
110 } 36 }
111 37
112 //============================================================================= 38 // Indicates whether a file exists and can be opened for reading.
113 39 bool FileCanBeRead(const base::FilePath file_path) {
114 MainDllLoader::MainDllLoader() 40 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
grt (UTC plus 2) 2015/10/29 18:51:50 nit: return base::File(...).IsValid(); also, just
fdoray 2015/10/30 14:45:45 Done. And yes, I confirmed with ProcMon that I use
115 : dll_(nullptr), metro_mode_(InMetroMode()) { 41 return file.IsValid();
116 } 42 }
117 43
118 MainDllLoader::~MainDllLoader() { 44 } // namespace
45
46 base::FilePath GetModulePath(const base::char16* module_name,
47 base::string16* version) {
48 DCHECK(module_name);
49 DCHECK(version);
50
51 base::FilePath module_dir = GetExecutableDir();
52 base::FilePath module = module_dir.Append(module_name);
53 if (FileCanBeRead(module))
54 return module;
55
56 base::string16 version_string(GetCurrentModuleVersion());
57 if (version_string.empty()) {
58 LOG(ERROR) << "No valid Chrome version found";
59 return base::FilePath();
60 }
61 *version = version_string;
62 return module_dir.Append(version_string).Append(module_name);
119 } 63 }
120
121 // Loading chrome is an interesting affair. First we try loading from the
122 // current directory to support run-what-you-compile and other development
123 // scenarios.
124 // If that fails then we look at the version resource in the current
125 // module. This is the expected path for chrome.exe browser instances in an
126 // installed build.
127 HMODULE MainDllLoader::Load(base::string16* version, base::FilePath* module) {
128 const base::char16* dll_name = nullptr;
129 if (metro_mode_) {
130 dll_name = installer::kChromeMetroDll;
131 } else if (process_type_ == "service" || process_type_.empty()) {
132 dll_name = installer::kChromeDll;
133 } else if (process_type_ == "watcher") {
134 dll_name = kChromeWatcherDll;
135 } else {
136 #if defined(CHROME_MULTIPLE_DLL)
137 dll_name = installer::kChromeChildDll;
138 #else
139 dll_name = installer::kChromeDll;
140 #endif
141 }
142
143 const bool pre_read = !metro_mode_;
144 base::FilePath module_dir = GetExecutableDir();
145 *module = module_dir.Append(dll_name);
146 HMODULE dll = LoadModuleWithDirectory(*module, pre_read);
147 if (!dll) {
148 base::string16 version_string(GetCurrentModuleVersion());
149 if (version_string.empty()) {
150 LOG(ERROR) << "No valid Chrome version found";
151 return nullptr;
152 }
153 *version = version_string;
154 *module = module_dir.Append(version_string).Append(dll_name);
155 dll = LoadModuleWithDirectory(*module, pre_read);
156 if (!dll) {
157 PLOG(ERROR) << "Failed to load Chrome DLL from " << module->value();
158 return nullptr;
159 }
160 }
161
162 DCHECK(dll);
163 return dll;
164 }
165
166 // Launching is a matter of loading the right dll, setting the CHROME_VERSION
167 // environment variable and just calling the entry point. Derived classes can
168 // add custom code in the OnBeforeLaunch callback.
169 int MainDllLoader::Launch(HINSTANCE instance) {
170 const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
171 process_type_ = cmd_line.GetSwitchValueASCII(switches::kProcessType);
172
173 base::string16 version;
174 base::FilePath file;
175
176 if (metro_mode_) {
177 HMODULE metro_dll = Load(&version, &file);
178 if (!metro_dll)
179 return chrome::RESULT_CODE_MISSING_DATA;
180
181 InitMetro chrome_metro_main =
182 reinterpret_cast<InitMetro>(::GetProcAddress(metro_dll, "InitMetro"));
183 return chrome_metro_main();
184 }
185
186 if (process_type_ == "watcher") {
187 chrome::RegisterPathProvider();
188
189 base::win::ScopedHandle parent_process;
190 base::win::ScopedHandle on_initialized_event;
191 DWORD main_thread_id = 0;
192 if (!InterpretChromeWatcherCommandLine(cmd_line, &parent_process,
193 &main_thread_id,
194 &on_initialized_event)) {
195 return chrome::RESULT_CODE_UNSUPPORTED_PARAM;
196 }
197
198 base::FilePath default_user_data_directory;
199 if (!PathService::Get(chrome::DIR_USER_DATA, &default_user_data_directory))
200 return chrome::RESULT_CODE_MISSING_DATA;
201 // The actual user data directory may differ from the default according to
202 // policy and command-line arguments evaluated in the browser process.
203 // The hang monitor will simply be disabled if a window with this name is
204 // never instantiated by the browser process. Since this should be
205 // exceptionally rare it should not impact stability efforts.
206 base::string16 message_window_name = default_user_data_directory.value();
207
208 base::FilePath watcher_data_directory;
209 if (!PathService::Get(chrome::DIR_WATCHER_DATA, &watcher_data_directory))
210 return chrome::RESULT_CODE_MISSING_DATA;
211
212 base::string16 channel_name = GoogleUpdateSettings::GetChromeChannel(
213 !InstallUtil::IsPerUserInstall(cmd_line.GetProgram()));
214
215 // Intentionally leaked.
216 HMODULE watcher_dll = Load(&version, &file);
217 if (!watcher_dll)
218 return chrome::RESULT_CODE_MISSING_DATA;
219
220 ChromeWatcherMainFunction watcher_main =
221 reinterpret_cast<ChromeWatcherMainFunction>(
222 ::GetProcAddress(watcher_dll, kChromeWatcherDLLEntrypoint));
223 return watcher_main(chrome::kBrowserExitCodesRegistryPath,
224 parent_process.Take(), main_thread_id,
225 on_initialized_event.Take(),
226 watcher_data_directory.value().c_str(),
227 message_window_name.c_str(), channel_name.c_str());
228 }
229
230 // Initialize the sandbox services.
231 sandbox::SandboxInterfaceInfo sandbox_info = {0};
232 content::InitializeSandboxInfo(&sandbox_info);
233
234 crash_reporter::SetCrashReporterClient(g_chrome_crash_client.Pointer());
235 bool exit_now = true;
236 if (process_type_.empty()) {
237 if (breakpad::ShowRestartDialogIfCrashed(&exit_now)) {
238 // We restarted because of a previous crash. Ask user if we should
239 // Relaunch. Only for the browser process. See crbug.com/132119.
240 if (exit_now)
241 return content::RESULT_CODE_NORMAL_EXIT;
242 }
243 }
244 breakpad::InitCrashReporter(process_type_);
245
246 dll_ = Load(&version, &file);
247 if (!dll_)
248 return chrome::RESULT_CODE_MISSING_DATA;
249
250 scoped_ptr<base::Environment> env(base::Environment::Create());
251 env->SetVar(chrome::kChromeVersionEnvVar, base::WideToUTF8(version));
252
253 OnBeforeLaunch(process_type_, file);
254 DLL_MAIN chrome_main =
255 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
256 int rc = chrome_main(instance, &sandbox_info);
257 rc = OnBeforeExit(rc, file);
258 // Sandboxed processes close some system DLL handles after lockdown so ignore
259 // EXCEPTION_INVALID_HANDLE generated on Windows 10 during shutdown of these
260 // processes.
261 // TODO(wfh): Check whether MS have fixed this in Win10 RTM. crbug.com/456193
262 if (base::win::GetVersion() >= base::win::VERSION_WIN10)
263 breakpad::ConsumeInvalidHandleExceptions();
264 return rc;
265 }
266
267 void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
268 if (!dll_)
269 return;
270
271 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
272 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
273 ::GetProcAddress(dll_,
274 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
275 if (!relaunch_function) {
276 LOG(ERROR) << "Could not find exported function "
277 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
278 } else {
279 relaunch_function();
280 }
281 }
282
283 //=============================================================================
284
285 class ChromeDllLoader : public MainDllLoader {
286 protected:
287 // MainDllLoader implementation.
288 void OnBeforeLaunch(const std::string& process_type,
289 const base::FilePath& dll_path) override;
290 int OnBeforeExit(int return_code, const base::FilePath& dll_path) override;
291
292 private:
293 scoped_ptr<ChromeWatcherClient> chrome_watcher_client_;
294 #if defined(KASKO)
295 scoped_ptr<KaskoClient> kasko_client_;
296 #endif // KASKO
297 };
298
299 void ChromeDllLoader::OnBeforeLaunch(const std::string& process_type,
300 const base::FilePath& dll_path) {
301 if (process_type.empty()) {
302 RecordDidRun(dll_path);
303
304 // Launch the watcher process if stats collection consent has been granted.
305 if (g_chrome_crash_client.Get().GetCollectStatsConsent()) {
306 base::FilePath exe_path;
307 if (PathService::Get(base::FILE_EXE, &exe_path)) {
308 chrome_watcher_client_.reset(new ChromeWatcherClient(
309 base::Bind(&GenerateChromeWatcherCommandLine, exe_path)));
310 if (chrome_watcher_client_->LaunchWatcher()) {
311 #if defined(KASKO)
312 kasko::api::MinidumpType minidump_type = kasko::api::SMALL_DUMP_TYPE;
313 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
314 switches::kFullMemoryCrashReport)) {
315 minidump_type = kasko::api::FULL_DUMP_TYPE;
316 } else {
317 bool is_per_user_install =
318 g_chrome_crash_client.Get().GetIsPerUserInstall(
319 base::FilePath(exe_path));
320 if (g_chrome_crash_client.Get().GetShouldDumpLargerDumps(
321 is_per_user_install)) {
322 minidump_type = kasko::api::LARGER_DUMP_TYPE;
323 }
324 }
325
326 kasko_client_.reset(
327 new KaskoClient(chrome_watcher_client_.get(), minidump_type));
328 #endif // KASKO
329 }
330 }
331 }
332 } else {
333 // Set non-browser processes up to be killed by the system after the browser
334 // goes away. The browser uses the default shutdown order, which is 0x280.
335 // Note that lower numbers here denote "kill later" and higher numbers mean
336 // "kill sooner".
337 // This gets rid of most of those unsighly sad tabs on logout and shutdown.
338 ::SetProcessShutdownParameters(0x280 - 1, SHUTDOWN_NORETRY);
339 }
340 }
341
342 int ChromeDllLoader::OnBeforeExit(int return_code,
343 const base::FilePath& dll_path) {
344 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
345 // so we need to reset the did_run signal so omaha does not count
346 // this run as active usage.
347 if (chrome::RESULT_CODE_NORMAL_EXIT_CANCEL == return_code) {
348 ClearDidRun(dll_path);
349 }
350
351 #if defined(KASKO)
352 kasko_client_.reset();
353 #endif // KASKO
354 chrome_watcher_client_.reset();
355
356 return return_code;
357 }
358
359 //=============================================================================
360
361 class ChromiumDllLoader : public MainDllLoader {
362 protected:
363 void OnBeforeLaunch(const std::string& process_type,
364 const base::FilePath& dll_path) override {}
365 int OnBeforeExit(int return_code, const base::FilePath& dll_path) override {
366 return return_code;
367 }
368 };
369
370 MainDllLoader* MakeMainDllLoader() {
371 #if defined(GOOGLE_CHROME_BUILD)
372 return new ChromeDllLoader();
373 #else
374 return new ChromiumDllLoader();
375 #endif
376 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698