// Copyright (c) 2014 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/app/chrome_watcher_command_line_win.h" #include #include #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/win/win_util.h" #include "chrome/common/chrome_switches.h" #include "components/startup_metric_utils/common/pre_read_field_trial_utils_win.h" #include "content/public/common/content_switches.h" namespace { const char kMainThreadIdSwitch[] = "main-thread-id"; const char kOnIninitializedEventHandleSwitch[] = "on-initialized-event-handle"; const char kParentHandleSwitch[] = "parent-handle"; void AppendHandleSwitch(const std::string& switch_name, HANDLE handle, base::CommandLine* command_line) { command_line->AppendSwitchASCII( switch_name, base::UintToString(base::win::HandleToUint32(handle))); } uint32_t ReadUintSwitch(const base::CommandLine& command_line, const std::string& switch_name) { std::string switch_string = command_line.GetSwitchValueASCII(switch_name); unsigned int switch_uint = 0; if (switch_string.empty() || !base::StringToUint(switch_string, &switch_uint)) { DLOG(ERROR) << "Missing or invalid " << switch_name << " argument."; return 0; } return switch_uint; } HANDLE ReadHandleFromSwitch(const base::CommandLine& command_line, const std::string& switch_name) { return reinterpret_cast(ReadUintSwitch(command_line, switch_name)); } } // namespace ChromeWatcherCommandLineGenerator::ChromeWatcherCommandLineGenerator( const base::FilePath& chrome_exe) : chrome_exe_(chrome_exe) { } ChromeWatcherCommandLineGenerator::~ChromeWatcherCommandLineGenerator() { } bool ChromeWatcherCommandLineGenerator::SetOnInitializedEventHandle( HANDLE on_initialized_event_handle) { return SetHandle(on_initialized_event_handle, &on_initialized_event_handle_); } bool ChromeWatcherCommandLineGenerator::SetParentProcessHandle( HANDLE parent_process_handle) { return SetHandle(parent_process_handle, &parent_process_handle_); } // Generates a command-line representing this configuration. base::CommandLine ChromeWatcherCommandLineGenerator::GenerateCommandLine() { // TODO(chrisha): Get rid of the following function and move the // implementation here. return GenerateChromeWatcherCommandLine( chrome_exe_, parent_process_handle_.Get(), ::GetCurrentThreadId(), on_initialized_event_handle_.Get()); } void ChromeWatcherCommandLineGenerator::GetInheritedHandles( std::vector* inherited_handles) const { if (on_initialized_event_handle_.IsValid()) inherited_handles->push_back(on_initialized_event_handle_.Get()); if (parent_process_handle_.IsValid()) inherited_handles->push_back(parent_process_handle_.Get()); } bool ChromeWatcherCommandLineGenerator::SetHandle( HANDLE handle, base::win::ScopedHandle* scoped_handle) { // Create a duplicate handle that is inheritable. HANDLE proc = ::GetCurrentProcess(); HANDLE new_handle = 0; if (!::DuplicateHandle(proc, handle, proc, &new_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { return false; } scoped_handle->Set(new_handle); return true; } ChromeWatcherCommandLine::ChromeWatcherCommandLine( HANDLE on_initialized_event_handle, HANDLE parent_process_handle, DWORD main_thread_id) : on_initialized_event_handle_(on_initialized_event_handle), parent_process_handle_(parent_process_handle), main_thread_id_(main_thread_id) { } ChromeWatcherCommandLine::~ChromeWatcherCommandLine() { // If any handles were not taken then die violently. CHECK(!on_initialized_event_handle_.IsValid() && !parent_process_handle_.IsValid()) << "Handles left untaken."; } scoped_ptr ChromeWatcherCommandLine::InterpretCommandLine( const base::CommandLine& command_line) { base::win::ScopedHandle on_initialized_event_handle; base::win::ScopedHandle parent_process_handle; DWORD main_thread_id = 0; // TODO(chrisha): Get rid of the following function and move the // implementation here. if (!InterpretChromeWatcherCommandLine( command_line, &parent_process_handle, &main_thread_id, &on_initialized_event_handle)) return scoped_ptr(); return scoped_ptr(new ChromeWatcherCommandLine( on_initialized_event_handle.Take(), parent_process_handle.Take(), main_thread_id)); } base::win::ScopedHandle ChromeWatcherCommandLine::TakeOnInitializedEventHandle() { return std::move(on_initialized_event_handle_); } base::win::ScopedHandle ChromeWatcherCommandLine::TakeParentProcessHandle() { return std::move(parent_process_handle_); } base::CommandLine GenerateChromeWatcherCommandLine( const base::FilePath& chrome_exe, HANDLE parent_process, DWORD main_thread_id, HANDLE on_initialized_event) { base::CommandLine command_line(chrome_exe); command_line.AppendSwitchASCII(switches::kProcessType, switches::kWatcherProcess); command_line.AppendSwitchASCII(kMainThreadIdSwitch, base::UintToString(main_thread_id)); AppendHandleSwitch(kOnIninitializedEventHandleSwitch, on_initialized_event, &command_line); AppendHandleSwitch(kParentHandleSwitch, parent_process, &command_line); #if defined(OS_WIN) if (startup_metric_utils::GetPreReadOptions().use_prefetch_argument) command_line.AppendArg(switches::kPrefetchArgumentWatcher); #endif // defined(OS_WIN) return command_line; } bool InterpretChromeWatcherCommandLine( const base::CommandLine& command_line, base::win::ScopedHandle* parent_process, DWORD* main_thread_id, base::win::ScopedHandle* on_initialized_event) { DCHECK(on_initialized_event); DCHECK(parent_process); // For consistency, always close any existing HANDLEs here. on_initialized_event->Close(); parent_process->Close(); HANDLE parent_handle = ReadHandleFromSwitch(command_line, kParentHandleSwitch); HANDLE on_initialized_event_handle = ReadHandleFromSwitch(command_line, kOnIninitializedEventHandleSwitch); *main_thread_id = ReadUintSwitch(command_line, kMainThreadIdSwitch); if (parent_handle) { // Initial test of the handle, a zero PID indicates invalid handle, or not // a process handle. In this case, parsing fails and we avoid closing the // handle. DWORD process_pid = ::GetProcessId(parent_handle); if (process_pid == 0) { DLOG(ERROR) << "Invalid " << kParentHandleSwitch << " argument. Can't get parent PID."; } else { parent_process->Set(parent_handle); } } if (on_initialized_event_handle) { DWORD result = ::WaitForSingleObject(on_initialized_event_handle, 0); if (result == WAIT_FAILED) { DPLOG(ERROR) << "Unexpected error while testing the initialization event."; } else if (result != WAIT_TIMEOUT) { DLOG(ERROR) << "Unexpected result while testing the initialization event " "with WaitForSingleObject: " << result; } else { on_initialized_event->Set(on_initialized_event_handle); } } if (!*main_thread_id || !on_initialized_event->IsValid() || !parent_process->IsValid()) { // If one was valid and not the other, free the valid one. on_initialized_event->Close(); parent_process->Close(); *main_thread_id = 0; return false; } return true; }