Move generic_handler.* to content\browser\webui since it's needed by all webui pages.
[chromium-blink-merge.git] / remoting / host / win / wts_session_process_delegate.cc
blob88ff519b3e89c3272d883b31986898969276da14
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 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions.
8 #include "remoting/host/win/wts_session_process_delegate.h"
10 #include "base/base_switches.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/file_path.h"
15 #include "base/file_util.h"
16 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/message_loop.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/utf_string_conversions.h"
21 #include "base/win/scoped_handle.h"
22 #include "base/win/windows_version.h"
23 #include "ipc/ipc_channel.h"
24 #include "ipc/ipc_channel_proxy.h"
25 #include "ipc/ipc_message.h"
26 #include "remoting/host/host_exit_codes.h"
27 #include "remoting/host/ipc_constants.h"
28 #include "remoting/host/win/launch_process_with_token.h"
29 #include "remoting/host/win/worker_process_launcher.h"
30 #include "remoting/host/win/wts_console_monitor.h"
31 #include "remoting/host/worker_process_ipc_delegate.h"
33 using base::win::ScopedHandle;
35 // Name of the default session desktop.
36 const char kDefaultDesktopName[] = "winsta0\\default";
38 const char kElevateSwitchName[] = "elevate";
40 // The command line parameters that should be copied from the service's command
41 // line to the host process.
42 const char* kCopiedSwitchNames[] = {
43 "host-config", switches::kV, switches::kVModule };
45 namespace remoting {
47 // A private class actually implementing the functionality provided by
48 // |WtsSessionProcessDelegate|. This class is ref-counted and implements
49 // asynchronous fire-and-forget shutdown.
50 class WtsSessionProcessDelegate::Core
51 : public base::RefCountedThreadSafe<WtsSessionProcessDelegate::Core>,
52 public base::MessagePumpForIO::IOHandler,
53 public WorkerProcessLauncher::Delegate {
54 public:
55 // The caller must ensure that |delegate| remains valid at least until
56 // Stop() method has been called.
57 Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
58 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
59 const FilePath& binary_path,
60 bool launch_elevated,
61 const std::string& channel_security);
63 // base::MessagePumpForIO::IOHandler implementation.
64 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
65 DWORD bytes_transferred,
66 DWORD error) OVERRIDE;
68 // IPC::Sender implementation.
69 virtual bool Send(IPC::Message* message) OVERRIDE;
71 // WorkerProcessLauncher::Delegate implementation.
72 virtual DWORD GetProcessId() const OVERRIDE;
73 virtual bool IsPermanentError(int failure_count) const OVERRIDE;
74 virtual void KillProcess(DWORD exit_code) OVERRIDE;
75 virtual bool LaunchProcess(
76 IPC::Listener* delegate,
77 base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
79 // Initializes the object returning true on success.
80 bool Initialize(uint32 session_id);
82 // Stops the object asynchronously.
83 void Stop();
85 private:
86 friend class base::RefCountedThreadSafe<Core>;
87 virtual ~Core();
89 // Drains the completion port queue to make sure that all job object
90 // notifications have been received.
91 void DrainJobNotifications();
93 // Notified that the completion port queue has been drained.
94 void DrainJobNotificationsCompleted();
96 // Creates and initializes the job object that will sandbox the launched child
97 // processes.
98 void InitializeJob();
100 // Notified that the job object initialization is complete.
101 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job);
103 // Called to process incoming job object notifications.
104 void OnJobNotification(DWORD message, DWORD pid);
106 // The task runner all public methods of this class should be called on.
107 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
109 // The task runner serving job object notifications.
110 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
112 // Path to the worker process binary.
113 FilePath binary_path_;
115 // The server end of the IPC channel used to communicate to the worker
116 // process.
117 scoped_ptr<IPC::ChannelProxy> channel_;
119 // Security descriptor (as SDDL) to be applied to |channel_|.
120 std::string channel_security_;
122 // Pointer to GetNamedPipeClientProcessId() API if it is available.
123 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*);
124 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_;
126 // The job object used to control the lifetime of child processes.
127 base::win::ScopedHandle job_;
129 // True if the worker process should be launched elevated.
130 bool launch_elevated_;
132 // The named pipe used as the transport by |channel_|.
133 base::win::ScopedHandle pipe_;
135 // A handle that becomes signalled once all processes associated with the job
136 // have been terminated.
137 base::win::ScopedHandle process_exit_event_;
139 // The token to be used to launch a process in a different session.
140 base::win::ScopedHandle session_token_;
142 // True if Stop() has been called.
143 bool stopping_;
145 // The handle of the worker process, if launched.
146 base::win::ScopedHandle worker_process_;
148 DISALLOW_COPY_AND_ASSIGN(Core);
151 WtsSessionProcessDelegate::Core::Core(
152 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
153 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
154 const FilePath& binary_path,
155 bool launch_elevated,
156 const std::string& channel_security)
157 : main_task_runner_(main_task_runner),
158 io_task_runner_(io_task_runner),
159 binary_path_(binary_path),
160 channel_security_(channel_security),
161 get_named_pipe_client_pid_(NULL),
162 launch_elevated_(launch_elevated),
163 stopping_(false) {
164 DCHECK(main_task_runner_->BelongsToCurrentThread());
167 void WtsSessionProcessDelegate::Core::OnIOCompleted(
168 base::MessagePumpForIO::IOContext* context,
169 DWORD bytes_transferred,
170 DWORD error) {
171 DCHECK(io_task_runner_->BelongsToCurrentThread());
173 // |bytes_transferred| is used in job object notifications to supply
174 // the message ID; |context| carries process ID.
175 main_task_runner_->PostTask(FROM_HERE, base::Bind(
176 &Core::OnJobNotification, this, bytes_transferred,
177 reinterpret_cast<DWORD>(context)));
180 bool WtsSessionProcessDelegate::Core::Send(IPC::Message* message) {
181 DCHECK(main_task_runner_->BelongsToCurrentThread());
183 if (channel_.get()) {
184 return channel_->Send(message);
185 } else {
186 delete message;
187 return false;
191 DWORD WtsSessionProcessDelegate::Core::GetProcessId() const {
192 DWORD pid = 0;
193 if (launch_elevated_ && pipe_.IsValid() &&
194 get_named_pipe_client_pid_(pipe_, &pid)) {
195 return pid;
198 if (worker_process_.IsValid())
199 return ::GetProcessId(worker_process_);
201 return 0;
204 bool WtsSessionProcessDelegate::Core::IsPermanentError(
205 int failure_count) const {
206 // Get exit code of the worker process if it is available.
207 DWORD exit_code = CONTROL_C_EXIT;
208 if (worker_process_.IsValid()) {
209 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
210 LOG_GETLASTERROR(INFO)
211 << "Failed to query the exit code of the worker process";
212 exit_code = CONTROL_C_EXIT;
216 // Stop trying to restart the worker process if it exited due to
217 // misconfiguration.
218 return (kMinPermanentErrorExitCode <= exit_code &&
219 exit_code <= kMaxPermanentErrorExitCode);
222 void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code) {
223 DCHECK(main_task_runner_->BelongsToCurrentThread());
225 channel_.reset();
226 pipe_.Close();
228 if (launch_elevated_) {
229 if (job_.IsValid()) {
230 TerminateJobObject(job_, exit_code);
232 } else {
233 if (worker_process_.IsValid()) {
234 TerminateProcess(worker_process_, exit_code);
239 bool WtsSessionProcessDelegate::Core::LaunchProcess(
240 IPC::Listener* delegate,
241 ScopedHandle* process_exit_event_out) {
242 DCHECK(main_task_runner_->BelongsToCurrentThread());
244 CommandLine command_line(CommandLine::NO_PROGRAM);
245 if (launch_elevated_) {
246 // The job object is not ready. Retry starting the host process later.
247 if (!job_.IsValid()) {
248 return false;
251 // Construct the helper binary name.
252 FilePath daemon_binary;
253 if (!GetInstalledBinaryPath(kDaemonBinaryName, &daemon_binary))
254 return false;
256 // Create the command line passing the name of the IPC channel to use and
257 // copying known switches from the caller's command line.
258 command_line.SetProgram(daemon_binary);
259 command_line.AppendSwitchPath(kElevateSwitchName, binary_path_);
261 CHECK(ResetEvent(process_exit_event_));
262 } else {
263 command_line.SetProgram(binary_path_);
266 // Create the server end of the IPC channel.
267 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
268 ScopedHandle pipe;
269 if (!CreateIpcChannel(channel_name, channel_security_, &pipe))
270 return false;
272 // Wrap the pipe into an IPC channel.
273 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy(
274 IPC::ChannelHandle(pipe),
275 IPC::Channel::MODE_SERVER,
276 delegate,
277 io_task_runner_));
279 // Create the command line passing the name of the IPC channel to use and
280 // copying known switches from the caller's command line.
281 command_line.AppendSwitchNative(kDaemonPipeSwitchName,
282 UTF8ToWide(channel_name));
283 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
284 kCopiedSwitchNames,
285 arraysize(kCopiedSwitchNames));
287 // Try to launch the process.
288 ScopedHandle worker_process;
289 ScopedHandle worker_thread;
290 if (!LaunchProcessWithToken(command_line.GetProgram(),
291 command_line.GetCommandLineString(),
292 session_token_,
293 NULL,
294 NULL,
295 false,
296 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
297 UTF8ToUTF16(kDefaultDesktopName).c_str(),
298 &worker_process,
299 &worker_thread)) {
300 return false;
303 HANDLE local_process_exit_event;
304 if (launch_elevated_) {
305 if (!AssignProcessToJobObject(job_, worker_process)) {
306 LOG_GETLASTERROR(ERROR)
307 << "Failed to assign the worker to the job object";
308 TerminateProcess(worker_process, CONTROL_C_EXIT);
309 return false;
312 local_process_exit_event = process_exit_event_;
313 } else {
314 worker_process_ = worker_process.Pass();
315 local_process_exit_event = worker_process_;
318 if (!ResumeThread(worker_thread)) {
319 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
320 KillProcess(CONTROL_C_EXIT);
321 return false;
324 // Return a handle that the caller can wait on to get notified when
325 // the process terminates.
326 ScopedHandle process_exit_event;
327 if (!DuplicateHandle(GetCurrentProcess(),
328 local_process_exit_event,
329 GetCurrentProcess(),
330 process_exit_event.Receive(),
331 SYNCHRONIZE,
332 FALSE,
333 0)) {
334 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
335 KillProcess(CONTROL_C_EXIT);
336 return false;
339 channel_ = channel.Pass();
340 pipe_ = pipe.Pass();
341 *process_exit_event_out = process_exit_event.Pass();
342 return true;
345 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) {
346 if (base::win::GetVersion() < base::win::VERSION_VISTA)
347 launch_elevated_ = false;
349 if (launch_elevated_) {
350 // GetNamedPipeClientProcessId() is available starting from Vista.
351 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
352 CHECK(kernel32 != NULL);
354 get_named_pipe_client_pid_ =
355 reinterpret_cast<GetNamedPipeClientProcessIdFn>(
356 GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
357 CHECK(get_named_pipe_client_pid_ != NULL);
359 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
360 if (!process_exit_event_.IsValid()) {
361 LOG(ERROR) << "Failed to create a nameless event";
362 return false;
365 // To receive job object notifications the job object is registered with
366 // the completion port represented by |io_task_runner|. The registration has
367 // to be done on the I/O thread because
368 // MessageLoopForIO::RegisterJobObject() can only be called via
369 // MessageLoopForIO::current().
370 io_task_runner_->PostTask(FROM_HERE,
371 base::Bind(&Core::InitializeJob, this));
374 // Create a session token for the launched process.
375 return CreateSessionToken(session_id, &session_token_);
378 void WtsSessionProcessDelegate::Core::Stop() {
379 DCHECK(main_task_runner_->BelongsToCurrentThread());
381 if (!stopping_) {
382 stopping_ = true;
384 // Drain the completion queue to make sure all job object notifications have
385 // been received.
386 DrainJobNotificationsCompleted();
390 WtsSessionProcessDelegate::Core::~Core() {
393 void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
394 DCHECK(io_task_runner_->BelongsToCurrentThread());
396 // DrainJobNotifications() is posted after the job object is destroyed, so
397 // by this time all notifications from the job object have been processed
398 // already. Let the main thread know that the queue has been drained.
399 main_task_runner_->PostTask(FROM_HERE, base::Bind(
400 &Core::DrainJobNotificationsCompleted, this));
403 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
404 DCHECK(main_task_runner_->BelongsToCurrentThread());
406 if (job_.IsValid()) {
407 job_.Close();
409 // Drain the completion queue to make sure all job object notification have
410 // been received.
411 io_task_runner_->PostTask(FROM_HERE, base::Bind(
412 &Core::DrainJobNotifications, this));
416 void WtsSessionProcessDelegate::Core::InitializeJob() {
417 DCHECK(io_task_runner_->BelongsToCurrentThread());
419 ScopedHandle job;
420 job.Set(CreateJobObject(NULL, NULL));
421 if (!job.IsValid()) {
422 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
423 return;
426 // Limit the number of active processes in the job to two (the process
427 // performing elevation and the host) and make sure that all processes will be
428 // killed once the job object is destroyed.
429 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
430 memset(&info, 0, sizeof(info));
431 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
432 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
433 info.BasicLimitInformation.ActiveProcessLimit = 2;
434 if (!SetInformationJobObject(job,
435 JobObjectExtendedLimitInformation,
436 &info,
437 sizeof(info))) {
438 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
439 return;
442 // Register to receive job notifications via the I/O thread's completion port.
443 if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
444 LOG_GETLASTERROR(ERROR)
445 << "Failed to associate the job object with a completion port";
446 return;
449 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
450 // pointer.
451 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
452 *job_wrapper = job.Pass();
454 // Let the main thread know that initialization is complete.
455 main_task_runner_->PostTask(FROM_HERE, base::Bind(
456 &Core::InitializeJobCompleted, this, base::Passed(&job_wrapper)));
459 void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
460 scoped_ptr<ScopedHandle> job) {
461 DCHECK(main_task_runner_->BelongsToCurrentThread());
462 DCHECK(!job_.IsValid());
464 job_ = job->Pass();
467 void WtsSessionProcessDelegate::Core::OnJobNotification(DWORD message,
468 DWORD pid) {
469 DCHECK(main_task_runner_->BelongsToCurrentThread());
471 switch (message) {
472 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
473 CHECK(SetEvent(process_exit_event_));
474 break;
476 case JOB_OBJECT_MSG_NEW_PROCESS:
477 // We report the exit code of the worker process to be |CONTROL_C_EXIT|
478 // if we cannot get the actual exit code. So here we can safely ignore
479 // the error returned by OpenProcess().
480 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
481 break;
485 WtsSessionProcessDelegate::WtsSessionProcessDelegate(
486 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
487 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
488 const FilePath& binary_path,
489 uint32 session_id,
490 bool launch_elevated,
491 const std::string& channel_security) {
492 core_ = new Core(main_task_runner, io_task_runner, binary_path,
493 launch_elevated, channel_security);
494 if (!core_->Initialize(session_id)) {
495 core_->Stop();
496 core_ = NULL;
500 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
501 if (core_) {
502 core_->Stop();
503 core_ = NULL;
507 bool WtsSessionProcessDelegate::Send(IPC::Message* message) {
508 return core_->Send(message);
511 DWORD WtsSessionProcessDelegate::GetProcessId() const {
512 if (!core_)
513 return 0;
515 return core_->GetProcessId();
518 bool WtsSessionProcessDelegate::IsPermanentError(int failure_count) const {
519 if (!core_)
520 return false;
522 return core_->IsPermanentError(failure_count);
525 void WtsSessionProcessDelegate::KillProcess(DWORD exit_code) {
526 if (core_)
527 core_->KillProcess(exit_code);
530 bool WtsSessionProcessDelegate::LaunchProcess(
531 IPC::Listener* delegate,
532 base::win::ScopedHandle* process_exit_event_out) {
533 if (!core_)
534 return false;
536 return core_->LaunchProcess(delegate, process_exit_event_out);
539 } // namespace remoting