OLD | NEW |
---|---|
(Empty) | |
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 "remoting/host/win/worker_process_launcher.h" | |
6 | |
7 #include <windows.h> | |
8 #include <sddl.h> | |
9 #include <limits> | |
10 | |
11 #include "base/base_switches.h" | |
12 #include "base/bind.h" | |
13 #include "base/bind_helpers.h" | |
14 #include "base/logging.h" | |
15 #include "base/single_thread_task_runner.h" | |
16 #include "base/process_util.h" | |
17 #include "base/rand_util.h" | |
18 #include "base/stringprintf.h" | |
19 #include "base/utf_string_conversions.h" | |
20 #include "base/win/scoped_handle.h" | |
21 #include "ipc/ipc_channel_proxy.h" | |
22 #include "ipc/ipc_message.h" | |
23 | |
24 using base::win::ScopedHandle; | |
25 | |
26 namespace { | |
27 | |
28 // Match the pipe name prefix used by Chrome IPC channels. | |
Wez
2012/08/08 20:13:41
nit: Clarify why we need to match Chrome's prefix.
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
29 const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome."; | |
30 | |
31 } // namespace | |
32 | |
33 namespace remoting { | |
34 | |
35 WorkerProcessLauncher::Delegate::~Delegate() { | |
36 } | |
37 | |
38 WorkerProcessLauncher::WorkerProcessLauncher( | |
39 const base::Closure& stopped_callback, | |
40 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
41 scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) | |
42 : Stoppable(main_task_runner, stopped_callback), | |
43 delegate_(NULL), | |
44 main_task_runner_(main_task_runner), | |
45 ipc_task_runner_(ipc_task_runner), | |
46 process_watcher_armed_(false) { | |
47 } | |
48 | |
49 WorkerProcessLauncher::~WorkerProcessLauncher() { | |
50 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
51 DCHECK(delegate_ == NULL); | |
52 } | |
53 | |
54 void WorkerProcessLauncher::Start(Delegate* delegate, | |
55 const std::string& pipe_sddl) { | |
56 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
57 DCHECK(delegate_ == NULL); | |
58 DCHECK(ipc_channel_.get() == NULL); | |
59 DCHECK(!pipe_.IsValid()); | |
60 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
61 DCHECK(!wait_.IsValid()); | |
62 | |
63 delegate_ = delegate; | |
64 std::string channel_name = GenerateRandomChannelId(); | |
65 if (CreatePipeForIpcChannel(channel_name, pipe_sddl, &pipe_)) { | |
66 // Wrap the pipe into an IPC channel. | |
67 ipc_channel_.reset(new IPC::ChannelProxy( | |
68 IPC::ChannelHandle(pipe_), | |
69 IPC::Channel::MODE_SERVER, | |
70 this, | |
71 ipc_task_runner_)); | |
72 | |
73 // Launch the process and attach an object watcher to the returned process | |
74 // handle so that we get notified if the process terminates. | |
75 if (delegate_->DoLaunchProcess(channel_name, &wait_)) { | |
76 if (process_watcher_.StartWatching(wait_, this)) { | |
77 process_watcher_armed_ = true; | |
Wez
2012/08/08 20:13:41
nit: Do we need a separate "armed" flag, rather th
alexeypa (please no reviews)
2012/08/08 21:49:58
Agree. It looks like the watcher object takes care
| |
78 return; | |
79 } | |
80 | |
81 delegate_->DoKillProcess(CONTROL_C_EXIT); | |
82 } | |
83 } | |
84 | |
85 Stop(); | |
86 } | |
87 | |
88 void WorkerProcessLauncher::Send(IPC::Message* message) { | |
89 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
90 | |
91 ipc_channel_->Send(message); | |
92 } | |
93 | |
94 void WorkerProcessLauncher::OnObjectSignaled(HANDLE object) { | |
95 if (!main_task_runner_->BelongsToCurrentThread()) { | |
96 main_task_runner_->PostTask( | |
97 FROM_HERE, base::Bind(&WorkerProcessLauncher::OnObjectSignaled, | |
98 base::Unretained(this), object)); | |
Wez
2012/08/08 20:13:41
What guarantee do you have that WorkerProcessLaunc
alexeypa (please no reviews)
2012/08/08 21:49:58
This code has been removed. OnObjectSignalled is g
| |
99 return; | |
100 } | |
101 | |
102 process_watcher_armed_ = false; | |
103 Stop(); | |
104 } | |
105 | |
106 bool WorkerProcessLauncher::OnMessageReceived(const IPC::Message& message) { | |
107 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
108 DCHECK(ipc_channel_.get() != NULL); | |
109 DCHECK(pipe_.IsValid()); | |
110 DCHECK(wait_.IsValid()); | |
111 | |
112 return delegate_->OnMessageReceived(message); | |
113 } | |
114 | |
115 void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid) { | |
116 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
117 DCHECK(ipc_channel_.get() != NULL); | |
118 DCHECK(pipe_.IsValid()); | |
119 DCHECK(wait_.IsValid()); | |
120 | |
121 // Verify that the launched process and the one connected to the pipe is | |
122 // the same process. | |
Wez
2012/08/08 20:13:41
nit: is -> are
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
123 ULONG process_id; | |
124 if (!GetNamedPipeClientProcessId(pipe_, &process_id)) { | |
Wez
2012/08/08 20:13:41
Do you mean to actually verify the ID here?
alexeypa (please no reviews)
2012/08/08 21:49:58
The code has changed since that time.
| |
125 LOG_GETLASTERROR(ERROR) << "Failed to verify PID of the client process"; | |
Wez
2012/08/08 20:13:41
nit: verify -> query
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
126 Stop(); | |
127 return; | |
128 } | |
129 | |
130 ScopedHandle peer; | |
131 peer.Set(OpenProcess(MAXIMUM_ALLOWED, FALSE, process_id)); | |
Wez
2012/08/08 20:13:41
What guarantee is there that the peer process does
alexeypa (please no reviews)
2012/08/08 21:49:58
There is no guarantee. The caller has to keep the
| |
132 if (!peer.IsValid()) { | |
133 LOG_GETLASTERROR(ERROR) << "Failed to open the process, PID=" << process_id; | |
134 Stop(); | |
135 return; | |
136 } | |
137 | |
138 delegate_->OnChannelConnected(peer.Pass()); | |
139 } | |
140 | |
141 void WorkerProcessLauncher::OnChannelError() { | |
142 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
143 DCHECK(ipc_channel_.get() != NULL); | |
144 DCHECK(pipe_.IsValid()); | |
145 DCHECK(wait_.IsValid()); | |
146 | |
147 Stop(); | |
148 } | |
149 | |
150 void WorkerProcessLauncher::DoStop() { | |
151 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
152 | |
153 switch (stoppable_state()) { | |
154 case Stoppable::kRunning: | |
155 ipc_channel_.reset(); | |
156 pipe_.Close(); | |
157 | |
158 // Kill the process if it has been started already. | |
159 if (process_watcher_armed_) { | |
160 delegate_->DoKillProcess(CONTROL_C_EXIT); | |
161 return; | |
162 } | |
163 | |
164 // fall through | |
165 | |
166 case Stoppable::kStopping: | |
167 DCHECK(ipc_channel_.get() == NULL); | |
168 DCHECK(!pipe_.IsValid()); | |
169 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
170 | |
171 wait_.Close(); | |
172 delegate_ = NULL; | |
173 CompleteStopping(); | |
174 break; | |
175 | |
176 case Stoppable::kStopped: | |
177 default: | |
178 DCHECK(!"Unexpected state"); | |
179 } | |
180 } | |
181 | |
182 // Creates the server end of the Chromoting IPC channel. | |
183 bool WorkerProcessLauncher::CreatePipeForIpcChannel( | |
184 const std::string& channel_name, | |
185 const std::string& pipe_sddl, | |
186 ScopedHandle* pipe_out) { | |
187 // Create security descriptor for the channel. | |
188 SECURITY_ATTRIBUTES security_attributes; | |
189 security_attributes.nLength = sizeof(security_attributes); | |
190 security_attributes.bInheritHandle = FALSE; | |
191 | |
192 ULONG security_descriptor_length = 0; | |
193 if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( | |
Wez
2012/08/08 20:13:41
nit: Strictly |pipe_sddl| is UTF-8 and this functi
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
194 pipe_sddl.c_str(), | |
195 SDDL_REVISION_1, | |
196 reinterpret_cast<PSECURITY_DESCRIPTOR*>( | |
197 &security_attributes.lpSecurityDescriptor), | |
198 &security_descriptor_length)) { | |
199 LOG_GETLASTERROR(ERROR) << | |
200 "Failed to create a security descriptor for the Chromoting IPC channel"; | |
201 return false; | |
202 } | |
203 | |
204 // Convert the channel name to the pipe name. | |
205 std::string pipe_name(kChromePipeNamePrefix); | |
206 pipe_name.append(channel_name); | |
207 | |
208 // Create the server end of the pipe. This code should match the code in | |
209 // IPC::Channel with exception of passing a non-default security descriptor. | |
210 base::win::ScopedHandle pipe; | |
211 pipe.Set(CreateNamedPipeW(UTF8ToUTF16(pipe_name).c_str(), | |
212 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | | |
213 FILE_FLAG_FIRST_PIPE_INSTANCE, | |
Wez
2012/08/08 20:13:41
nit: This would be more readable if the parameters
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
214 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, | |
215 1, | |
216 IPC::Channel::kReadBufferSize, | |
217 IPC::Channel::kReadBufferSize, | |
218 5000, | |
219 &security_attributes)); | |
220 if (!pipe.IsValid()) { | |
221 LOG_GETLASTERROR(ERROR) << | |
222 "Failed to create the server end of the Chromoting IPC channel"; | |
223 LocalFree(security_attributes.lpSecurityDescriptor); | |
224 return false; | |
225 } | |
226 | |
227 LocalFree(security_attributes.lpSecurityDescriptor); | |
228 | |
229 *pipe_out = pipe.Pass(); | |
230 return true; | |
231 } | |
232 | |
233 // N.B. Stolen from src/content/common/child_process_host_impl.cc | |
Wez
2012/08/08 20:13:41
nit: Stolen -> Copied ;)
alexeypa (please no reviews)
2012/08/08 21:49:58
Your honor, I object! :-)
Wez
2012/08/10 00:09:23
ChildProcessHostImpl still has this code, so you c
alexeypa (please no reviews)
2012/08/10 16:18:44
Done.
| |
234 std::string WorkerProcessLauncher::GenerateRandomChannelId() { | |
235 return base::StringPrintf("%d.%p.%d", | |
236 base::GetCurrentProcId(), this, | |
237 base::RandInt(0, std::numeric_limits<int>::max())); | |
238 } | |
239 | |
240 } // namespace remoting | |
OLD | NEW |