| OLD | NEW |
| 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_launcher.h" | 8 #include "remoting/host/win/wts_session_process_launcher.h" |
| 9 | 9 |
| 10 #include <windows.h> | 10 #include <windows.h> |
| 11 #include <sddl.h> | 11 #include <sddl.h> |
| 12 #include <limits> | 12 #include <limits> |
| 13 | 13 |
| 14 #include "base/base_switches.h" | 14 #include "base/base_switches.h" |
| 15 #include "base/bind.h" | 15 #include "base/bind.h" |
| 16 #include "base/bind_helpers.h" | 16 #include "base/bind_helpers.h" |
| 17 #include "base/command_line.h" | 17 #include "base/command_line.h" |
| 18 #include "base/file_path.h" | 18 #include "base/file_path.h" |
| 19 #include "base/file_util.h" | 19 #include "base/file_util.h" |
| 20 #include "base/logging.h" | 20 #include "base/logging.h" |
| 21 #include "base/single_thread_task_runner.h" | 21 #include "base/single_thread_task_runner.h" |
| 22 #include "base/path_service.h" | 22 #include "base/path_service.h" |
| 23 #include "base/rand_util.h" | 23 #include "base/rand_util.h" |
| 24 #include "base/stringprintf.h" | 24 #include "base/stringprintf.h" |
| 25 #include "base/utf_string_conversions.h" | 25 #include "base/utf_string_conversions.h" |
| 26 #include "base/win/scoped_handle.h" | 26 #include "base/win/scoped_handle.h" |
| 27 #include "base/win/windows_version.h" |
| 27 #include "ipc/ipc_channel_proxy.h" | 28 #include "ipc/ipc_channel_proxy.h" |
| 28 #include "ipc/ipc_message.h" | 29 #include "ipc/ipc_message.h" |
| 29 #include "ipc/ipc_message_macros.h" | 30 #include "ipc/ipc_message_macros.h" |
| 30 #include "remoting/host/constants.h" | 31 #include "remoting/host/constants.h" |
| 31 #include "remoting/host/chromoting_messages.h" | 32 #include "remoting/host/chromoting_messages.h" |
| 32 #include "remoting/host/win/launch_process_with_token.h" | 33 #include "remoting/host/win/launch_process_with_token.h" |
| 33 #include "remoting/host/win/wts_console_monitor.h" | 34 #include "remoting/host/win/wts_console_monitor.h" |
| 34 | 35 |
| 35 using base::win::ScopedHandle; | 36 using base::win::ScopedHandle; |
| 36 using base::TimeDelta; | 37 using base::TimeDelta; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 59 "host-config", switches::kV, switches::kVModule }; | 60 "host-config", switches::kV, switches::kVModule }; |
| 60 | 61 |
| 61 // The security descriptor of the daemon IPC endpoint. It gives full access | 62 // The security descriptor of the daemon IPC endpoint. It gives full access |
| 62 // to LocalSystem and denies access by anyone else. | 63 // to LocalSystem and denies access by anyone else. |
| 63 const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; | 64 const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; |
| 64 | 65 |
| 65 } // namespace | 66 } // namespace |
| 66 | 67 |
| 67 namespace remoting { | 68 namespace remoting { |
| 68 | 69 |
| 69 WtsSessionProcessLauncher::WtsSessionProcessLauncher( | 70 class WtsSessionProcessLauncherImpl |
| 70 const base::Closure& stopped_callback, | 71 : public base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>, |
| 71 WtsConsoleMonitor* monitor, | 72 public WorkerProcessLauncher::Delegate { |
| 72 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, | 73 public: |
| 73 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) | 74 // Returns the exit code of the worker process. |
| 74 : Stoppable(main_message_loop, stopped_callback), | 75 virtual DWORD GetExitCode() = 0; |
| 75 attached_(false), | 76 |
| 76 main_message_loop_(main_message_loop), | 77 // Sets the token to be used to launch the worker process. |
| 77 ipc_message_loop_(ipc_message_loop), | 78 virtual void ResetUserToken(ScopedHandle* user_token) = 0; |
| 78 monitor_(monitor), | 79 |
| 79 job_state_(kJobUninitialized) { | 80 // Stops the object asynchronously. |
| 80 monitor_->AddWtsConsoleObserver(this); | 81 virtual void Stop() = 0; |
| 81 | 82 |
| 83 protected: |
| 84 friend class base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>; |
| 85 virtual ~WtsSessionProcessLauncherImpl(); |
| 86 }; |
| 87 |
| 88 namespace { |
| 89 |
| 90 // Implements |WorkerProcessLauncher::Delegate| that starts the specified |
| 91 // process in a different session via CreateProcessAsUser() and uses |
| 92 // the returned handle to monitor and terminate the process. |
| 93 class SingleProcessLauncher : public WtsSessionProcessLauncherImpl { |
| 94 public: |
| 95 SingleProcessLauncher( |
| 96 const FilePath& binary_path, |
| 97 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); |
| 98 |
| 99 // WorkerProcessLauncher::Delegate implementation. |
| 100 virtual bool DoLaunchProcess( |
| 101 const std::string& channel_name, |
| 102 ScopedHandle* process_exit_event_out) OVERRIDE; |
| 103 virtual void DoKillProcess(DWORD exit_code) OVERRIDE; |
| 104 |
| 105 // WtsSessionProcessLauncherImpl implementation. |
| 106 virtual DWORD GetExitCode() OVERRIDE; |
| 107 virtual void ResetUserToken(ScopedHandle* user_token) OVERRIDE; |
| 108 virtual void Stop() OVERRIDE; |
| 109 |
| 110 private: |
| 111 FilePath binary_path_; |
| 112 |
| 113 // The task runner all public methods of this class should be called on. |
| 114 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| 115 |
| 116 // A handle that becomes signalled once the process associated has been |
| 117 // terminated. |
| 118 ScopedHandle process_exit_event_; |
| 119 |
| 120 // The token to be used to launch a process in a different session. |
| 121 ScopedHandle user_token_; |
| 122 |
| 123 // The handle of the worker process, if launched. |
| 124 ScopedHandle worker_process_; |
| 125 |
| 126 DISALLOW_COPY_AND_ASSIGN(SingleProcessLauncher); |
| 127 }; |
| 128 |
| 129 // Implements |WorkerProcessLauncher::Delegate| that starts the specified |
| 130 // process (UAC) elevated in a different session. |ElevatedProcessLauncher| |
| 131 // utilizes a helper process to bypass limitations of ShellExecute() which |
| 132 // cannot spawn processes across the session boundary. |
| 133 class ElevatedProcessLauncher : public WtsSessionProcessLauncherImpl, |
| 134 public base::MessagePumpForIO::IOHandler { |
| 135 public: |
| 136 ElevatedProcessLauncher( |
| 137 const FilePath& binary_path, |
| 138 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 139 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner); |
| 140 |
| 141 // base::MessagePumpForIO::IOHandler implementation. |
| 142 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context, |
| 143 DWORD bytes_transferred, |
| 144 DWORD error) OVERRIDE; |
| 145 |
| 146 // WorkerProcessLauncher::Delegate implementation. |
| 147 virtual bool DoLaunchProcess( |
| 148 const std::string& channel_name, |
| 149 ScopedHandle* process_exit_event_out) OVERRIDE; |
| 150 virtual void DoKillProcess(DWORD exit_code) OVERRIDE; |
| 151 |
| 152 // WtsSessionProcessLauncherImpl implementation. |
| 153 virtual DWORD GetExitCode() OVERRIDE; |
| 154 virtual void ResetUserToken(ScopedHandle* user_token) OVERRIDE; |
| 155 virtual void Stop() OVERRIDE; |
| 156 |
| 157 private: |
| 158 // Drains the completion port queue to make sure that all job object |
| 159 // notifications have been received. |
| 160 void DrainJobNotifications(); |
| 161 |
| 162 // Notified that the completion port queue has been drained. |
| 163 void DrainJobNotificationsCompleted(); |
| 164 |
| 165 // Creates and initializes the job object that will sandbox the launched child |
| 166 // processes. |
| 167 void InitializeJob(); |
| 168 |
| 169 // Notified that the job object initialization is complete. |
| 170 void InitializeJobCompleted(scoped_ptr<ScopedHandle> job); |
| 171 |
| 172 // Called to process incoming job object notifications. |
| 173 void OnJobNotification(DWORD message, DWORD pid); |
| 174 |
| 175 FilePath binary_path_; |
| 176 |
| 177 // The task runner all public methods of this class should be called on. |
| 178 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| 179 |
| 180 // The task runner serving job object notifications. |
| 181 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; |
| 182 |
| 183 // The job object used to control the lifetime of child processes. |
| 184 ScopedHandle job_; |
| 185 |
| 186 // A waiting handle that becomes signalled once all process associated with |
| 187 // the job have been terminated. |
| 188 ScopedHandle process_exit_event_; |
| 189 |
| 190 // The token to be used to launch a process in a different session. |
| 191 ScopedHandle user_token_; |
| 192 |
| 193 // The handle of the worker process, if launched. |
| 194 ScopedHandle worker_process_; |
| 195 |
| 196 DISALLOW_COPY_AND_ASSIGN(ElevatedProcessLauncher); |
| 197 }; |
| 198 |
| 199 SingleProcessLauncher::SingleProcessLauncher( |
| 200 const FilePath& binary_path, |
| 201 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) |
| 202 : binary_path_(binary_path), |
| 203 main_task_runner_(main_task_runner) { |
| 204 } |
| 205 |
| 206 bool SingleProcessLauncher::DoLaunchProcess( |
| 207 const std::string& channel_name, |
| 208 ScopedHandle* process_exit_event_out) { |
| 209 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 210 |
| 211 // Create the command line passing the name of the IPC channel to use and |
| 212 // copying known switches from the caller's command line. |
| 213 CommandLine command_line(binary_path_); |
| 214 command_line.AppendSwitchNative(kDaemonIpcSwitchName, |
| 215 UTF8ToWide(channel_name)); |
| 216 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), |
| 217 kCopiedSwitchNames, |
| 218 _countof(kCopiedSwitchNames)); |
| 219 |
| 220 // Try to launch the process and attach an object watcher to the returned |
| 221 // handle so that we get notified when the process terminates. |
| 222 ScopedHandle worker_thread; |
| 223 worker_process_.Close(); |
| 224 if (!LaunchProcessWithToken(binary_path_, |
| 225 command_line.GetCommandLineString(), |
| 226 user_token_, |
| 227 0, |
| 228 &worker_process_, |
| 229 &worker_thread)) { |
| 230 return false; |
| 231 } |
| 232 |
| 233 ScopedHandle process_exit_event; |
| 234 if (!DuplicateHandle(GetCurrentProcess(), |
| 235 worker_process_, |
| 236 GetCurrentProcess(), |
| 237 process_exit_event.Receive(), |
| 238 SYNCHRONIZE, |
| 239 FALSE, |
| 240 0)) { |
| 241 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; |
| 242 DoKillProcess(CONTROL_C_EXIT); |
| 243 return false; |
| 244 } |
| 245 |
| 246 *process_exit_event_out = process_exit_event.Pass(); |
| 247 return true; |
| 248 } |
| 249 |
| 250 void SingleProcessLauncher::DoKillProcess(DWORD exit_code) { |
| 251 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 252 |
| 253 if (worker_process_.IsValid()) { |
| 254 TerminateProcess(worker_process_, exit_code); |
| 255 } |
| 256 } |
| 257 |
| 258 DWORD SingleProcessLauncher::GetExitCode() { |
| 259 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 260 |
| 261 DWORD exit_code = CONTROL_C_EXIT; |
| 262 if (worker_process_.IsValid()) { |
| 263 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { |
| 264 LOG_GETLASTERROR(INFO) |
| 265 << "Failed to query the exit code of the worker process"; |
| 266 exit_code = CONTROL_C_EXIT; |
| 267 } |
| 268 |
| 269 worker_process_.Close(); |
| 270 } |
| 271 |
| 272 return exit_code; |
| 273 } |
| 274 |
| 275 void SingleProcessLauncher::ResetUserToken(ScopedHandle* user_token) { |
| 276 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 277 |
| 278 user_token_ = user_token->Pass(); |
| 279 } |
| 280 |
| 281 void SingleProcessLauncher::Stop() { |
| 282 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 283 } |
| 284 |
| 285 ElevatedProcessLauncher::ElevatedProcessLauncher( |
| 286 const FilePath& binary_path, |
| 287 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 288 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) |
| 289 : binary_path_(binary_path), |
| 290 main_task_runner_(main_task_runner), |
| 291 io_task_runner_(io_task_runner) { |
| 82 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); | 292 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); |
| 83 CHECK(process_exit_event_.IsValid()); | 293 CHECK(process_exit_event_.IsValid()); |
| 84 | 294 |
| 85 // To receive job object notifications it is registered with the completion | 295 // To receive job object notifications the job object is registered with |
| 86 // port represented by |ipc_message_loop|. The registration has to be done on | 296 // the completion port represented by |io_task_runner|. The registration has |
| 87 // the I/O thread because MessageLoopForIO::RegisterJobObject() can only be | 297 // to be done on the I/O thread because MessageLoopForIO::RegisterJobObject() |
| 88 // called via MessageLoopForIO::current(). | 298 // can only be called via MessageLoopForIO::current(). |
| 89 ipc_message_loop_->PostTask(FROM_HERE, base::Bind( | 299 io_task_runner_->PostTask( |
| 90 &WtsSessionProcessLauncher::InitializeJob, | 300 FROM_HERE, |
| 91 base::Unretained(this))); | 301 base::Bind(&ElevatedProcessLauncher::InitializeJob, this)); |
| 92 } | 302 } |
| 93 | 303 |
| 94 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { | 304 void ElevatedProcessLauncher::OnIOCompleted( |
| 95 // Make sure that the object is completely stopped. The same check exists | |
| 96 // in Stoppable::~Stoppable() but this one helps us to fail early and | |
| 97 // predictably. | |
| 98 CHECK_EQ(stoppable_state(), Stoppable::kStopped); | |
| 99 | |
| 100 monitor_->RemoveWtsConsoleObserver(this); | |
| 101 | |
| 102 CHECK(!attached_); | |
| 103 CHECK(!timer_.IsRunning()); | |
| 104 } | |
| 105 | |
| 106 void WtsSessionProcessLauncher::OnIOCompleted( | |
| 107 base::MessagePumpForIO::IOContext* context, | 305 base::MessagePumpForIO::IOContext* context, |
| 108 DWORD bytes_transferred, | 306 DWORD bytes_transferred, |
| 109 DWORD error) { | 307 DWORD error) { |
| 110 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); | 308 DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| 111 | 309 |
| 112 // |bytes_transferred| is used in job object notifications to supply | 310 // |bytes_transferred| is used in job object notifications to supply |
| 113 // the message ID; |context| carries process ID. | 311 // the message ID; |context| carries process ID. |
| 114 main_message_loop_->PostTask(FROM_HERE, base::Bind( | 312 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 115 &WtsSessionProcessLauncher::OnJobNotification, | 313 &ElevatedProcessLauncher::OnJobNotification, this, bytes_transferred, |
| 116 base::Unretained(this), bytes_transferred, | |
| 117 reinterpret_cast<DWORD>(context))); | 314 reinterpret_cast<DWORD>(context))); |
| 118 } | 315 } |
| 119 | 316 |
| 120 bool WtsSessionProcessLauncher::DoLaunchProcess( | 317 bool ElevatedProcessLauncher::DoLaunchProcess( |
| 121 const std::string& channel_name, | 318 const std::string& channel_name, |
| 122 ScopedHandle* process_exit_event_out) { | 319 ScopedHandle* process_exit_event_out) { |
| 123 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 320 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 124 | 321 |
| 125 // The job object is not ready. Retry starting the host process later. | 322 // The job object is not ready. Retry starting the host process later. |
| 126 if (!job_.IsValid()) { | 323 if (!job_.IsValid()) { |
| 127 return false; | 324 return false; |
| 128 } | 325 } |
| 129 | 326 |
| 130 // Construct the host binary name. | 327 // Construct the helper binary name. |
| 131 FilePath dir_path; | 328 FilePath dir_path; |
| 132 if (!PathService::Get(base::DIR_EXE, &dir_path)) { | 329 if (!PathService::Get(base::DIR_EXE, &dir_path)) { |
| 133 LOG(ERROR) << "Failed to get the executable file name."; | 330 LOG(ERROR) << "Failed to get the executable file name."; |
| 134 return false; | 331 return false; |
| 135 } | 332 } |
| 136 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); | |
| 137 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName); | 333 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName); |
| 138 | 334 |
| 139 // Create the host process command line passing the name of the IPC channel | 335 // Create the command line passing the name of the IPC channel to use and |
| 140 // to use and copying known switches from the service's command line. | 336 // copying known switches from the caller's command line. |
| 141 CommandLine command_line(service_binary); | 337 CommandLine command_line(service_binary); |
| 142 command_line.AppendSwitchPath(kElevateSwitchName, host_binary); | 338 command_line.AppendSwitchPath(kElevateSwitchName, binary_path_); |
| 143 command_line.AppendSwitchNative(kDaemonIpcSwitchName, | 339 command_line.AppendSwitchNative(kDaemonIpcSwitchName, |
| 144 UTF8ToWide(channel_name)); | 340 UTF8ToWide(channel_name)); |
| 145 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), | 341 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), |
| 146 kCopiedSwitchNames, | 342 kCopiedSwitchNames, |
| 147 _countof(kCopiedSwitchNames)); | 343 _countof(kCopiedSwitchNames)); |
| 148 | 344 |
| 149 CHECK(ResetEvent(process_exit_event_)); | 345 CHECK(ResetEvent(process_exit_event_)); |
| 150 | 346 |
| 151 // Try to launch the process and attach an object watcher to the returned | 347 // Try to launch the process and attach an object watcher to the returned |
| 152 // handle so that we get notified when the process terminates. | 348 // handle so that we get notified when the process terminates. |
| 153 base::win::ScopedHandle worker_process; | 349 ScopedHandle worker_process; |
| 154 base::win::ScopedHandle worker_thread; | 350 ScopedHandle worker_thread; |
| 155 if (!LaunchProcessWithToken(service_binary, | 351 if (!LaunchProcessWithToken(service_binary, |
| 156 command_line.GetCommandLineString(), | 352 command_line.GetCommandLineString(), |
| 157 session_token_, | 353 user_token_, |
| 158 CREATE_SUSPENDED, | 354 CREATE_SUSPENDED, |
| 159 &worker_process, | 355 &worker_process, |
| 160 &worker_thread)) { | 356 &worker_thread)) { |
| 161 return false; | 357 return false; |
| 162 } | 358 } |
| 163 | 359 |
| 164 if (!AssignProcessToJobObject(job_, worker_process)) { | 360 if (!AssignProcessToJobObject(job_, worker_process)) { |
| 165 LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object"; | 361 LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object"; |
| 166 TerminateProcess(worker_process, CONTROL_C_EXIT); | 362 TerminateProcess(worker_process, CONTROL_C_EXIT); |
| 167 return false; | 363 return false; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 183 0)) { | 379 0)) { |
| 184 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; | 380 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; |
| 185 DoKillProcess(CONTROL_C_EXIT); | 381 DoKillProcess(CONTROL_C_EXIT); |
| 186 return false; | 382 return false; |
| 187 } | 383 } |
| 188 | 384 |
| 189 *process_exit_event_out = process_exit_event.Pass(); | 385 *process_exit_event_out = process_exit_event.Pass(); |
| 190 return true; | 386 return true; |
| 191 } | 387 } |
| 192 | 388 |
| 193 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) { | 389 void ElevatedProcessLauncher::DoKillProcess(DWORD exit_code) { |
| 194 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 390 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 195 | 391 |
| 196 if (job_.IsValid()) { | 392 if (job_.IsValid()) { |
| 197 TerminateJobObject(job_, exit_code); | 393 TerminateJobObject(job_, exit_code); |
| 198 } | 394 } |
| 199 } | 395 } |
| 200 | 396 |
| 201 void WtsSessionProcessLauncher::OnChannelConnected() { | 397 DWORD ElevatedProcessLauncher::GetExitCode() { |
| 202 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 398 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 399 |
| 400 DWORD exit_code = CONTROL_C_EXIT; |
| 401 if (worker_process_.IsValid()) { |
| 402 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { |
| 403 LOG_GETLASTERROR(INFO) |
| 404 << "Failed to query the exit code of the worker process"; |
| 405 exit_code = CONTROL_C_EXIT; |
| 406 } |
| 407 |
| 408 worker_process_.Close(); |
| 409 } |
| 410 |
| 411 return exit_code; |
| 203 } | 412 } |
| 204 | 413 |
| 205 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { | 414 void ElevatedProcessLauncher::ResetUserToken( |
| 206 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 415 ScopedHandle* user_token) { |
| 416 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 207 | 417 |
| 208 return false; | 418 user_token_ = user_token->Pass(); |
| 209 } | 419 } |
| 210 | 420 |
| 211 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { | 421 void ElevatedProcessLauncher::Stop() { |
| 212 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 422 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 213 | |
| 214 if (stoppable_state() != Stoppable::kRunning) { | |
| 215 return; | |
| 216 } | |
| 217 | |
| 218 DCHECK(!attached_); | |
| 219 DCHECK(!timer_.IsRunning()); | |
| 220 | |
| 221 attached_ = true; | |
| 222 | |
| 223 // Create a session token for the launched process. | |
| 224 if (!CreateSessionToken(session_id, &session_token_)) | |
| 225 return; | |
| 226 | |
| 227 // Now try to launch the host. | |
| 228 LaunchProcess(); | |
| 229 } | |
| 230 | |
| 231 void WtsSessionProcessLauncher::OnSessionDetached() { | |
| 232 DCHECK(main_message_loop_->BelongsToCurrentThread()); | |
| 233 DCHECK(attached_); | |
| 234 | |
| 235 attached_ = false; | |
| 236 launch_backoff_ = base::TimeDelta(); | |
| 237 session_token_.Close(); | |
| 238 timer_.Stop(); | |
| 239 | |
| 240 if (launcher_.get() != NULL) { | |
| 241 launcher_->Stop(); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 void WtsSessionProcessLauncher::DoStop() { | |
| 246 DCHECK(main_message_loop_->BelongsToCurrentThread()); | |
| 247 | |
| 248 if (attached_) { | |
| 249 OnSessionDetached(); | |
| 250 } | |
| 251 | |
| 252 job_.Close(); | |
| 253 | 423 |
| 254 // Drain the completion queue to make sure all job object notification have | 424 // Drain the completion queue to make sure all job object notification have |
| 255 // been received. | 425 // been received. |
| 256 if (job_state_ == kJobRunning) { | 426 DrainJobNotificationsCompleted(); |
| 257 job_state_ = kJobStopping; | |
| 258 ipc_message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 259 &WtsSessionProcessLauncher::DrainJobNotifications, | |
| 260 base::Unretained(this))); | |
| 261 } | |
| 262 | |
| 263 // Don't complete shutdown if |launcher_| is not completely stopped. | |
| 264 if (launcher_.get() != NULL) { | |
| 265 return; | |
| 266 } | |
| 267 | |
| 268 // Don't complete shutdown if the completion queue hasn't been drained. | |
| 269 if (job_state_ != kJobUninitialized && job_state_ != kJobStopped) { | |
| 270 return; | |
| 271 } | |
| 272 | |
| 273 CompleteStopping(); | |
| 274 } | 427 } |
| 275 | 428 |
| 276 void WtsSessionProcessLauncher::DrainJobNotifications() { | 429 void ElevatedProcessLauncher::DrainJobNotifications() { |
| 277 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); | 430 DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| 278 | 431 |
| 279 // DrainJobNotifications() is posted after the job object is destroyed, so | 432 // DrainJobNotifications() is posted after the job object is destroyed, so |
| 280 // by this time all notifications from the job object have been processed | 433 // by this time all notifications from the job object have been processed |
| 281 // already. Let the main thread know that the queue has been drained. | 434 // already. Let the main thread know that the queue has been drained. |
| 282 main_message_loop_->PostTask(FROM_HERE, base::Bind( | 435 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 283 &WtsSessionProcessLauncher::DrainJobNotificationsCompleted, | 436 &ElevatedProcessLauncher::DrainJobNotificationsCompleted, this)); |
| 284 base::Unretained(this))); | |
| 285 } | 437 } |
| 286 | 438 |
| 287 void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() { | 439 void ElevatedProcessLauncher::DrainJobNotificationsCompleted() { |
| 288 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 440 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 289 DCHECK_EQ(job_state_, kJobStopping); | |
| 290 | 441 |
| 291 job_state_ = kJobStopped; | 442 if (job_.IsValid()) { |
| 292 Stop(); | 443 job_.Close(); |
| 444 |
| 445 // Drain the completion queue to make sure all job object notification have |
| 446 // been received. |
| 447 io_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 448 &ElevatedProcessLauncher::DrainJobNotifications, this)); |
| 449 } |
| 293 } | 450 } |
| 294 | 451 |
| 295 void WtsSessionProcessLauncher::InitializeJob() { | 452 void ElevatedProcessLauncher::InitializeJob() { |
| 296 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); | 453 DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| 297 | 454 |
| 298 ScopedHandle job; | 455 ScopedHandle job; |
| 299 job.Set(CreateJobObject(NULL, NULL)); | 456 job.Set(CreateJobObject(NULL, NULL)); |
| 300 if (!job.IsValid()) { | 457 if (!job.IsValid()) { |
| 301 LOG_GETLASTERROR(ERROR) << "Failed to create a job object"; | 458 LOG_GETLASTERROR(ERROR) << "Failed to create a job object"; |
| 302 return; | 459 return; |
| 303 } | 460 } |
| 304 | 461 |
| 305 // Limit the number of active processes in the job to two (the process | 462 // Limit the number of active processes in the job to two (the process |
| 306 // performing elevation and the host) and make sure that all processes will be | 463 // performing elevation and the host) and make sure that all processes will be |
| (...skipping 18 matching lines...) Expand all Loading... |
| 325 << "Failed to associate the job object with a completion port"; | 482 << "Failed to associate the job object with a completion port"; |
| 326 return; | 483 return; |
| 327 } | 484 } |
| 328 | 485 |
| 329 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped | 486 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped |
| 330 // pointer. | 487 // pointer. |
| 331 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle()); | 488 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle()); |
| 332 *job_wrapper = job.Pass(); | 489 *job_wrapper = job.Pass(); |
| 333 | 490 |
| 334 // Let the main thread know that initialization is complete. | 491 // Let the main thread know that initialization is complete. |
| 335 main_message_loop_->PostTask(FROM_HERE, base::Bind( | 492 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 336 &WtsSessionProcessLauncher::InitializeJobCompleted, | 493 &ElevatedProcessLauncher::InitializeJobCompleted, this, |
| 337 base::Unretained(this), base::Passed(&job_wrapper))); | 494 base::Passed(&job_wrapper))); |
| 338 } | 495 } |
| 339 | 496 |
| 340 void WtsSessionProcessLauncher::InitializeJobCompleted( | 497 void ElevatedProcessLauncher::InitializeJobCompleted( |
| 341 scoped_ptr<ScopedHandle> job) { | 498 scoped_ptr<ScopedHandle> job) { |
| 342 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 499 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 343 DCHECK(!job_.IsValid()); | 500 DCHECK(!job_.IsValid()); |
| 344 DCHECK_EQ(job_state_, kJobUninitialized); | |
| 345 | 501 |
| 346 if (stoppable_state() == Stoppable::kRunning) { | 502 job_ = job->Pass(); |
| 347 job_ = job->Pass(); | |
| 348 job_state_ = kJobRunning; | |
| 349 } | |
| 350 } | 503 } |
| 351 | 504 |
| 352 void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) { | 505 void ElevatedProcessLauncher::OnJobNotification(DWORD message, DWORD pid) { |
| 353 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 506 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 354 | 507 |
| 355 switch (message) { | 508 switch (message) { |
| 356 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: | 509 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: |
| 357 CHECK(SetEvent(process_exit_event_)); | 510 CHECK(SetEvent(process_exit_event_)); |
| 358 break; | 511 break; |
| 359 | 512 |
| 360 case JOB_OBJECT_MSG_NEW_PROCESS: | 513 case JOB_OBJECT_MSG_NEW_PROCESS: |
| 361 // We report the exit code of the worker process to be |CONTROL_C_EXIT| | 514 // We report the exit code of the worker process to be |CONTROL_C_EXIT| |
| 362 // if we cannot get the actual exit code. So here we can safely ignore | 515 // if we cannot get the actual exit code. So here we can safely ignore |
| 363 // the error returned by OpenProcess(). | 516 // the error returned by OpenProcess(). |
| 364 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)); | 517 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)); |
| 365 break; | 518 break; |
| 366 } | 519 } |
| 367 } | 520 } |
| 368 | 521 |
| 522 } // namespace |
| 523 |
| 524 WtsSessionProcessLauncherImpl::~WtsSessionProcessLauncherImpl() { |
| 525 } |
| 526 |
| 527 WtsSessionProcessLauncher::WtsSessionProcessLauncher( |
| 528 const base::Closure& stopped_callback, |
| 529 WtsConsoleMonitor* monitor, |
| 530 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, |
| 531 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) |
| 532 : Stoppable(main_message_loop, stopped_callback), |
| 533 attached_(false), |
| 534 main_message_loop_(main_message_loop), |
| 535 ipc_message_loop_(ipc_message_loop), |
| 536 monitor_(monitor) { |
| 537 monitor_->AddWtsConsoleObserver(this); |
| 538 |
| 539 // Construct the host binary name. |
| 540 FilePath dir_path; |
| 541 if (!PathService::Get(base::DIR_EXE, &dir_path)) { |
| 542 LOG(ERROR) << "Failed to get the executable file name."; |
| 543 Stop(); |
| 544 return; |
| 545 } |
| 546 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); |
| 547 |
| 548 // The workaround we use to launch a process in a not yet logged in session |
| 549 // (see CreateRemoteSessionProcess()) on Windows XP/2K3 does not let us to |
| 550 // assign the started process to a job. However on Vista+ we have to start |
| 551 // the host via a helper process. The helper process calls ShellExecute() that |
| 552 // does not work across the session boundary but it required to launch |
| 553 // a binary specifying uiAccess='true' in its manifest. |
| 554 // |
| 555 // Below we choose which implementation of |WorkerProcessLauncher::Delegate| |
| 556 // to use. A single process is launched on XP (since uiAccess='true' does not |
| 557 // have effect any way). Vista+ utilizes a helper process and assign it to |
| 558 // a job object to control it. |
| 559 if (base::win::GetVersion() == base::win::VERSION_XP) { |
| 560 impl_ = new SingleProcessLauncher(host_binary, main_message_loop); |
| 561 } else { |
| 562 impl_ = new ElevatedProcessLauncher(host_binary, main_message_loop, |
| 563 ipc_message_loop); |
| 564 } |
| 565 } |
| 566 |
| 567 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { |
| 568 // Make sure that the object is completely stopped. The same check exists |
| 569 // in Stoppable::~Stoppable() but this one helps us to fail early and |
| 570 // predictably. |
| 571 CHECK_EQ(stoppable_state(), Stoppable::kStopped); |
| 572 |
| 573 monitor_->RemoveWtsConsoleObserver(this); |
| 574 |
| 575 CHECK(!attached_); |
| 576 CHECK(!timer_.IsRunning()); |
| 577 } |
| 578 |
| 579 void WtsSessionProcessLauncher::OnChannelConnected() { |
| 580 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 581 } |
| 582 |
| 583 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { |
| 584 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 585 |
| 586 return false; |
| 587 } |
| 588 |
| 589 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { |
| 590 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 591 |
| 592 if (stoppable_state() != Stoppable::kRunning) { |
| 593 return; |
| 594 } |
| 595 |
| 596 DCHECK(!attached_); |
| 597 DCHECK(!timer_.IsRunning()); |
| 598 |
| 599 attached_ = true; |
| 600 |
| 601 // Create a session token and launch the host. |
| 602 ScopedHandle user_token; |
| 603 if (CreateSessionToken(session_id, &user_token)) { |
| 604 impl_->ResetUserToken(&user_token); |
| 605 LaunchProcess(); |
| 606 } |
| 607 } |
| 608 |
| 609 void WtsSessionProcessLauncher::OnSessionDetached() { |
| 610 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 611 DCHECK(attached_); |
| 612 |
| 613 attached_ = false; |
| 614 launch_backoff_ = base::TimeDelta(); |
| 615 timer_.Stop(); |
| 616 |
| 617 if (launcher_.get() != NULL) { |
| 618 launcher_->Stop(); |
| 619 } |
| 620 } |
| 621 |
| 622 void WtsSessionProcessLauncher::DoStop() { |
| 623 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 624 |
| 625 if (attached_) { |
| 626 OnSessionDetached(); |
| 627 } |
| 628 |
| 629 // Don't complete shutdown if |launcher_| is not completely stopped. |
| 630 if (launcher_.get() != NULL) { |
| 631 return; |
| 632 } |
| 633 |
| 634 // Tear down the core asynchromously. |
| 635 if (impl_.get()) { |
| 636 impl_->Stop(); |
| 637 impl_ = NULL; |
| 638 } |
| 639 |
| 640 CompleteStopping(); |
| 641 } |
| 642 |
| 369 void WtsSessionProcessLauncher::LaunchProcess() { | 643 void WtsSessionProcessLauncher::LaunchProcess() { |
| 370 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 644 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 371 DCHECK(attached_); | 645 DCHECK(attached_); |
| 372 DCHECK(launcher_.get() == NULL); | 646 DCHECK(launcher_.get() == NULL); |
| 373 DCHECK(!timer_.IsRunning()); | 647 DCHECK(!timer_.IsRunning()); |
| 374 | 648 |
| 375 launch_time_ = base::Time::Now(); | 649 launch_time_ = base::Time::Now(); |
| 376 launcher_.reset(new WorkerProcessLauncher( | 650 launcher_.reset(new WorkerProcessLauncher( |
| 377 this, this, | 651 impl_.get(), this, |
| 378 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, | 652 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, |
| 379 base::Unretained(this)), | 653 base::Unretained(this)), |
| 380 main_message_loop_, | 654 main_message_loop_, |
| 381 ipc_message_loop_)); | 655 ipc_message_loop_)); |
| 382 launcher_->Start(kDaemonIpcSecurityDescriptor); | 656 launcher_->Start(kDaemonIpcSecurityDescriptor); |
| 383 } | 657 } |
| 384 | 658 |
| 385 void WtsSessionProcessLauncher::OnLauncherStopped() { | 659 void WtsSessionProcessLauncher::OnLauncherStopped() { |
| 386 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 660 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
| 387 | 661 |
| 388 DWORD exit_code = CONTROL_C_EXIT; | 662 // Retrieve the exit code of the worker process. |
| 389 if (worker_process_.IsValid()) { | 663 DWORD exit_code = impl_->GetExitCode(); |
| 390 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { | |
| 391 LOG_GETLASTERROR(INFO) | |
| 392 << "Failed to query the exit code of the worker process"; | |
| 393 exit_code = CONTROL_C_EXIT; | |
| 394 } | |
| 395 | |
| 396 worker_process_.Close(); | |
| 397 } | |
| 398 | 664 |
| 399 launcher_.reset(NULL); | 665 launcher_.reset(NULL); |
| 400 | 666 |
| 401 // Do not relaunch the worker process if the caller has asked us to stop. | 667 // Do not relaunch the worker process if the caller has asked us to stop. |
| 402 if (stoppable_state() != Stoppable::kRunning) { | 668 if (stoppable_state() != Stoppable::kRunning) { |
| 403 Stop(); | 669 Stop(); |
| 404 return; | 670 return; |
| 405 } | 671 } |
| 406 | 672 |
| 407 // Stop trying to restart the worker process if its process exited due to | 673 // Stop trying to restart the worker process if its process exited due to |
| (...skipping 19 matching lines...) Expand all Loading... |
| 427 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); | 693 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); |
| 428 } | 694 } |
| 429 | 695 |
| 430 // Try to launch the worker process. | 696 // Try to launch the worker process. |
| 431 timer_.Start(FROM_HERE, launch_backoff_, | 697 timer_.Start(FROM_HERE, launch_backoff_, |
| 432 this, &WtsSessionProcessLauncher::LaunchProcess); | 698 this, &WtsSessionProcessLauncher::LaunchProcess); |
| 433 } | 699 } |
| 434 } | 700 } |
| 435 | 701 |
| 436 } // namespace remoting | 702 } // namespace remoting |
| OLD | NEW |