blob: c6edb61f2e5bfb6508aeb47dc491a9bd9bd23a99 [file] [log] [blame]
erikwright7c4a4262015-01-09 18:32:331// 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
avi2729e442015-12-26 05:27:457#include <stdint.h>
8
erikwright7c4a4262015-01-09 18:32:339#include <string>
10
Bruce Dawson5c9466e2017-07-13 20:54:5211#include "base/base_switches.h"
erikwright7c4a4262015-01-09 18:32:3312#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"
brucedawsond3beca72015-12-10 02:22:4416#include "base/win/win_util.h"
erikwright7c4a4262015-01-09 18:32:3317#include "chrome/common/chrome_switches.h"
brettw90e92602015-10-10 00:12:4018#include "content/public/common/content_switches.h"
erikwright7c4a4262015-01-09 18:32:3319
20namespace {
erikwrightc57091e2015-02-04 15:19:1021
chrisha5a17ab252016-01-11 17:17:3122const char kMainThreadIdSwitch[] = "main-thread-id";
erikwrightc57091e2015-02-04 15:19:1023const char kOnIninitializedEventHandleSwitch[] = "on-initialized-event-handle";
erikwright7c4a4262015-01-09 18:32:3324const char kParentHandleSwitch[] = "parent-handle";
erikwrightc57091e2015-02-04 15:19:1025
26void AppendHandleSwitch(const std::string& switch_name,
27 HANDLE handle,
28 base::CommandLine* command_line) {
29 command_line->AppendSwitchASCII(
brucedawsond3beca72015-12-10 02:22:4430 switch_name, base::UintToString(base::win::HandleToUint32(handle)));
erikwrightc57091e2015-02-04 15:19:1031}
32
erikwright17e228e2015-07-31 19:01:2533uint32_t ReadUintSwitch(const base::CommandLine& command_line,
erikwrightc57091e2015-02-04 15:19:1034 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.";
erikwright17e228e2015-07-31 19:01:2540 return 0;
erikwrightc57091e2015-02-04 15:19:1041 }
erikwright17e228e2015-07-31 19:01:2542 return switch_uint;
43}
44
45HANDLE ReadHandleFromSwitch(const base::CommandLine& command_line,
46 const std::string& switch_name) {
47 return reinterpret_cast<HANDLE>(ReadUintSwitch(command_line, switch_name));
erikwrightc57091e2015-02-04 15:19:1048}
49
erikwright7c4a4262015-01-09 18:32:3350} // namespace
51
chrisha5a17ab252016-01-11 17:17:3152ChromeWatcherCommandLineGenerator::ChromeWatcherCommandLineGenerator(
53 const base::FilePath& chrome_exe) : chrome_exe_(chrome_exe) {
54}
55
chrisha8106a6b2016-01-11 21:17:5956ChromeWatcherCommandLineGenerator::~ChromeWatcherCommandLineGenerator() {
57}
58
chrisha5a17ab252016-01-11 17:17:3159bool ChromeWatcherCommandLineGenerator::SetOnInitializedEventHandle(
60 HANDLE on_initialized_event_handle) {
61 return SetHandle(on_initialized_event_handle, &on_initialized_event_handle_);
62}
63
64bool 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.
70base::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
78void 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
86bool 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
100ChromeWatcherCommandLine::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
109ChromeWatcherCommandLine::~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
dchengc0e39d572016-04-19 06:15:17116std::unique_ptr<ChromeWatcherCommandLine>
chrisha5a17ab252016-01-11 17:17:31117ChromeWatcherCommandLine::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))
dchengc0e39d572016-04-19 06:15:17128 return std::unique_ptr<ChromeWatcherCommandLine>();
chrisha5a17ab252016-01-11 17:17:31129
dchengc0e39d572016-04-19 06:15:17130 return std::unique_ptr<ChromeWatcherCommandLine>(new ChromeWatcherCommandLine(
chrisha5a17ab252016-01-11 17:17:31131 on_initialized_event_handle.Take(), parent_process_handle.Take(),
132 main_thread_id));
133}
134
135base::win::ScopedHandle
136ChromeWatcherCommandLine::TakeOnInitializedEventHandle() {
137 return std::move(on_initialized_event_handle_);
138}
139
140base::win::ScopedHandle ChromeWatcherCommandLine::TakeParentProcessHandle() {
141 return std::move(parent_process_handle_);
142}
143
erikwright7c4a4262015-01-09 18:32:33144base::CommandLine GenerateChromeWatcherCommandLine(
145 const base::FilePath& chrome_exe,
erikwrightc57091e2015-02-04 15:19:10146 HANDLE parent_process,
erikwright17e228e2015-07-31 19:01:25147 DWORD main_thread_id,
erikwrightc57091e2015-02-04 15:19:10148 HANDLE on_initialized_event) {
erikwright7c4a4262015-01-09 18:32:33149 base::CommandLine command_line(chrome_exe);
fdorayc196a2b42016-01-14 20:44:34150 command_line.AppendSwitchASCII(switches::kProcessType,
151 switches::kWatcherProcess);
erikwright17e228e2015-07-31 19:01:25152 command_line.AppendSwitchASCII(kMainThreadIdSwitch,
153 base::UintToString(main_thread_id));
erikwrightc57091e2015-02-04 15:19:10154 AppendHandleSwitch(kOnIninitializedEventHandleSwitch, on_initialized_event,
155 &command_line);
156 AppendHandleSwitch(kParentHandleSwitch, parent_process, &command_line);
fdoray343068c42016-02-03 15:45:58157
fdoray9a9f0c42016-06-17 21:46:31158 command_line.AppendArg(switches::kPrefetchArgumentWatcher);
Bruce Dawson5c9466e2017-07-13 20:54:52159
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));
fdoray343068c42016-02-03 15:45:58167
erikwright7c4a4262015-01-09 18:32:33168 return command_line;
169}
170
erikwrightc57091e2015-02-04 15:19:10171bool InterpretChromeWatcherCommandLine(
172 const base::CommandLine& command_line,
173 base::win::ScopedHandle* parent_process,
erikwright17e228e2015-07-31 19:01:25174 DWORD* main_thread_id,
erikwrightc57091e2015-02-04 15:19:10175 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);
erikwright17e228e2015-07-31 19:01:25187 *main_thread_id = ReadUintSwitch(command_line, kMainThreadIdSwitch);
erikwrightc57091e2015-02-04 15:19:10188
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 }
erikwright7c4a4262015-01-09 18:32:33200 }
201
erikwrightc57091e2015-02-04 15:19:10202 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 }
erikwright7c4a4262015-01-09 18:32:33213 }
214
erikwright17e228e2015-07-31 19:01:25215 if (!*main_thread_id || !on_initialized_event->IsValid() ||
216 !parent_process->IsValid()) {
erikwrightc57091e2015-02-04 15:19:10217 // If one was valid and not the other, free the valid one.
218 on_initialized_event->Close();
219 parent_process->Close();
erikwright17e228e2015-07-31 19:01:25220 *main_thread_id = 0;
erikwrightc57091e2015-02-04 15:19:10221 return false;
222 }
223
224 return true;
erikwright7c4a4262015-01-09 18:32:33225}