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.
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
};
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
{
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
,
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.
86 friend class base::RefCountedThreadSafe
<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
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
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.
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
),
164 DCHECK(main_task_runner_
->BelongsToCurrentThread());
167 void WtsSessionProcessDelegate::Core::OnIOCompleted(
168 base::MessagePumpForIO::IOContext
* context
,
169 DWORD bytes_transferred
,
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
);
191 DWORD
WtsSessionProcessDelegate::Core::GetProcessId() const {
193 if (launch_elevated_
&& pipe_
.IsValid() &&
194 get_named_pipe_client_pid_(pipe_
, &pid
)) {
198 if (worker_process_
.IsValid())
199 return ::GetProcessId(worker_process_
);
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
218 return (kMinPermanentErrorExitCode
<= exit_code
&&
219 exit_code
<= kMaxPermanentErrorExitCode
);
222 void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code
) {
223 DCHECK(main_task_runner_
->BelongsToCurrentThread());
228 if (launch_elevated_
) {
229 if (job_
.IsValid()) {
230 TerminateJobObject(job_
, exit_code
);
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()) {
251 // Construct the helper binary name.
252 FilePath daemon_binary
;
253 if (!GetInstalledBinaryPath(kDaemonBinaryName
, &daemon_binary
))
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_
));
263 command_line
.SetProgram(binary_path_
);
266 // Create the server end of the IPC channel.
267 std::string channel_name
= IPC::Channel::GenerateUniqueRandomChannelID();
269 if (!CreateIpcChannel(channel_name
, channel_security_
, &pipe
))
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
,
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(),
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(),
296 CREATE_SUSPENDED
| CREATE_BREAKAWAY_FROM_JOB
,
297 UTF8ToUTF16(kDefaultDesktopName
).c_str(),
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
);
312 local_process_exit_event
= process_exit_event_
;
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
);
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
,
330 process_exit_event
.Receive(),
334 LOG_GETLASTERROR(ERROR
) << "Failed to duplicate a handle";
335 KillProcess(CONTROL_C_EXIT
);
339 channel_
= channel
.Pass();
341 *process_exit_event_out
= process_exit_event
.Pass();
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";
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());
384 // Drain the completion queue to make sure all job object notifications have
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()) {
409 // Drain the completion queue to make sure all job object notification have
411 io_task_runner_
->PostTask(FROM_HERE
, base::Bind(
412 &Core::DrainJobNotifications
, this));
416 void WtsSessionProcessDelegate::Core::InitializeJob() {
417 DCHECK(io_task_runner_
->BelongsToCurrentThread());
420 job
.Set(CreateJobObject(NULL
, NULL
));
421 if (!job
.IsValid()) {
422 LOG_GETLASTERROR(ERROR
) << "Failed to create a job object";
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
,
438 LOG_GETLASTERROR(ERROR
) << "Failed to set limits on the job object";
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";
449 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
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());
467 void WtsSessionProcessDelegate::Core::OnJobNotification(DWORD message
,
469 DCHECK(main_task_runner_
->BelongsToCurrentThread());
472 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
:
473 CHECK(SetEvent(process_exit_event_
));
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
));
485 WtsSessionProcessDelegate::WtsSessionProcessDelegate(
486 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
487 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner
,
488 const FilePath
& binary_path
,
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
)) {
500 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
507 bool WtsSessionProcessDelegate::Send(IPC::Message
* message
) {
508 return core_
->Send(message
);
511 DWORD
WtsSessionProcessDelegate::GetProcessId() const {
515 return core_
->GetProcessId();
518 bool WtsSessionProcessDelegate::IsPermanentError(int failure_count
) const {
522 return core_
->IsPermanentError(failure_count
);
525 void WtsSessionProcessDelegate::KillProcess(DWORD exit_code
) {
527 core_
->KillProcess(exit_code
);
530 bool WtsSessionProcessDelegate::LaunchProcess(
531 IPC::Listener
* delegate
,
532 base::win::ScopedHandle
* process_exit_event_out
) {
536 return core_
->LaunchProcess(delegate
, process_exit_event_out
);
539 } // namespace remoting