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

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: rebased 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
« no previous file with comments | « remoting/host/win/wts_session_process_delegate.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // Windows XP does not support elevation.
162 if (base::win::GetVersion() < base::win::VERSION_VISTA)
163 launch_elevated_ = false;
164
165 if (launch_elevated_) {
166 // GetNamedPipeClientProcessId() is available starting from Vista.
167 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
168 CHECK(kernel32 != NULL);
169
170 get_named_pipe_client_pid_ =
171 reinterpret_cast<GetNamedPipeClientProcessIdFn>(
172 GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
173 CHECK(get_named_pipe_client_pid_ != NULL);
174
175 ScopedHandle job;
176 job.Set(CreateJobObject(NULL, NULL));
177 if (!job.IsValid()) {
178 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
179 return false;
180 }
181
182 // Limit the number of active processes in the job to two (the helper
183 // process performing elevation and the worker process itself) and make sure
184 // that all processes will be killed once the job object is destroyed.
185 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
186 memset(&info, 0, sizeof(info));
187 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
188 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
189 info.BasicLimitInformation.ActiveProcessLimit = 2;
190 if (!SetInformationJobObject(job,
191 JobObjectExtendedLimitInformation,
192 &info,
193 sizeof(info))) {
194 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
195 return false;
196 }
197
198 // ScopedHandle is not compatible with base::Passed, so we wrap it to
199 // a scoped pointer.
200 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
201 *job_wrapper = job.Pass();
202
203 // To receive job object notifications the job object is registered with
204 // the completion port represented by |io_task_runner|. The registration has
205 // to be done on the I/O thread because
206 // MessageLoopForIO::RegisterJobObject() can only be called via
207 // MessageLoopForIO::current().
208 io_task_runner_->PostTask(
209 FROM_HERE,
210 base::Bind(&Core::InitializeJob, this, base::Passed(&job_wrapper)));
211 }
212
213 // Create a session token for the launched process.
214 return CreateSessionToken(session_id, &session_token_);
215 }
216
217 void WtsSessionProcessDelegate::Core::Stop() {
218 DCHECK(caller_task_runner_->BelongsToCurrentThread());
219
220 KillProcess();
221
222 // Drain the completion queue to make sure all job object notifications have
223 // been received.
224 DrainJobNotificationsCompleted();
225 }
226
227 void WtsSessionProcessDelegate::Core::LaunchProcess(
228 WorkerProcessLauncher* event_handler) {
229 DCHECK(caller_task_runner_->BelongsToCurrentThread());
230 DCHECK(!event_handler_);
231
232 event_handler_ = event_handler;
233 DoLaunchProcess();
234 }
235
236 void WtsSessionProcessDelegate::Core::Send(IPC::Message* message) {
237 DCHECK(caller_task_runner_->BelongsToCurrentThread());
238
239 if (channel_) {
240 channel_->Send(message);
241 } else {
242 delete message;
243 }
244 }
245
246 void WtsSessionProcessDelegate::Core::CloseChannel() {
247 DCHECK(caller_task_runner_->BelongsToCurrentThread());
248
249 channel_.reset();
250 pipe_.Close();
251 }
252
253 void WtsSessionProcessDelegate::Core::KillProcess() {
254 DCHECK(caller_task_runner_->BelongsToCurrentThread());
255
256 channel_.reset();
257 event_handler_ = NULL;
258 launch_pending_ = false;
259 pipe_.Close();
260
261 if (launch_elevated_) {
262 if (job_.IsValid())
263 TerminateJobObject(job_, CONTROL_C_EXIT);
264 } else {
265 if (worker_process_.IsValid())
266 TerminateProcess(worker_process_, CONTROL_C_EXIT);
267 }
268
269 worker_process_.Close();
270 }
271
272 WtsSessionProcessDelegate::Core::~Core() {
273 DCHECK(!channel_);
274 DCHECK(!event_handler_);
275 DCHECK(!pipe_.IsValid());
276 DCHECK(!worker_process_.IsValid());
159 } 277 }
160 278
161 void WtsSessionProcessDelegate::Core::OnIOCompleted( 279 void WtsSessionProcessDelegate::Core::OnIOCompleted(
162 base::MessagePumpForIO::IOContext* context, 280 base::MessagePumpForIO::IOContext* context,
163 DWORD bytes_transferred, 281 DWORD bytes_transferred,
164 DWORD error) { 282 DWORD error) {
165 DCHECK(io_task_runner_->BelongsToCurrentThread()); 283 DCHECK(io_task_runner_->BelongsToCurrentThread());
166 284
167 // |bytes_transferred| is used in job object notifications to supply 285 // |bytes_transferred| is used in job object notifications to supply
168 // the message ID; |context| carries process ID. 286 // the message ID; |context| carries process ID.
169 main_task_runner_->PostTask(FROM_HERE, base::Bind( 287 if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
170 &Core::OnJobNotification, this, bytes_transferred, 288 caller_task_runner_->PostTask(FROM_HERE,
171 reinterpret_cast<DWORD>(context))); 289 base::Bind(&Core::OnActiveProcessZero, this));
172 } 290 }
173 291 }
174 bool WtsSessionProcessDelegate::Core::Send(IPC::Message* message) { 292
175 DCHECK(main_task_runner_->BelongsToCurrentThread()); 293 bool WtsSessionProcessDelegate::Core::OnMessageReceived(
176 294 const IPC::Message& message) {
177 if (channel_) { 295 DCHECK(caller_task_runner_->BelongsToCurrentThread());
178 return channel_->Send(message); 296
179 } else { 297 return event_handler_->OnMessageReceived(message);
180 delete message; 298 }
181 return false; 299
182 } 300 void WtsSessionProcessDelegate::Core::OnChannelConnected(int32 peer_pid) {
183 } 301 DCHECK(caller_task_runner_->BelongsToCurrentThread());
184 302
185 void WtsSessionProcessDelegate::Core::CloseChannel() { 303 // Report the worker PID now if the worker process is launched indirectly.
186 DCHECK(main_task_runner_->BelongsToCurrentThread()); 304 // Note that in this case the pipe's security descriptor is the only
187 305 // protection against a malicious processed connecting to the pipe.
188 channel_.reset(); 306 if (launch_elevated_) {
189 pipe_.Close(); 307 DWORD pid;
190 } 308 if (!get_named_pipe_client_pid_(pipe_, &pid)) {
191 309 LOG_GETLASTERROR(ERROR) << "Failed to retrive PID of the client";
192 DWORD WtsSessionProcessDelegate::Core::GetProcessId() const { 310 ReportFatalError();
193 DWORD pid = 0; 311 return;
194 if (launch_elevated_ && pipe_.IsValid() && 312 }
195 get_named_pipe_client_pid_(pipe_, &pid)) { 313
196 return pid; 314 if (pid != static_cast<DWORD>(peer_pid)) {
197 } 315 LOG(ERROR) << "The actual client PID " << pid
198 316 << " does not match the one reported by the client: "
199 if (worker_process_.IsValid()) 317 << peer_pid;
200 return ::GetProcessId(worker_process_); 318 ReportFatalError();
201 319 return;
202 return 0; 320 }
203 } 321
204 322 DWORD desired_access =
205 bool WtsSessionProcessDelegate::Core::IsPermanentError( 323 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
206 int failure_count) const { 324 ScopedHandle worker_process(OpenProcess(desired_access, false, pid));
207 // Get exit code of the worker process if it is available. 325 if (!worker_process.IsValid()) {
208 DWORD exit_code = CONTROL_C_EXIT; 326 LOG_GETLASTERROR(ERROR) << "Failed to open process " << pid;
209 if (worker_process_.IsValid()) { 327 ReportFatalError();
210 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { 328 return;
211 LOG_GETLASTERROR(INFO) 329 }
212 << "Failed to query the exit code of the worker process"; 330
213 exit_code = CONTROL_C_EXIT; 331 ReportProcessLaunched(worker_process.Pass());
214 } 332 }
215 } 333
216 334 if (event_handler_)
217 // Stop trying to restart the worker process if it exited due to 335 event_handler_->OnChannelConnected(peer_pid);
218 // misconfiguration. 336 }
219 return (kMinPermanentErrorExitCode <= exit_code && 337
220 exit_code <= kMaxPermanentErrorExitCode); 338 void WtsSessionProcessDelegate::Core::OnChannelError() {
221 } 339 DCHECK(caller_task_runner_->BelongsToCurrentThread());
222 340
223 void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code) { 341 event_handler_->OnChannelError();
224 DCHECK(main_task_runner_->BelongsToCurrentThread()); 342 }
225 343
226 channel_.reset(); 344 void WtsSessionProcessDelegate::Core::DoLaunchProcess() {
227 pipe_.Close(); 345 DCHECK(caller_task_runner_->BelongsToCurrentThread());
228 346 DCHECK(!channel_);
229 if (launch_elevated_) { 347 DCHECK(!pipe_.IsValid());
230 if (job_.IsValid()) { 348 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 349
245 CommandLine command_line(target_command_->argv()); 350 CommandLine command_line(target_command_->argv());
246 if (launch_elevated_) { 351 if (launch_elevated_) {
247 // The job object is not ready. Retry starting the host process later. 352 // The job object is not ready. Retry starting the host process later.
248 if (!job_.IsValid()) { 353 if (!job_.IsValid()) {
249 LOG(ERROR) << "The job object is not ready yet."; 354 launch_pending_ = true;
250 return false; 355 return;
251 } 356 }
252 357
253 // Construct the helper binary name. 358 // Construct the helper binary name.
254 base::FilePath helper_binary; 359 base::FilePath helper_binary;
255 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) 360 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) {
256 return false; 361 ReportFatalError();
362 return;
363 }
257 364
258 // Create the command line passing the name of the IPC channel to use and 365 // Create the command line passing the name of the IPC channel to use and
259 // copying known switches from the caller's command line. 366 // copying known switches from the caller's command line.
260 command_line.SetProgram(helper_binary); 367 command_line.SetProgram(helper_binary);
261 command_line.AppendSwitchPath(kElevateSwitchName, 368 command_line.AppendSwitchPath(kElevateSwitchName,
262 target_command_->GetProgram()); 369 target_command_->GetProgram());
263
264 CHECK(ResetEvent(process_exit_event_));
265 } 370 }
266 371
267 // Create the server end of the IPC channel. 372 // Create the server end of the IPC channel.
268 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); 373 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
269 ScopedHandle pipe; 374 ScopedHandle pipe;
270 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) 375 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) {
271 return false; 376 ReportFatalError();
377 return;
378 }
272 379
273 // Wrap the pipe into an IPC channel. 380 // Wrap the pipe into an IPC channel.
274 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy( 381 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy(
275 IPC::ChannelHandle(pipe), 382 IPC::ChannelHandle(pipe),
276 IPC::Channel::MODE_SERVER, 383 IPC::Channel::MODE_SERVER,
277 delegate, 384 this,
278 io_task_runner_)); 385 io_task_runner_));
279 386
280 // Pass the name of the IPC channel to use. 387 // Pass the name of the IPC channel to use.
281 command_line.AppendSwitchNative(kDaemonPipeSwitchName, 388 command_line.AppendSwitchNative(kDaemonPipeSwitchName,
282 UTF8ToWide(channel_name)); 389 UTF8ToWide(channel_name));
283 390
284 // Try to launch the process. 391 // Try to launch the process.
285 ScopedHandle worker_process; 392 ScopedHandle worker_process;
286 ScopedHandle worker_thread; 393 ScopedHandle worker_thread;
287 if (!LaunchProcessWithToken(command_line.GetProgram(), 394 if (!LaunchProcessWithToken(command_line.GetProgram(),
288 command_line.GetCommandLineString(), 395 command_line.GetCommandLineString(),
289 session_token_, 396 session_token_,
290 NULL, 397 NULL,
291 NULL, 398 NULL,
292 false, 399 false,
293 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, 400 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
294 UTF8ToUTF16(kDefaultDesktopName).c_str(), 401 UTF8ToUTF16(kDefaultDesktopName).c_str(),
295 &worker_process, 402 &worker_process,
296 &worker_thread)) { 403 &worker_thread)) {
297 return false; 404 ReportFatalError();
405 return;
298 } 406 }
299 407
300 HANDLE local_process_exit_event;
301 if (launch_elevated_) { 408 if (launch_elevated_) {
302 if (!AssignProcessToJobObject(job_, worker_process)) { 409 if (!AssignProcessToJobObject(job_, worker_process)) {
303 LOG_GETLASTERROR(ERROR) 410 LOG_GETLASTERROR(ERROR)
304 << "Failed to assign the worker to the job object"; 411 << "Failed to assign the worker to the job object";
305 TerminateProcess(worker_process, CONTROL_C_EXIT); 412 ReportFatalError();
306 return false; 413 return;
307 } 414 }
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 } 415 }
314 416
315 if (!ResumeThread(worker_thread)) { 417 if (!ResumeThread(worker_thread)) {
316 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread"; 418 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
317 KillProcess(CONTROL_C_EXIT); 419 ReportFatalError();
318 return false; 420 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 } 421 }
335 422
336 channel_ = channel.Pass(); 423 channel_ = channel.Pass();
337 pipe_ = pipe.Pass(); 424 pipe_ = pipe.Pass();
338 *process_exit_event_out = process_exit_event.Pass();
339 return true;
340 }
341 425
342 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) { 426 // Report success if the worker process is lauched directly. Otherwise, PID of
343 if (base::win::GetVersion() < base::win::VERSION_VISTA) 427 // the client connected to the pipe will be used later. See
344 launch_elevated_ = false; 428 // OnChannelConnected().
345 429 if (!launch_elevated_)
346 if (launch_elevated_) { 430 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 } 431 }
389 432
390 void WtsSessionProcessDelegate::Core::DrainJobNotifications() { 433 void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
391 DCHECK(io_task_runner_->BelongsToCurrentThread()); 434 DCHECK(io_task_runner_->BelongsToCurrentThread());
392 435
393 // DrainJobNotifications() is posted after the job object is destroyed, so 436 // DrainJobNotifications() is posted after the job object is destroyed, so
394 // by this time all notifications from the job object have been processed 437 // 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. 438 // already. Let the main thread know that the queue has been drained.
396 main_task_runner_->PostTask(FROM_HERE, base::Bind( 439 caller_task_runner_->PostTask(FROM_HERE, base::Bind(
397 &Core::DrainJobNotificationsCompleted, this)); 440 &Core::DrainJobNotificationsCompleted, this));
398 } 441 }
399 442
400 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() { 443 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
401 DCHECK(main_task_runner_->BelongsToCurrentThread()); 444 DCHECK(caller_task_runner_->BelongsToCurrentThread());
402 445
403 if (job_.IsValid()) { 446 if (job_.IsValid()) {
404 job_.Close(); 447 job_.Close();
405 448
406 // Drain the completion queue to make sure all job object notification have 449 // Drain the completion queue to make sure all job object notification have
407 // been received. 450 // been received.
408 io_task_runner_->PostTask(FROM_HERE, base::Bind( 451 io_task_runner_->PostTask(FROM_HERE, base::Bind(
409 &Core::DrainJobNotifications, this)); 452 &Core::DrainJobNotifications, this));
410 } 453 }
411 } 454 }
412 455
413 void WtsSessionProcessDelegate::Core::InitializeJob() { 456 void WtsSessionProcessDelegate::Core::InitializeJob(
457 scoped_ptr<base::win::ScopedHandle> job) {
414 DCHECK(io_task_runner_->BelongsToCurrentThread()); 458 DCHECK(io_task_runner_->BelongsToCurrentThread());
415 459
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. 460 // Register to receive job notifications via the I/O thread's completion port.
440 if (!base::MessageLoopForIO::current()->RegisterJobObject(job, this)) { 461 if (!base::MessageLoopForIO::current()->RegisterJobObject(job->Get(), this)) {
441 LOG_GETLASTERROR(ERROR) 462 LOG_GETLASTERROR(ERROR)
442 << "Failed to associate the job object with a completion port"; 463 << "Failed to associate the job object with a completion port";
443 return; 464 return;
444 } 465 }
445 466
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. 467 // Let the main thread know that initialization is complete.
452 main_task_runner_->PostTask(FROM_HERE, base::Bind( 468 caller_task_runner_->PostTask(FROM_HERE, base::Bind(
453 &Core::InitializeJobCompleted, this, base::Passed(&job_wrapper))); 469 &Core::InitializeJobCompleted, this, base::Passed(&job)));
454 } 470 }
455 471
456 void WtsSessionProcessDelegate::Core::InitializeJobCompleted( 472 void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
457 scoped_ptr<ScopedHandle> job) { 473 scoped_ptr<ScopedHandle> job) {
458 DCHECK(main_task_runner_->BelongsToCurrentThread()); 474 DCHECK(caller_task_runner_->BelongsToCurrentThread());
459 DCHECK(!job_.IsValid()); 475 DCHECK(!job_.IsValid());
460 476
461 job_ = job->Pass(); 477 job_ = job->Pass();
478
479 if (launch_pending_)
480 DoLaunchProcess();
462 } 481 }
463 482
464 void WtsSessionProcessDelegate::Core::OnJobNotification(DWORD message, 483 void WtsSessionProcessDelegate::Core::OnActiveProcessZero() {
465 DWORD pid) { 484 DCHECK(caller_task_runner_->BelongsToCurrentThread());
466 DCHECK(main_task_runner_->BelongsToCurrentThread());
467 485
468 switch (message) { 486 if (launch_pending_) {
469 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: 487 LOG(ERROR) << "The worker process exited before connecting via IPC.";
470 CHECK(SetEvent(process_exit_event_)); 488 launch_pending_ = false;
471 break; 489 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 } 490 }
480 } 491 }
481 492
493 void WtsSessionProcessDelegate::Core::ReportFatalError() {
494 DCHECK(caller_task_runner_->BelongsToCurrentThread());
495
496 channel_.reset();
497 pipe_.Close();
498
499 WorkerProcessLauncher* event_handler = event_handler_;
500 event_handler_ = NULL;
501 event_handler->OnFatalError();
502 }
503
504 void WtsSessionProcessDelegate::Core::ReportProcessLaunched(
505 base::win::ScopedHandle worker_process) {
506 DCHECK(caller_task_runner_->BelongsToCurrentThread());
507 DCHECK(!worker_process_.IsValid());
508
509 worker_process_ = worker_process.Pass();
510
511 // Report a handle that can be used to wait for the worker process completion,
512 // query information about the process and duplicate handles.
513 DWORD desired_access =
514 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
515 ScopedHandle limited_handle;
516 if (!DuplicateHandle(GetCurrentProcess(),
517 worker_process_,
518 GetCurrentProcess(),
519 limited_handle.Receive(),
520 desired_access,
521 FALSE,
522 0)) {
523 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
524 ReportFatalError();
525 return;
526 }
527
528 event_handler_->OnProcessLaunched(limited_handle.Pass());
529 }
530
482 WtsSessionProcessDelegate::WtsSessionProcessDelegate( 531 WtsSessionProcessDelegate::WtsSessionProcessDelegate(
483 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
484 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 532 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
485 scoped_ptr<CommandLine> target_command, 533 scoped_ptr<CommandLine> target_command,
486 uint32 session_id,
487 bool launch_elevated, 534 bool launch_elevated,
488 const std::string& channel_security) { 535 const std::string& channel_security) {
489 core_ = new Core(main_task_runner, io_task_runner, target_command.Pass(), 536 core_ = new Core(io_task_runner,
490 launch_elevated, channel_security); 537 target_command.Pass(),
491 if (!core_->Initialize(session_id)) { 538 launch_elevated,
492 core_->Stop(); 539 channel_security);
493 core_ = NULL;
494 }
495 } 540 }
496 541
497 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() { 542 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
498 if (core_) { 543 core_->Stop();
499 core_->Stop();
500 core_ = NULL;
501 }
502 } 544 }
503 545
504 bool WtsSessionProcessDelegate::Send(IPC::Message* message) { 546 bool WtsSessionProcessDelegate::Initialize(uint32 session_id) {
505 return core_->Send(message); 547 return core_->Initialize(session_id);
548 }
549
550 void WtsSessionProcessDelegate::LaunchProcess(
551 WorkerProcessLauncher* event_handler) {
552 core_->LaunchProcess(event_handler);
553 }
554
555 void WtsSessionProcessDelegate::Send(IPC::Message* message) {
556 core_->Send(message);
506 } 557 }
507 558
508 void WtsSessionProcessDelegate::CloseChannel() { 559 void WtsSessionProcessDelegate::CloseChannel() {
509 if (core_) 560 core_->CloseChannel();
510 core_->CloseChannel();
511 } 561 }
512 562
513 DWORD WtsSessionProcessDelegate::GetProcessId() const { 563 void WtsSessionProcessDelegate::KillProcess() {
514 if (!core_) 564 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 } 565 }
540 566
541 } // namespace remoting 567 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/win/wts_session_process_delegate.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698