diff options
Diffstat (limited to 'remoting/host/daemon_process_win.cc')
-rw-r--r-- | remoting/host/daemon_process_win.cc | 246 |
1 files changed, 240 insertions, 6 deletions
diff --git a/remoting/host/daemon_process_win.cc b/remoting/host/daemon_process_win.cc index 2f7706d..d8588d5 100644 --- a/remoting/host/daemon_process_win.cc +++ b/remoting/host/daemon_process_win.cc @@ -4,23 +4,96 @@ #include "remoting/host/daemon_process.h" +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/logging.h" +#include "base/path_service.h" #include "base/single_thread_task_runner.h" +#include "base/time.h" +#include "base/timer.h" +#include "base/utf_string_conversions.h" #include "base/win/scoped_handle.h" +#include "remoting/host/constants.h" +#include "remoting/host/win/launch_process_with_token.h" +#include "remoting/host/win/worker_process_launcher.h" + +using base::win::ScopedHandle; +using base::TimeDelta; + +namespace { + +// The minimum and maximum delays between attempts to launch the networking +// process. +const int kMaxLaunchDelaySeconds = 60; +const int kMinLaunchDelaySeconds = 1; + +const FilePath::CharType kMe2meHostBinaryName[] = + FILE_PATH_LITERAL("remoting_me2me_host.exe"); + +// The IPC channel name is passed to the networking process in the command line. +const char kDaemonPipeSwitchName[] = "daemon-pipe"; + +// The command line parameters that should be copied from the service's command +// line to the network process. +const char* kCopiedSwitchNames[] = { + "host-config", switches::kV, switches::kVModule }; + +// The security descriptor of the daemon IPC endpoint. It gives full access +// to LocalSystem and denies access by anyone else. +const char kDaemonPipeSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; + +} // namespace namespace remoting { -class DaemonProcessWin : public DaemonProcess { +class DaemonProcessWin : public DaemonProcess, + public WorkerProcessLauncher::Delegate { public: DaemonProcessWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, const base::Closure& stopped_callback); virtual ~DaemonProcessWin(); + virtual void OnChannelConnected() OVERRIDE; + + // Sends an IPC message to the worker process. This method can be called only + // after successful Start() and until Stop() is called or an error occurred. + virtual void Send(IPC::Message* message) OVERRIDE; + + // WorkerProcessLauncher::Delegate implementation. + virtual bool DoLaunchProcess( + const std::string& channel_name, + ScopedHandle* process_exit_event_out) OVERRIDE; + virtual void DoKillProcess(DWORD exit_code) OVERRIDE; + + protected: + // Stoppable implementation. + virtual void DoStop() OVERRIDE; + // DaemonProcess implementation. - virtual bool LaunchNetworkProcess() OVERRIDE; + virtual void LaunchNetworkProcess() OVERRIDE; private: + // Called when the launcher reports the worker process has stopped. + void OnLauncherStopped(); + + // True if the network process is connected to the daemon. + bool connected_; + + // Time of the last launch attempt. + base::Time launch_time_; + + // Current backoff delay. + base::TimeDelta launch_backoff_; + + // Timer used to schedule the next attempt to launch the process. + base::OneShotTimer<DaemonProcessWin> timer_; + + scoped_ptr<WorkerProcessLauncher> launcher_; + + ScopedHandle network_process_; + DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); }; @@ -28,15 +101,176 @@ DaemonProcessWin::DaemonProcessWin( scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, const base::Closure& stopped_callback) - : DaemonProcess(main_task_runner, io_task_runner, stopped_callback) { + : DaemonProcess(main_task_runner, io_task_runner, stopped_callback), + connected_(false) { } DaemonProcessWin::~DaemonProcessWin() { + // Make sure that the object is completely stopped. The same check exists + // in Stoppable::~Stoppable() but this one helps us to fail early and + // predictably. + CHECK_EQ(stoppable_state(), Stoppable::kStopped); } -bool DaemonProcessWin::LaunchNetworkProcess() { - NOTIMPLEMENTED(); - return false; +void DaemonProcessWin::LaunchNetworkProcess() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + DCHECK(launcher_.get() == NULL); + DCHECK(!network_process_.IsValid()); + DCHECK(!timer_.IsRunning()); + + launch_time_ = base::Time::Now(); + launcher_.reset(new WorkerProcessLauncher( + this, this, + base::Bind(&DaemonProcessWin::OnLauncherStopped, base::Unretained(this)), + main_task_runner(), + io_task_runner())); + launcher_->Start(kDaemonPipeSecurityDescriptor); +} + +void DaemonProcessWin::OnChannelConnected() { + connected_ = true; + DaemonProcess::OnChannelConnected(); +} + +void DaemonProcessWin::Send(IPC::Message* message) { + if (connected_) { + launcher_->Send(message); + } else { + delete message; + } +} + +bool DaemonProcessWin::DoLaunchProcess( + const std::string& channel_name, + ScopedHandle* process_exit_event_out) { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + DCHECK(!network_process_.IsValid()); + + // Construct the host binary name. + FilePath dir_path; + if (!PathService::Get(base::DIR_EXE, &dir_path)) { + LOG(ERROR) << "Failed to get the executable file name."; + return false; + } + FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); + + // Create the host process command line, passing the name of the IPC channel + // to use and copying known switches from the service's command line. + CommandLine command_line(host_binary); + command_line.AppendSwitchNative(kDaemonPipeSwitchName, + UTF8ToWide(channel_name)); + command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), + kCopiedSwitchNames, + _countof(kCopiedSwitchNames)); + + ScopedHandle token; + if (!OpenProcessToken(GetCurrentProcess(), + MAXIMUM_ALLOWED, + token.Receive())) { + LOG_GETLASTERROR(FATAL) << "Failed to open process token"; + return false; + } + + // Try to launch the process and attach an object watcher to the returned + // handle so that we get notified when the process terminates. + // TODO(alexeypa): Pass a restricted process token. + // See http://crbug.com/134694. + ScopedHandle worker_thread; + if (!LaunchProcessWithToken(host_binary, + command_line.GetCommandLineString(), + token, + 0, + &network_process_, + &worker_thread)) { + return false; + } + + ScopedHandle process_exit_event; + if (!DuplicateHandle(GetCurrentProcess(), + network_process_, + GetCurrentProcess(), + process_exit_event.Receive(), + SYNCHRONIZE, + FALSE, + 0)) { + LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; + DoKillProcess(CONTROL_C_EXIT); + return false; + } + + *process_exit_event_out = process_exit_event.Pass(); + return true; +} + +void DaemonProcessWin::DoKillProcess(DWORD exit_code) { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + CHECK(network_process_.IsValid()); + + TerminateProcess(network_process_, exit_code); +} + +void DaemonProcessWin::DoStop() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + + timer_.Stop(); + + if (launcher_.get() != NULL) { + launcher_->Stop(); + } + + // Early exit if we're still waiting for |launcher_| to stop. + if (launcher_.get() != NULL) { + return; + } + + DaemonProcess::DoStop(); +} + +void DaemonProcessWin::OnLauncherStopped() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + CHECK(network_process_.IsValid()); + + DWORD exit_code = CONTROL_C_EXIT; + if (!::GetExitCodeProcess(network_process_, &exit_code)) { + LOG_GETLASTERROR(INFO) + << "Failed to query the exit code of the worker process"; + exit_code = CONTROL_C_EXIT; + } + + network_process_.Close(); + connected_ = false; + launcher_.reset(NULL); + + // Do not relaunch the network process if the caller has asked us to stop. + if (stoppable_state() != Stoppable::kRunning) { + Stop(); + return; + } + + // Stop trying to restart the worker process if its process exited due to + // misconfiguration. + if (kMinPermanentErrorExitCode <= exit_code && + exit_code <= kMaxPermanentErrorExitCode) { + Stop(); + return; + } + + // Expand the backoff interval if the process has died quickly or reset it + // if it was up longer than the maximum backoff delay. + base::TimeDelta delta = base::Time::Now() - launch_time_; + if (delta < base::TimeDelta() || + delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { + launch_backoff_ = base::TimeDelta(); + } else { + launch_backoff_ = std::max( + launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); + launch_backoff_ = std::min( + launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); + } + + // Try to launch the worker process. + timer_.Start(FROM_HERE, launch_backoff_, + this, &DaemonProcessWin::LaunchNetworkProcess); } scoped_ptr<DaemonProcess> DaemonProcess::Create( |