erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 1 | // Copyright (c) 2014 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/app/chrome_watcher_command_line_win.h" |
| 6 | |
avi | 2729e44 | 2015-12-26 05:27:45 | [diff] [blame] | 7 | #include <stdint.h> |
| 8 | |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 9 | #include <string> |
| 10 | |
Bruce Dawson | 5c9466e | 2017-07-13 20:54:52 | [diff] [blame] | 11 | #include "base/base_switches.h" |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 12 | #include "base/command_line.h" |
| 13 | #include "base/files/file_path.h" |
| 14 | #include "base/logging.h" |
| 15 | #include "base/strings/string_number_conversions.h" |
brucedawson | d3beca7 | 2015-12-10 02:22:44 | [diff] [blame] | 16 | #include "base/win/win_util.h" |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 17 | #include "chrome/common/chrome_switches.h" |
brettw | 90e9260 | 2015-10-10 00:12:40 | [diff] [blame] | 18 | #include "content/public/common/content_switches.h" |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 19 | |
| 20 | namespace { |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 21 | |
chrisha | 5a17ab25 | 2016-01-11 17:17:31 | [diff] [blame] | 22 | const char kMainThreadIdSwitch[] = "main-thread-id"; |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 23 | const char kOnIninitializedEventHandleSwitch[] = "on-initialized-event-handle"; |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 24 | const char kParentHandleSwitch[] = "parent-handle"; |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 25 | |
| 26 | void AppendHandleSwitch(const std::string& switch_name, |
| 27 | HANDLE handle, |
| 28 | base::CommandLine* command_line) { |
| 29 | command_line->AppendSwitchASCII( |
brucedawson | d3beca7 | 2015-12-10 02:22:44 | [diff] [blame] | 30 | switch_name, base::UintToString(base::win::HandleToUint32(handle))); |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 31 | } |
| 32 | |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 33 | uint32_t ReadUintSwitch(const base::CommandLine& command_line, |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 34 | const std::string& switch_name) { |
| 35 | std::string switch_string = command_line.GetSwitchValueASCII(switch_name); |
| 36 | unsigned int switch_uint = 0; |
| 37 | if (switch_string.empty() || |
| 38 | !base::StringToUint(switch_string, &switch_uint)) { |
| 39 | DLOG(ERROR) << "Missing or invalid " << switch_name << " argument."; |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 40 | return 0; |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 41 | } |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 42 | return switch_uint; |
| 43 | } |
| 44 | |
| 45 | HANDLE ReadHandleFromSwitch(const base::CommandLine& command_line, |
| 46 | const std::string& switch_name) { |
| 47 | return reinterpret_cast<HANDLE>(ReadUintSwitch(command_line, switch_name)); |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 48 | } |
| 49 | |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 50 | } // namespace |
| 51 | |
chrisha | 5a17ab25 | 2016-01-11 17:17:31 | [diff] [blame] | 52 | ChromeWatcherCommandLineGenerator::ChromeWatcherCommandLineGenerator( |
| 53 | const base::FilePath& chrome_exe) : chrome_exe_(chrome_exe) { |
| 54 | } |
| 55 | |
chrisha | 8106a6b | 2016-01-11 21:17:59 | [diff] [blame] | 56 | ChromeWatcherCommandLineGenerator::~ChromeWatcherCommandLineGenerator() { |
| 57 | } |
| 58 | |
chrisha | 5a17ab25 | 2016-01-11 17:17:31 | [diff] [blame] | 59 | bool ChromeWatcherCommandLineGenerator::SetOnInitializedEventHandle( |
| 60 | HANDLE on_initialized_event_handle) { |
| 61 | return SetHandle(on_initialized_event_handle, &on_initialized_event_handle_); |
| 62 | } |
| 63 | |
| 64 | bool ChromeWatcherCommandLineGenerator::SetParentProcessHandle( |
| 65 | HANDLE parent_process_handle) { |
| 66 | return SetHandle(parent_process_handle, &parent_process_handle_); |
| 67 | } |
| 68 | |
| 69 | // Generates a command-line representing this configuration. |
| 70 | base::CommandLine ChromeWatcherCommandLineGenerator::GenerateCommandLine() { |
| 71 | // TODO(chrisha): Get rid of the following function and move the |
| 72 | // implementation here. |
| 73 | return GenerateChromeWatcherCommandLine( |
| 74 | chrome_exe_, parent_process_handle_.Get(), ::GetCurrentThreadId(), |
| 75 | on_initialized_event_handle_.Get()); |
| 76 | } |
| 77 | |
| 78 | void ChromeWatcherCommandLineGenerator::GetInheritedHandles( |
| 79 | std::vector<HANDLE>* inherited_handles) const { |
| 80 | if (on_initialized_event_handle_.IsValid()) |
| 81 | inherited_handles->push_back(on_initialized_event_handle_.Get()); |
| 82 | if (parent_process_handle_.IsValid()) |
| 83 | inherited_handles->push_back(parent_process_handle_.Get()); |
| 84 | } |
| 85 | |
| 86 | bool ChromeWatcherCommandLineGenerator::SetHandle( |
| 87 | HANDLE handle, base::win::ScopedHandle* scoped_handle) { |
| 88 | // Create a duplicate handle that is inheritable. |
| 89 | HANDLE proc = ::GetCurrentProcess(); |
| 90 | HANDLE new_handle = 0; |
| 91 | if (!::DuplicateHandle(proc, handle, proc, &new_handle, 0, TRUE, |
| 92 | DUPLICATE_SAME_ACCESS)) { |
| 93 | return false; |
| 94 | } |
| 95 | |
| 96 | scoped_handle->Set(new_handle); |
| 97 | return true; |
| 98 | } |
| 99 | |
| 100 | ChromeWatcherCommandLine::ChromeWatcherCommandLine( |
| 101 | HANDLE on_initialized_event_handle, |
| 102 | HANDLE parent_process_handle, |
| 103 | DWORD main_thread_id) |
| 104 | : on_initialized_event_handle_(on_initialized_event_handle), |
| 105 | parent_process_handle_(parent_process_handle), |
| 106 | main_thread_id_(main_thread_id) { |
| 107 | } |
| 108 | |
| 109 | ChromeWatcherCommandLine::~ChromeWatcherCommandLine() { |
| 110 | // If any handles were not taken then die violently. |
| 111 | CHECK(!on_initialized_event_handle_.IsValid() && |
| 112 | !parent_process_handle_.IsValid()) |
| 113 | << "Handles left untaken."; |
| 114 | } |
| 115 | |
dcheng | c0e39d57 | 2016-04-19 06:15:17 | [diff] [blame] | 116 | std::unique_ptr<ChromeWatcherCommandLine> |
chrisha | 5a17ab25 | 2016-01-11 17:17:31 | [diff] [blame] | 117 | ChromeWatcherCommandLine::InterpretCommandLine( |
| 118 | const base::CommandLine& command_line) { |
| 119 | base::win::ScopedHandle on_initialized_event_handle; |
| 120 | base::win::ScopedHandle parent_process_handle; |
| 121 | DWORD main_thread_id = 0; |
| 122 | |
| 123 | // TODO(chrisha): Get rid of the following function and move the |
| 124 | // implementation here. |
| 125 | if (!InterpretChromeWatcherCommandLine( |
| 126 | command_line, &parent_process_handle, &main_thread_id, |
| 127 | &on_initialized_event_handle)) |
dcheng | c0e39d57 | 2016-04-19 06:15:17 | [diff] [blame] | 128 | return std::unique_ptr<ChromeWatcherCommandLine>(); |
chrisha | 5a17ab25 | 2016-01-11 17:17:31 | [diff] [blame] | 129 | |
dcheng | c0e39d57 | 2016-04-19 06:15:17 | [diff] [blame] | 130 | return std::unique_ptr<ChromeWatcherCommandLine>(new ChromeWatcherCommandLine( |
chrisha | 5a17ab25 | 2016-01-11 17:17:31 | [diff] [blame] | 131 | on_initialized_event_handle.Take(), parent_process_handle.Take(), |
| 132 | main_thread_id)); |
| 133 | } |
| 134 | |
| 135 | base::win::ScopedHandle |
| 136 | ChromeWatcherCommandLine::TakeOnInitializedEventHandle() { |
| 137 | return std::move(on_initialized_event_handle_); |
| 138 | } |
| 139 | |
| 140 | base::win::ScopedHandle ChromeWatcherCommandLine::TakeParentProcessHandle() { |
| 141 | return std::move(parent_process_handle_); |
| 142 | } |
| 143 | |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 144 | base::CommandLine GenerateChromeWatcherCommandLine( |
| 145 | const base::FilePath& chrome_exe, |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 146 | HANDLE parent_process, |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 147 | DWORD main_thread_id, |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 148 | HANDLE on_initialized_event) { |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 149 | base::CommandLine command_line(chrome_exe); |
fdoray | c196a2b4 | 2016-01-14 20:44:34 | [diff] [blame] | 150 | command_line.AppendSwitchASCII(switches::kProcessType, |
| 151 | switches::kWatcherProcess); |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 152 | command_line.AppendSwitchASCII(kMainThreadIdSwitch, |
| 153 | base::UintToString(main_thread_id)); |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 154 | AppendHandleSwitch(kOnIninitializedEventHandleSwitch, on_initialized_event, |
| 155 | &command_line); |
| 156 | AppendHandleSwitch(kParentHandleSwitch, parent_process, &command_line); |
fdoray | 343068c4 | 2016-02-03 15:45:58 | [diff] [blame] | 157 | |
fdoray | 9a9f0c4 | 2016-06-17 21:46:31 | [diff] [blame] | 158 | command_line.AppendArg(switches::kPrefetchArgumentWatcher); |
Bruce Dawson | 5c9466e | 2017-07-13 20:54:52 | [diff] [blame] | 159 | |
| 160 | // Copy over logging switches. |
| 161 | static const char* const kSwitchNames[] = {switches::kEnableLogging, |
| 162 | switches::kV, switches::kVModule}; |
| 163 | base::CommandLine current_command_line = |
| 164 | *base::CommandLine::ForCurrentProcess(); |
| 165 | command_line.CopySwitchesFrom(current_command_line, kSwitchNames, |
| 166 | arraysize(kSwitchNames)); |
fdoray | 343068c4 | 2016-02-03 15:45:58 | [diff] [blame] | 167 | |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 168 | return command_line; |
| 169 | } |
| 170 | |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 171 | bool InterpretChromeWatcherCommandLine( |
| 172 | const base::CommandLine& command_line, |
| 173 | base::win::ScopedHandle* parent_process, |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 174 | DWORD* main_thread_id, |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 175 | base::win::ScopedHandle* on_initialized_event) { |
| 176 | DCHECK(on_initialized_event); |
| 177 | DCHECK(parent_process); |
| 178 | |
| 179 | // For consistency, always close any existing HANDLEs here. |
| 180 | on_initialized_event->Close(); |
| 181 | parent_process->Close(); |
| 182 | |
| 183 | HANDLE parent_handle = |
| 184 | ReadHandleFromSwitch(command_line, kParentHandleSwitch); |
| 185 | HANDLE on_initialized_event_handle = |
| 186 | ReadHandleFromSwitch(command_line, kOnIninitializedEventHandleSwitch); |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 187 | *main_thread_id = ReadUintSwitch(command_line, kMainThreadIdSwitch); |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 188 | |
| 189 | if (parent_handle) { |
| 190 | // Initial test of the handle, a zero PID indicates invalid handle, or not |
| 191 | // a process handle. In this case, parsing fails and we avoid closing the |
| 192 | // handle. |
| 193 | DWORD process_pid = ::GetProcessId(parent_handle); |
| 194 | if (process_pid == 0) { |
| 195 | DLOG(ERROR) << "Invalid " << kParentHandleSwitch |
| 196 | << " argument. Can't get parent PID."; |
| 197 | } else { |
| 198 | parent_process->Set(parent_handle); |
| 199 | } |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 200 | } |
| 201 | |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 202 | if (on_initialized_event_handle) { |
| 203 | DWORD result = ::WaitForSingleObject(on_initialized_event_handle, 0); |
| 204 | if (result == WAIT_FAILED) { |
| 205 | DPLOG(ERROR) |
| 206 | << "Unexpected error while testing the initialization event."; |
| 207 | } else if (result != WAIT_TIMEOUT) { |
| 208 | DLOG(ERROR) << "Unexpected result while testing the initialization event " |
| 209 | "with WaitForSingleObject: " << result; |
| 210 | } else { |
| 211 | on_initialized_event->Set(on_initialized_event_handle); |
| 212 | } |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 213 | } |
| 214 | |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 215 | if (!*main_thread_id || !on_initialized_event->IsValid() || |
| 216 | !parent_process->IsValid()) { |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 217 | // If one was valid and not the other, free the valid one. |
| 218 | on_initialized_event->Close(); |
| 219 | parent_process->Close(); |
erikwright | 17e228e | 2015-07-31 19:01:25 | [diff] [blame] | 220 | *main_thread_id = 0; |
erikwright | c57091e | 2015-02-04 15:19:10 | [diff] [blame] | 221 | return false; |
| 222 | } |
| 223 | |
| 224 | return true; |
erikwright | 7c4a426 | 2015-01-09 18:32:33 | [diff] [blame] | 225 | } |