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

Side by Side Diff: remoting/host/win/wts_session_process_delegate.cc

Issue 15077010: [Chromoting] Refactored worker process launching code and speeded up the desktop process launch. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: - Created 7 years, 7 months 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 | Annotate | Revision Log
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 // This file implements the Windows service controlling Me2Me host processes 5 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions. 6 // running within user sessions.
7 7
8 #include "remoting/host/win/wts_session_process_delegate.h" 8 #include "remoting/host/win/wts_session_process_delegate.h"
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/command_line.h" 11 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h" 12 #include "base/files/file_path.h"
15 #include "base/logging.h" 13 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop.h" 14 #include "base/message_loop.h"
18 #include "base/single_thread_task_runner.h" 15 #include "base/single_thread_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
19 #include "base/utf_string_conversions.h" 17 #include "base/utf_string_conversions.h"
20 #include "base/win/scoped_handle.h" 18 #include "base/win/scoped_handle.h"
21 #include "base/win/windows_version.h" 19 #include "base/win/windows_version.h"
22 #include "ipc/ipc_channel.h" 20 #include "ipc/ipc_channel.h"
23 #include "ipc/ipc_channel_proxy.h" 21 #include "ipc/ipc_channel_proxy.h"
22 #include "ipc/ipc_listener.h"
24 #include "ipc/ipc_message.h" 23 #include "ipc/ipc_message.h"
25 #include "remoting/host/host_exit_codes.h"
26 #include "remoting/host/host_main.h" 24 #include "remoting/host/host_main.h"
27 #include "remoting/host/ipc_constants.h" 25 #include "remoting/host/ipc_constants.h"
28 #include "remoting/host/win/launch_process_with_token.h" 26 #include "remoting/host/win/launch_process_with_token.h"
29 #include "remoting/host/win/worker_process_launcher.h" 27 #include "remoting/host/win/worker_process_launcher.h"
30 #include "remoting/host/win/wts_terminal_monitor.h" 28 #include "remoting/host/win/wts_terminal_monitor.h"
31 #include "remoting/host/worker_process_ipc_delegate.h" 29 #include "remoting/host/worker_process_ipc_delegate.h"
32 30
33 using base::win::ScopedHandle; 31 using base::win::ScopedHandle;
34 32
35 // Name of the default session desktop. 33 // Name of the default session desktop.
36 const char kDefaultDesktopName[] = "winsta0\\default"; 34 const char kDefaultDesktopName[] = "winsta0\\default";
37 35
38 namespace remoting { 36 namespace remoting {
39 37
40 // A private class actually implementing the functionality provided by 38 // A private class actually implementing the functionality provided by
41 // |WtsSessionProcessDelegate|. This class is ref-counted and implements 39 // |WtsSessionProcessDelegate|. This class is ref-counted and implements
42 // asynchronous fire-and-forget shutdown. 40 // asynchronous fire-and-forget shutdown.
43 class WtsSessionProcessDelegate::Core 41 class WtsSessionProcessDelegate::Core
44 : public base::RefCountedThreadSafe<WtsSessionProcessDelegate::Core>, 42 : public base::RefCountedThreadSafe<Core>,
45 public base::MessagePumpForIO::IOHandler, 43 public base::MessagePumpForIO::IOHandler,
46 public WorkerProcessLauncher::Delegate { 44 public IPC::Listener {
47 public: 45 public:
48 // The caller must ensure that |delegate| remains valid at least until 46 Core(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
49 // Stop() method has been called. 47 scoped_ptr<CommandLine> target,
50 Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
51 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
52 scoped_ptr<CommandLine> target_command,
53 bool launch_elevated, 48 bool launch_elevated,
54 const std::string& channel_security); 49 const std::string& channel_security);
55 50
56 // base::MessagePumpForIO::IOHandler implementation.
57 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
58 DWORD bytes_transferred,
59 DWORD error) OVERRIDE;
60
61 // IPC::Sender implementation.
62 virtual bool Send(IPC::Message* message) OVERRIDE;
63
64 // WorkerProcessLauncher::Delegate implementation.
65 virtual void CloseChannel() OVERRIDE;
66 virtual DWORD GetProcessId() const OVERRIDE;
67 virtual bool IsPermanentError(int failure_count) const OVERRIDE;
68 virtual void KillProcess(DWORD exit_code) OVERRIDE;
69 virtual bool LaunchProcess(
70 IPC::Listener* delegate,
71 base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
72
73 // Initializes the object returning true on success. 51 // Initializes the object returning true on success.
74 bool Initialize(uint32 session_id); 52 bool Initialize(uint32 session_id);
75 53
76 // Stops the object asynchronously. 54 // Stops the object asynchronously.
77 void Stop(); 55 void Stop();
78 56
57 // Mirrors WorkerProcessLauncher::Delegate.
58 void LaunchProcess(WorkerProcessLauncher* event_handler);
59 void Send(IPC::Message* message);
60 void CloseChannel();
61 void KillProcess();
62
79 private: 63 private:
80 friend class base::RefCountedThreadSafe<Core>; 64 friend class base::RefCountedThreadSafe<Core>;
81 virtual ~Core(); 65 virtual ~Core();
82 66
67 // base::MessagePumpForIO::IOHandler implementation.
68 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
69 DWORD bytes_transferred,
70 DWORD error) OVERRIDE;
71
72 // IPC::Listener implementation.
73 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
74 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
75 virtual void OnChannelError() OVERRIDE;
76
77 // The actual implementation of LaunchProcess()
78 void DoLaunchProcess();
79
83 // Drains the completion port queue to make sure that all job object 80 // Drains the completion port queue to make sure that all job object
84 // notifications have been received. 81 // notifications have been received.
85 void DrainJobNotifications(); 82 void DrainJobNotifications();
86 83
87 // Notified that the completion port queue has been drained. 84 // Notified that the completion port queue has been drained.
88 void DrainJobNotificationsCompleted(); 85 void DrainJobNotificationsCompleted();
89 86
90 // Creates and initializes the job object that will sandbox the launched child 87 // Creates and initializes the job object that will sandbox the launched child
91 // processes. 88 // processes.
92 void InitializeJob(); 89 void InitializeJob(scoped_ptr<base::win::ScopedHandle> job);
93 90
94 // Notified that the job object initialization is complete. 91 // Notified that the job object initialization is complete.
95 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job); 92 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job);
96 93
97 // Called to process incoming job object notifications. 94 // Called when the number of processes running in the job reaches zero.
98 void OnJobNotification(DWORD message, DWORD pid); 95 void OnActiveProcessZero();
96
97 void ReportFatalError();
98 void ReportProcessLaunched(base::win::ScopedHandle worker_process);
99 99
100 // The task runner all public methods of this class should be called on. 100 // The task runner all public methods of this class should be called on.
101 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 101 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
102 102
103 // The task runner serving job object notifications. 103 // The task runner serving job object notifications.
104 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; 104 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
105 105
106 // Command line of the launched process.
107 scoped_ptr<CommandLine> target_command_;
108
109 // The server end of the IPC channel used to communicate to the worker 106 // The server end of the IPC channel used to communicate to the worker
110 // process. 107 // process.
111 scoped_ptr<IPC::ChannelProxy> channel_; 108 scoped_ptr<IPC::ChannelProxy> channel_;
112 109
113 // Security descriptor (as SDDL) to be applied to |channel_|. 110 // Security descriptor (as SDDL) to be applied to |channel_|.
114 std::string channel_security_; 111 std::string channel_security_;
115 112
113 WorkerProcessLauncher* event_handler_;
114
116 // Pointer to GetNamedPipeClientProcessId() API if it is available. 115 // Pointer to GetNamedPipeClientProcessId() API if it is available.
117 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*); 116 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*);
118 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_; 117 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_;
119 118
120 // The job object used to control the lifetime of child processes. 119 // The job object used to control the lifetime of child processes.
121 base::win::ScopedHandle job_; 120 base::win::ScopedHandle job_;
122 121
123 // True if the worker process should be launched elevated. 122 // True if the worker process should be launched elevated.
124 bool launch_elevated_; 123 bool launch_elevated_;
125 124
125 // True if a laucnh attemp is pending.
126 bool launch_pending_;
127
126 // The named pipe used as the transport by |channel_|. 128 // The named pipe used as the transport by |channel_|.
127 base::win::ScopedHandle pipe_; 129 base::win::ScopedHandle pipe_;
128 130
129 // A handle that becomes signalled once all processes associated with the job
130 // have been terminated.
131 base::win::ScopedHandle process_exit_event_;
132
133 // The token to be used to launch a process in a different session. 131 // The token to be used to launch a process in a different session.
134 base::win::ScopedHandle session_token_; 132 base::win::ScopedHandle session_token_;
135 133
136 // True if Stop() has been called. 134 // Command line of the launched process.
137 bool stopping_; 135 scoped_ptr<CommandLine> target_command_;
138 136
139 // The handle of the worker process, if launched. 137 // The handle of the worker process, if launched.
140 base::win::ScopedHandle worker_process_; 138 base::win::ScopedHandle worker_process_;
141 139
142 DISALLOW_COPY_AND_ASSIGN(Core); 140 DISALLOW_COPY_AND_ASSIGN(Core);
143 }; 141 };
144 142
145 WtsSessionProcessDelegate::Core::Core( 143 WtsSessionProcessDelegate::Core::Core(
146 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
147 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 144 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
148 scoped_ptr<CommandLine> target_command, 145 scoped_ptr<CommandLine> target_command,
149 bool launch_elevated, 146 bool launch_elevated,
150 const std::string& channel_security) 147 const std::string& channel_security)
151 : main_task_runner_(main_task_runner), 148 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
152 io_task_runner_(io_task_runner), 149 io_task_runner_(io_task_runner),
153 target_command_(target_command.Pass()),
154 channel_security_(channel_security), 150 channel_security_(channel_security),
151 event_handler_(NULL),
155 get_named_pipe_client_pid_(NULL), 152 get_named_pipe_client_pid_(NULL),
156 launch_elevated_(launch_elevated), 153 launch_elevated_(launch_elevated),
157 stopping_(false) { 154 launch_pending_(false),
158 DCHECK(main_task_runner_->BelongsToCurrentThread()); 155 target_command_(target_command.Pass()) {
156 }
157
158 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) {
159 DCHECK(caller_task_runner_->BelongsToCurrentThread());
160
161 if (base::win::GetVersion() < base::win::VERSION_VISTA)
garykac 2013/05/15 23:24:30 The other |launch_elevated| code is set when versi
alexeypa (please no reviews) 2013/05/16 17:50:04 Yes, |< VISTA| is the right thing to do here.
162 launch_elevated_ = false;
163
164 if (launch_elevated_) {
165 // GetNamedPipeClientProcessId() is available starting from Vista.
garykac 2013/05/15 23:24:30 This seems to answer part of my question above. Bu
alexeypa (please no reviews) 2013/05/16 17:50:04 We launch the process directly in that case. So th
166 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
167 CHECK(kernel32 != NULL);
168
169 get_named_pipe_client_pid_ =
170 reinterpret_cast<GetNamedPipeClientProcessIdFn>(
171 GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
172 CHECK(get_named_pipe_client_pid_ != NULL);
173
174 ScopedHandle job;
175 job.Set(CreateJobObject(NULL, NULL));
176 if (!job.IsValid()) {
177 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
178 return false;
179 }
180
181 // Limit the number of active processes in the job to two (the helper
182 // process performing elevation and the worker process itself) and make sure
183 // that all processes will be killed once the job object is destroyed.
184 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
185 memset(&info, 0, sizeof(info));
186 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
187 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
188 info.BasicLimitInformation.ActiveProcessLimit = 2;
189 if (!SetInformationJobObject(job,
190 JobObjectExtendedLimitInformation,
191 &info,
192 sizeof(info))) {
193 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
194 return false;
195 }
196
197 // ScopedHandle is not compatible with base::Passed, so we wrap it to
198 // a scoped pointer.
199 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
200 *job_wrapper = job.Pass();
201
202 // To receive job object notifications the job object is registered with
203 // the completion port represented by |io_task_runner|. The registration has
204 // to be done on the I/O thread because
205 // MessageLoopForIO::RegisterJobObject() can only be called via
206 // MessageLoopForIO::current().
207 io_task_runner_->PostTask(
208 FROM_HERE,
209 base::Bind(&Core::InitializeJob, this, base::Passed(&job_wrapper)));
210 }
211
212 // Create a session token for the launched process.
213 return CreateSessionToken(session_id, &session_token_);
214 }
215
216 void WtsSessionProcessDelegate::Core::Stop() {
217 DCHECK(caller_task_runner_->BelongsToCurrentThread());
218
219 KillProcess();
220
221 // Drain the completion queue to make sure all job object notifications have
222 // been received.
223 DrainJobNotificationsCompleted();
224 }
225
226 void WtsSessionProcessDelegate::Core::LaunchProcess(
227 WorkerProcessLauncher* event_handler) {
228 DCHECK(caller_task_runner_->BelongsToCurrentThread());
229 DCHECK(!event_handler_);
230
231 event_handler_ = event_handler;
232 DoLaunchProcess();
233 }
234
235 void WtsSessionProcessDelegate::Core::Send(IPC::Message* message) {
236 DCHECK(caller_task_runner_->BelongsToCurrentThread());
237
238 if (channel_) {
239 channel_->Send(message);
240 } else {
241 delete message;
242 }
243 }
244
245 void WtsSessionProcessDelegate::Core::CloseChannel() {
246 DCHECK(caller_task_runner_->BelongsToCurrentThread());
247
248 channel_.reset();
249 pipe_.Close();
250 }
251
252 void WtsSessionProcessDelegate::Core::KillProcess() {
253 DCHECK(caller_task_runner_->BelongsToCurrentThread());
254
255 channel_.reset();
256 event_handler_ = NULL;
257 launch_pending_ = false;
258 pipe_.Close();
259
260 if (launch_elevated_) {
261 if (job_.IsValid())
262 TerminateJobObject(job_, CONTROL_C_EXIT);
263 } else {
264 if (worker_process_.IsValid())
265 TerminateProcess(worker_process_, CONTROL_C_EXIT);
266 }
267
268 worker_process_.Close();
269 }
270
271 WtsSessionProcessDelegate::Core::~Core() {
272 DCHECK(!channel_);
273 DCHECK(!event_handler_);
274 DCHECK(!pipe_.IsValid());
275 DCHECK(!worker_process_.IsValid());
159 } 276 }
160 277
161 void WtsSessionProcessDelegate::Core::OnIOCompleted( 278 void WtsSessionProcessDelegate::Core::OnIOCompleted(
162 base::MessagePumpForIO::IOContext* context, 279 base::MessagePumpForIO::IOContext* context,
163 DWORD bytes_transferred, 280 DWORD bytes_transferred,
164 DWORD error) { 281 DWORD error) {
165 DCHECK(io_task_runner_->BelongsToCurrentThread()); 282 DCHECK(io_task_runner_->BelongsToCurrentThread());
166 283
167 // |bytes_transferred| is used in job object notifications to supply 284 // |bytes_transferred| is used in job object notifications to supply
168 // the message ID; |context| carries process ID. 285 // the message ID; |context| carries process ID.
169 main_task_runner_->PostTask(FROM_HERE, base::Bind( 286 if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
170 &Core::OnJobNotification, this, bytes_transferred, 287 caller_task_runner_->PostTask(FROM_HERE,
171 reinterpret_cast<DWORD>(context))); 288 base::Bind(&Core::OnActiveProcessZero, this));
172 } 289 }
173 290 }
174 bool WtsSessionProcessDelegate::Core::Send(IPC::Message* message) { 291
175 DCHECK(main_task_runner_->BelongsToCurrentThread()); 292 bool WtsSessionProcessDelegate::Core::OnMessageReceived(
176 293 const IPC::Message& message) {
177 if (channel_) { 294 DCHECK(caller_task_runner_->BelongsToCurrentThread());
178 return channel_->Send(message); 295
179 } else { 296 return event_handler_->OnMessageReceived(message);
180 delete message; 297 }
181 return false; 298
182 } 299 void WtsSessionProcessDelegate::Core::OnChannelConnected(int32 peer_pid) {
183 } 300 DCHECK(caller_task_runner_->BelongsToCurrentThread());
184 301
185 void WtsSessionProcessDelegate::Core::CloseChannel() { 302 // Report the worker PID now if the worker process is launched indirectly.
186 DCHECK(main_task_runner_->BelongsToCurrentThread()); 303 // Note that in this case the pipe's security descriptor is the only
187 304 // protection against a malicious processed connecting to the pipe.
188 channel_.reset(); 305 if (launch_elevated_) {
189 pipe_.Close(); 306 DWORD pid;
190 } 307 if (!get_named_pipe_client_pid_(pipe_, &pid)) {
191 308 LOG_GETLASTERROR(ERROR) << "Failed to retrive PID of the client";
192 DWORD WtsSessionProcessDelegate::Core::GetProcessId() const { 309 ReportFatalError();
193 DWORD pid = 0; 310 return;
194 if (launch_elevated_ && pipe_.IsValid() && 311 }
195 get_named_pipe_client_pid_(pipe_, &pid)) { 312
196 return pid; 313 if (pid != static_cast<DWORD>(peer_pid)) {
197 } 314 LOG(ERROR) << "The actual client PID " << pid
198 315 << " does not match the one reported by the client: "
199 if (worker_process_.IsValid()) 316 << peer_pid;
200 return ::GetProcessId(worker_process_); 317 ReportFatalError();
201 318 return;
202 return 0; 319 }
203 } 320
204 321 DWORD desired_access =
205 bool WtsSessionProcessDelegate::Core::IsPermanentError( 322 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
206 int failure_count) const { 323 ScopedHandle worker_process(OpenProcess(desired_access, false, pid));
207 // Get exit code of the worker process if it is available. 324 if (!worker_process.IsValid()) {
208 DWORD exit_code = CONTROL_C_EXIT; 325 LOG_GETLASTERROR(ERROR) << "Failed to open process " << pid;
209 if (worker_process_.IsValid()) { 326 ReportFatalError();
210 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { 327 return;
211 LOG_GETLASTERROR(INFO) 328 }
212 << "Failed to query the exit code of the worker process"; 329
213 exit_code = CONTROL_C_EXIT; 330 ReportProcessLaunched(worker_process.Pass());
214 } 331 }
215 } 332
216 333 if (event_handler_)
217 // Stop trying to restart the worker process if it exited due to 334 event_handler_->OnChannelConnected(peer_pid);
218 // misconfiguration. 335 }
219 return (kMinPermanentErrorExitCode <= exit_code && 336
220 exit_code <= kMaxPermanentErrorExitCode); 337 void WtsSessionProcessDelegate::Core::OnChannelError() {
221 } 338 DCHECK(caller_task_runner_->BelongsToCurrentThread());
222 339
223 void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code) { 340 event_handler_->OnChannelError();
224 DCHECK(main_task_runner_->BelongsToCurrentThread()); 341 }
225 342
226 channel_.reset(); 343 void WtsSessionProcessDelegate::Core::DoLaunchProcess() {
227 pipe_.Close(); 344 DCHECK(caller_task_runner_->BelongsToCurrentThread());
228 345 DCHECK(!channel_);
229 if (launch_elevated_) { 346 DCHECK(!pipe_.IsValid());
230 if (job_.IsValid()) { 347 DCHECK(!worker_process_.IsValid());
231 TerminateJobObject(job_, exit_code);
232 }
233 } else {
234 if (worker_process_.IsValid()) {
235 TerminateProcess(worker_process_, exit_code);
236 }
237 }
238 }
239
240 bool WtsSessionProcessDelegate::Core::LaunchProcess(
241 IPC::Listener* delegate,
242 ScopedHandle* process_exit_event_out) {
243 DCHECK(main_task_runner_->BelongsToCurrentThread());
244 348
245 CommandLine command_line(target_command_->argv()); 349 CommandLine command_line(target_command_->argv());
246 if (launch_elevated_) { 350 if (launch_elevated_) {
247 // The job object is not ready. Retry starting the host process later. 351 // The job object is not ready. Retry starting the host process later.
248 if (!job_.IsValid()) { 352 if (!job_.IsValid()) {
249 LOG(ERROR) << "The job object is not ready yet."; 353 launch_pending_ = true;
250 return false; 354 return;
251 } 355 }
252 356
253 // Construct the helper binary name. 357 // Construct the helper binary name.
254 base::FilePath helper_binary; 358 base::FilePath helper_binary;
255 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) 359 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) {
256 return false; 360 ReportFatalError();
361 return;
362 }
257 363
258 // Create the command line passing the name of the IPC channel to use and 364 // Create the command line passing the name of the IPC channel to use and
259 // copying known switches from the caller's command line. 365 // copying known switches from the caller's command line.
260 command_line.SetProgram(helper_binary); 366 command_line.SetProgram(helper_binary);
261 command_line.AppendSwitchPath(kElevateSwitchName, 367 command_line.AppendSwitchPath(kElevateSwitchName,
262 target_command_->GetProgram()); 368 target_command_->GetProgram());
263
264 CHECK(ResetEvent(process_exit_event_));
265 } 369 }
266 370
267 // Create the server end of the IPC channel. 371 // Create the server end of the IPC channel.
268 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); 372 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
269 ScopedHandle pipe; 373 ScopedHandle pipe;
270 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) 374 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) {
271 return false; 375 ReportFatalError();
376 return;
377 }
272 378
273 // Wrap the pipe into an IPC channel. 379 // Wrap the pipe into an IPC channel.
274 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy( 380 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy(
275 IPC::ChannelHandle(pipe), 381 IPC::ChannelHandle(pipe),
276 IPC::Channel::MODE_SERVER, 382 IPC::Channel::MODE_SERVER,
277 delegate, 383 this,
278 io_task_runner_)); 384 io_task_runner_));
279 385
280 // Pass the name of the IPC channel to use. 386 // Pass the name of the IPC channel to use.
281 command_line.AppendSwitchNative(kDaemonPipeSwitchName, 387 command_line.AppendSwitchNative(kDaemonPipeSwitchName,
282 UTF8ToWide(channel_name)); 388 UTF8ToWide(channel_name));
283 389
284 // Try to launch the process. 390 // Try to launch the process.
285 ScopedHandle worker_process; 391 ScopedHandle worker_process;
286 ScopedHandle worker_thread; 392 ScopedHandle worker_thread;
287 if (!LaunchProcessWithToken(command_line.GetProgram(), 393 if (!LaunchProcessWithToken(command_line.GetProgram(),
288 command_line.GetCommandLineString(), 394 command_line.GetCommandLineString(),
289 session_token_, 395 session_token_,
290 NULL, 396 NULL,
291 NULL, 397 NULL,
292 false, 398 false,
293 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, 399 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
294 UTF8ToUTF16(kDefaultDesktopName).c_str(), 400 UTF8ToUTF16(kDefaultDesktopName).c_str(),
295 &worker_process, 401 &worker_process,
296 &worker_thread)) { 402 &worker_thread)) {
297 return false; 403 ReportFatalError();
404 return;
298 } 405 }
299 406
300 HANDLE local_process_exit_event;
301 if (launch_elevated_) { 407 if (launch_elevated_) {
302 if (!AssignProcessToJobObject(job_, worker_process)) { 408 if (!AssignProcessToJobObject(job_, worker_process)) {
303 LOG_GETLASTERROR(ERROR) 409 LOG_GETLASTERROR(ERROR)
304 << "Failed to assign the worker to the job object"; 410 << "Failed to assign the worker to the job object";
305 TerminateProcess(worker_process, CONTROL_C_EXIT); 411 ReportFatalError();
306 return false; 412 return;
307 } 413 }
308
309 local_process_exit_event = process_exit_event_;
310 } else {
311 worker_process_ = worker_process.Pass();
312 local_process_exit_event = worker_process_;
313 } 414 }
314 415
315 if (!ResumeThread(worker_thread)) { 416 if (!ResumeThread(worker_thread)) {
316 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread"; 417 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
317 KillProcess(CONTROL_C_EXIT); 418 ReportFatalError();
318 return false; 419 return;
319 }
320
321 // Return a handle that the caller can wait on to get notified when
322 // the process terminates.
323 ScopedHandle process_exit_event;
324 if (!DuplicateHandle(GetCurrentProcess(),
325 local_process_exit_event,
326 GetCurrentProcess(),
327 process_exit_event.Receive(),
328 SYNCHRONIZE,
329 FALSE,
330 0)) {
331 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
332 KillProcess(CONTROL_C_EXIT);
333 return false;
334 } 420 }
335 421
336 channel_ = channel.Pass(); 422 channel_ = channel.Pass();
337 pipe_ = pipe.Pass(); 423 pipe_ = pipe.Pass();
338 *process_exit_event_out = process_exit_event.Pass();
339 return true;
340 }
341 424
342 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) { 425 // Report success if the worker process is lauched directly. Otherwise, PID of
343 if (base::win::GetVersion() < base::win::VERSION_VISTA) 426 // the client connected to the pipe will be used later. See
344 launch_elevated_ = false; 427 // OnChannelConnected().
345 428 if (!launch_elevated_)
346 if (launch_elevated_) { 429 ReportProcessLaunched(worker_process.Pass());
347 // GetNamedPipeClientProcessId() is available starting from Vista.
348 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
349 CHECK(kernel32 != NULL);
350
351 get_named_pipe_client_pid_ =
352 reinterpret_cast<GetNamedPipeClientProcessIdFn>(
353 GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
354 CHECK(get_named_pipe_client_pid_ != NULL);
355
356 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
357 if (!process_exit_event_.IsValid()) {
358 LOG(ERROR) << "Failed to create a nameless event";
359 return false;
360 }
361
362 // To receive job object notifications the job object is registered with
363 // the completion port represented by |io_task_runner|. The registration has
364 // to be done on the I/O thread because
365 // MessageLoopForIO::RegisterJobObject() can only be called via
366 // MessageLoopForIO::current().
367 io_task_runner_->PostTask(FROM_HERE,
368 base::Bind(&Core::InitializeJob, this));
369 }
370
371 // Create a session token for the launched process.
372 return CreateSessionToken(session_id, &session_token_);
373 }
374
375 void WtsSessionProcessDelegate::Core::Stop() {
376 DCHECK(main_task_runner_->BelongsToCurrentThread());
377
378 if (!stopping_) {
379 stopping_ = true;
380
381 // Drain the completion queue to make sure all job object notifications have
382 // been received.
383 DrainJobNotificationsCompleted();
384 }
385 }
386
387 WtsSessionProcessDelegate::Core::~Core() {
388 } 430 }
389 431
390 void WtsSessionProcessDelegate::Core::DrainJobNotifications() { 432 void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
391 DCHECK(io_task_runner_->BelongsToCurrentThread()); 433 DCHECK(io_task_runner_->BelongsToCurrentThread());
392 434
393 // DrainJobNotifications() is posted after the job object is destroyed, so 435 // DrainJobNotifications() is posted after the job object is destroyed, so
394 // by this time all notifications from the job object have been processed 436 // by this time all notifications from the job object have been processed
395 // already. Let the main thread know that the queue has been drained. 437 // already. Let the main thread know that the queue has been drained.
396 main_task_runner_->PostTask(FROM_HERE, base::Bind( 438 caller_task_runner_->PostTask(FROM_HERE, base::Bind(
397 &Core::DrainJobNotificationsCompleted, this)); 439 &Core::DrainJobNotificationsCompleted, this));
398 } 440 }
399 441
400 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() { 442 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
401 DCHECK(main_task_runner_->BelongsToCurrentThread()); 443 DCHECK(caller_task_runner_->BelongsToCurrentThread());
402 444
403 if (job_.IsValid()) { 445 if (job_.IsValid()) {
404 job_.Close(); 446 job_.Close();
405 447
406 // Drain the completion queue to make sure all job object notification have 448 // Drain the completion queue to make sure all job object notification have
407 // been received. 449 // been received.
408 io_task_runner_->PostTask(FROM_HERE, base::Bind( 450 io_task_runner_->PostTask(FROM_HERE, base::Bind(
409 &Core::DrainJobNotifications, this)); 451 &Core::DrainJobNotifications, this));
410 } 452 }
411 } 453 }
412 454
413 void WtsSessionProcessDelegate::Core::InitializeJob() { 455 void WtsSessionProcessDelegate::Core::InitializeJob(
456 scoped_ptr<base::win::ScopedHandle> job) {
414 DCHECK(io_task_runner_->BelongsToCurrentThread()); 457 DCHECK(io_task_runner_->BelongsToCurrentThread());
415 458
416 ScopedHandle job;
417 job.Set(CreateJobObject(NULL, NULL));
418 if (!job.IsValid()) {
419 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
420 return;
421 }
422
423 // Limit the number of active processes in the job to two (the process
424 // performing elevation and the host) and make sure that all processes will be
425 // killed once the job object is destroyed.
426 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
427 memset(&info, 0, sizeof(info));
428 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
429 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
430 info.BasicLimitInformation.ActiveProcessLimit = 2;
431 if (!SetInformationJobObject(job,
432 JobObjectExtendedLimitInformation,
433 &info,
434 sizeof(info))) {
435 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
436 return;
437 }
438
439 // Register to receive job notifications via the I/O thread's completion port. 459 // Register to receive job notifications via the I/O thread's completion port.
440 if (!base::MessageLoopForIO::current()->RegisterJobObject(job, this)) { 460 if (!base::MessageLoopForIO::current()->RegisterJobObject(job->Get(), this)) {
441 LOG_GETLASTERROR(ERROR) 461 LOG_GETLASTERROR(ERROR)
442 << "Failed to associate the job object with a completion port"; 462 << "Failed to associate the job object with a completion port";
443 return; 463 return;
444 } 464 }
445 465
446 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
447 // pointer.
448 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
449 *job_wrapper = job.Pass();
450
451 // Let the main thread know that initialization is complete. 466 // Let the main thread know that initialization is complete.
452 main_task_runner_->PostTask(FROM_HERE, base::Bind( 467 caller_task_runner_->PostTask(FROM_HERE, base::Bind(
453 &Core::InitializeJobCompleted, this, base::Passed(&job_wrapper))); 468 &Core::InitializeJobCompleted, this, base::Passed(&job)));
454 } 469 }
455 470
456 void WtsSessionProcessDelegate::Core::InitializeJobCompleted( 471 void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
457 scoped_ptr<ScopedHandle> job) { 472 scoped_ptr<ScopedHandle> job) {
458 DCHECK(main_task_runner_->BelongsToCurrentThread()); 473 DCHECK(caller_task_runner_->BelongsToCurrentThread());
459 DCHECK(!job_.IsValid()); 474 DCHECK(!job_.IsValid());
460 475
461 job_ = job->Pass(); 476 job_ = job->Pass();
477
478 if (launch_pending_)
479 DoLaunchProcess();
462 } 480 }
463 481
464 void WtsSessionProcessDelegate::Core::OnJobNotification(DWORD message, 482 void WtsSessionProcessDelegate::Core::OnActiveProcessZero() {
465 DWORD pid) { 483 DCHECK(caller_task_runner_->BelongsToCurrentThread());
466 DCHECK(main_task_runner_->BelongsToCurrentThread());
467 484
468 switch (message) { 485 if (launch_pending_) {
469 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: 486 LOG(ERROR) << "The worker process exited before connecting via IPC.";
470 CHECK(SetEvent(process_exit_event_)); 487 launch_pending_ = false;
471 break; 488 ReportFatalError();
472
473 case JOB_OBJECT_MSG_NEW_PROCESS:
474 // We report the exit code of the worker process to be |CONTROL_C_EXIT|
475 // if we cannot get the actual exit code. So here we can safely ignore
476 // the error returned by OpenProcess().
477 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
478 break;
479 } 489 }
480 } 490 }
481 491
492 void WtsSessionProcessDelegate::Core::ReportFatalError() {
493 DCHECK(caller_task_runner_->BelongsToCurrentThread());
494
495 channel_.reset();
496 pipe_.Close();
497
498 WorkerProcessLauncher* event_handler = event_handler_;
499 event_handler_ = NULL;
500 event_handler->OnFatalError();
501 }
502
503 void WtsSessionProcessDelegate::Core::ReportProcessLaunched(
504 base::win::ScopedHandle worker_process) {
505 DCHECK(caller_task_runner_->BelongsToCurrentThread());
506 DCHECK(!worker_process_.IsValid());
507
508 worker_process_ = worker_process.Pass();
509
510 // Report a handle that can be used to wait for the worker process completion,
511 // query information about the process and duplicate handles.
512 DWORD desired_access =
513 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
514 ScopedHandle limited_handle;
515 if (!DuplicateHandle(GetCurrentProcess(),
516 worker_process_,
517 GetCurrentProcess(),
518 limited_handle.Receive(),
519 desired_access,
520 FALSE,
521 0)) {
522 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
523 ReportFatalError();
524 return;
525 }
526
527 event_handler_->OnProcessLaunched(limited_handle.Pass());
528 }
529
482 WtsSessionProcessDelegate::WtsSessionProcessDelegate( 530 WtsSessionProcessDelegate::WtsSessionProcessDelegate(
483 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
484 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 531 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
485 scoped_ptr<CommandLine> target_command, 532 scoped_ptr<CommandLine> target_command,
486 uint32 session_id,
487 bool launch_elevated, 533 bool launch_elevated,
488 const std::string& channel_security) { 534 const std::string& channel_security) {
489 core_ = new Core(main_task_runner, io_task_runner, target_command.Pass(), 535 core_ = new Core(io_task_runner,
490 launch_elevated, channel_security); 536 target_command.Pass(),
491 if (!core_->Initialize(session_id)) { 537 launch_elevated,
492 core_->Stop(); 538 channel_security);
493 core_ = NULL;
494 }
495 } 539 }
496 540
497 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() { 541 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
498 if (core_) { 542 core_->Stop();
499 core_->Stop();
500 core_ = NULL;
501 }
502 } 543 }
503 544
504 bool WtsSessionProcessDelegate::Send(IPC::Message* message) { 545 bool WtsSessionProcessDelegate::Initialize(uint32 session_id) {
505 return core_->Send(message); 546 return core_->Initialize(session_id);
547 }
548
549 void WtsSessionProcessDelegate::LaunchProcess(
550 WorkerProcessLauncher* event_handler) {
551 core_->LaunchProcess(event_handler);
552 }
553
554 void WtsSessionProcessDelegate::Send(IPC::Message* message) {
555 core_->Send(message);
506 } 556 }
507 557
508 void WtsSessionProcessDelegate::CloseChannel() { 558 void WtsSessionProcessDelegate::CloseChannel() {
509 if (core_) 559 core_->CloseChannel();
510 core_->CloseChannel();
511 } 560 }
512 561
513 DWORD WtsSessionProcessDelegate::GetProcessId() const { 562 void WtsSessionProcessDelegate::KillProcess() {
514 if (!core_) 563 core_->KillProcess();
515 return 0;
516
517 return core_->GetProcessId();
518 }
519
520 bool WtsSessionProcessDelegate::IsPermanentError(int failure_count) const {
521 if (!core_)
522 return false;
523
524 return core_->IsPermanentError(failure_count);
525 }
526
527 void WtsSessionProcessDelegate::KillProcess(DWORD exit_code) {
528 if (core_)
529 core_->KillProcess(exit_code);
530 }
531
532 bool WtsSessionProcessDelegate::LaunchProcess(
533 IPC::Listener* delegate,
534 base::win::ScopedHandle* process_exit_event_out) {
535 if (!core_)
536 return false;
537
538 return core_->LaunchProcess(delegate, process_exit_event_out);
539 } 564 }
540 565
541 } // namespace remoting 566 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698