summaryrefslogtreecommitdiffstats
path: root/remoting/host/daemon_process_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/host/daemon_process_win.cc')
-rw-r--r--remoting/host/daemon_process_win.cc246
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(