| 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 #include "remoting/host/daemon_process.h" | 5 #include "remoting/host/daemon_process.h" |
| 6 | 6 |
| 7 #include "base/base_switches.h" |
| 8 #include "base/bind.h" |
| 9 #include "base/bind_helpers.h" |
| 7 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/path_service.h" |
| 8 #include "base/single_thread_task_runner.h" | 12 #include "base/single_thread_task_runner.h" |
| 13 #include "base/time.h" |
| 14 #include "base/timer.h" |
| 15 #include "base/utf_string_conversions.h" |
| 9 #include "base/win/scoped_handle.h" | 16 #include "base/win/scoped_handle.h" |
| 17 #include "remoting/host/constants.h" |
| 18 #include "remoting/host/win/launch_process_with_token.h" |
| 19 #include "remoting/host/win/worker_process_launcher.h" |
| 20 |
| 21 using base::win::ScopedHandle; |
| 22 using base::TimeDelta; |
| 23 |
| 24 namespace { |
| 25 |
| 26 // The minimum and maximum delays between attempts to launch the networking |
| 27 // process. |
| 28 const int kMaxLaunchDelaySeconds = 60; |
| 29 const int kMinLaunchDelaySeconds = 1; |
| 30 |
| 31 const FilePath::CharType kMe2meHostBinaryName[] = |
| 32 FILE_PATH_LITERAL("remoting_me2me_host.exe"); |
| 33 |
| 34 // The IPC channel name is passed to the networking process in the command line. |
| 35 const char kDaemonPipeSwitchName[] = "daemon-pipe"; |
| 36 |
| 37 // The command line parameters that should be copied from the service's command |
| 38 // line to the network process. |
| 39 const char* kCopiedSwitchNames[] = { |
| 40 "host-config", switches::kV, switches::kVModule }; |
| 41 |
| 42 // The security descriptor of the daemon IPC endpoint. It gives full access |
| 43 // to LocalSystem and denies access by anyone else. |
| 44 const char kDaemonPipeSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; |
| 45 |
| 46 } // namespace |
| 10 | 47 |
| 11 namespace remoting { | 48 namespace remoting { |
| 12 | 49 |
| 13 class DaemonProcessWin : public DaemonProcess { | 50 class DaemonProcessWin : public DaemonProcess, |
| 51 public WorkerProcessLauncher::Delegate { |
| 14 public: | 52 public: |
| 15 DaemonProcessWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | 53 DaemonProcessWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 16 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 54 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 17 const base::Closure& stopped_callback); | 55 const base::Closure& stopped_callback); |
| 18 virtual ~DaemonProcessWin(); | 56 virtual ~DaemonProcessWin(); |
| 19 | 57 |
| 58 virtual void OnChannelConnected() OVERRIDE; |
| 59 |
| 60 // Sends an IPC message to the worker process. This method can be called only |
| 61 // after successful Start() and until Stop() is called or an error occurred. |
| 62 virtual void Send(IPC::Message* message) OVERRIDE; |
| 63 |
| 64 // WorkerProcessLauncher::Delegate implementation. |
| 65 virtual bool DoLaunchProcess( |
| 66 const std::string& channel_name, |
| 67 ScopedHandle* process_exit_event_out) OVERRIDE; |
| 68 virtual void DoKillProcess(DWORD exit_code) OVERRIDE; |
| 69 |
| 70 protected: |
| 71 // Stoppable implementation. |
| 72 virtual void DoStop() OVERRIDE; |
| 73 |
| 20 // DaemonProcess implementation. | 74 // DaemonProcess implementation. |
| 21 virtual bool LaunchNetworkProcess() OVERRIDE; | 75 virtual void LaunchNetworkProcess() OVERRIDE; |
| 22 | 76 |
| 23 private: | 77 private: |
| 78 // Called when the launcher reports the worker process has stopped. |
| 79 void OnLauncherStopped(); |
| 80 |
| 81 // True if the network process is connected to the daemon. |
| 82 bool connected_; |
| 83 |
| 84 // Time of the last launch attempt. |
| 85 base::Time launch_time_; |
| 86 |
| 87 // Current backoff delay. |
| 88 base::TimeDelta launch_backoff_; |
| 89 |
| 90 // Timer used to schedule the next attempt to launch the process. |
| 91 base::OneShotTimer<DaemonProcessWin> timer_; |
| 92 |
| 93 scoped_ptr<WorkerProcessLauncher> launcher_; |
| 94 |
| 95 ScopedHandle network_process_; |
| 96 |
| 24 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); | 97 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); |
| 25 }; | 98 }; |
| 26 | 99 |
| 27 DaemonProcessWin::DaemonProcessWin( | 100 DaemonProcessWin::DaemonProcessWin( |
| 28 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | 101 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 29 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 102 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 30 const base::Closure& stopped_callback) | 103 const base::Closure& stopped_callback) |
| 31 : DaemonProcess(main_task_runner, io_task_runner, stopped_callback) { | 104 : DaemonProcess(main_task_runner, io_task_runner, stopped_callback), |
| 105 connected_(false) { |
| 32 } | 106 } |
| 33 | 107 |
| 34 DaemonProcessWin::~DaemonProcessWin() { | 108 DaemonProcessWin::~DaemonProcessWin() { |
| 109 // Make sure that the object is completely stopped. The same check exists |
| 110 // in Stoppable::~Stoppable() but this one allows us to examine the state of |
| 111 // the object before destruction. |
| 112 CHECK_EQ(stoppable_state(), Stoppable::kStopped); |
| 35 } | 113 } |
| 36 | 114 |
| 37 bool DaemonProcessWin::LaunchNetworkProcess() { | 115 void DaemonProcessWin::LaunchNetworkProcess() { |
| 38 NOTIMPLEMENTED(); | 116 DCHECK(main_task_runner()->BelongsToCurrentThread()); |
| 39 return false; | 117 DCHECK(launcher_.get() == NULL); |
| 118 DCHECK(!network_process_.IsValid()); |
| 119 DCHECK(!timer_.IsRunning()); |
| 120 |
| 121 launch_time_ = base::Time::Now(); |
| 122 launcher_.reset(new WorkerProcessLauncher( |
| 123 this, this, |
| 124 base::Bind(&DaemonProcessWin::OnLauncherStopped, base::Unretained(this)), |
| 125 main_task_runner(), |
| 126 io_task_runner())); |
| 127 launcher_->Start(kDaemonPipeSecurityDescriptor); |
| 128 } |
| 129 |
| 130 void DaemonProcessWin::OnChannelConnected() { |
| 131 connected_ = true; |
| 132 DaemonProcess::OnChannelConnected(); |
| 133 } |
| 134 |
| 135 void DaemonProcessWin::Send(IPC::Message* message) { |
| 136 if (connected_) { |
| 137 launcher_->Send(message); |
| 138 } else { |
| 139 delete message; |
| 140 } |
| 141 } |
| 142 |
| 143 bool DaemonProcessWin::DoLaunchProcess( |
| 144 const std::string& channel_name, |
| 145 ScopedHandle* process_exit_event_out) { |
| 146 DCHECK(main_task_runner()->BelongsToCurrentThread()); |
| 147 DCHECK(!network_process_.IsValid()); |
| 148 |
| 149 // Construct the host binary name. |
| 150 FilePath dir_path; |
| 151 if (!PathService::Get(base::DIR_EXE, &dir_path)) { |
| 152 LOG(ERROR) << "Failed to get the executable file name."; |
| 153 return false; |
| 154 } |
| 155 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); |
| 156 |
| 157 // Create the host process command line passing the name of the IPC channel |
| 158 // to use and copying known switches from the service's command line. |
| 159 CommandLine command_line(host_binary); |
| 160 command_line.AppendSwitchNative(kDaemonPipeSwitchName, |
| 161 UTF8ToWide(channel_name)); |
| 162 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), |
| 163 kCopiedSwitchNames, |
| 164 _countof(kCopiedSwitchNames)); |
| 165 |
| 166 ScopedHandle token; |
| 167 if (!OpenProcessToken(GetCurrentProcess(), |
| 168 MAXIMUM_ALLOWED, |
| 169 token.Receive())) { |
| 170 LOG_GETLASTERROR(FATAL) << "Failed to open process token"; |
| 171 return false; |
| 172 } |
| 173 |
| 174 // Try to launch the process and attach an object watcher to the returned |
| 175 // handle so that we get notified when the process terminates. |
| 176 // TODO(alexeypa): Pass a restricted process token. |
| 177 // See http://crbug.com/134694. |
| 178 ScopedHandle worker_thread; |
| 179 if (!LaunchProcessWithToken(host_binary, |
| 180 command_line.GetCommandLineString(), |
| 181 token, |
| 182 0, |
| 183 &network_process_, |
| 184 &worker_thread)) { |
| 185 return false; |
| 186 } |
| 187 |
| 188 ScopedHandle process_exit_event; |
| 189 if (!DuplicateHandle(GetCurrentProcess(), |
| 190 network_process_, |
| 191 GetCurrentProcess(), |
| 192 process_exit_event.Receive(), |
| 193 SYNCHRONIZE, |
| 194 FALSE, |
| 195 0)) { |
| 196 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; |
| 197 DoKillProcess(CONTROL_C_EXIT); |
| 198 return false; |
| 199 } |
| 200 |
| 201 *process_exit_event_out = process_exit_event.Pass(); |
| 202 return true; |
| 203 } |
| 204 |
| 205 void DaemonProcessWin::DoKillProcess(DWORD exit_code) { |
| 206 DCHECK(main_task_runner()->BelongsToCurrentThread()); |
| 207 CHECK(network_process_.IsValid()); |
| 208 |
| 209 TerminateProcess(network_process_, exit_code); |
| 210 } |
| 211 |
| 212 void DaemonProcessWin::DoStop() { |
| 213 DCHECK(main_task_runner()->BelongsToCurrentThread()); |
| 214 |
| 215 launch_backoff_ = base::TimeDelta(); |
| 216 timer_.Stop(); |
| 217 |
| 218 if (launcher_.get() != NULL) { |
| 219 launcher_->Stop(); |
| 220 } |
| 221 |
| 222 // Early exit if we're still waiting for |launcher_| to stop. |
| 223 if (launcher_.get() != NULL) { |
| 224 return; |
| 225 } |
| 226 |
| 227 DaemonProcess::DoStop(); |
| 228 } |
| 229 |
| 230 void DaemonProcessWin::OnLauncherStopped() { |
| 231 DCHECK(main_task_runner()->BelongsToCurrentThread()); |
| 232 CHECK(network_process_.IsValid()); |
| 233 |
| 234 DWORD exit_code = CONTROL_C_EXIT; |
| 235 if (!::GetExitCodeProcess(network_process_, &exit_code)) { |
| 236 LOG_GETLASTERROR(INFO) |
| 237 << "Failed to query the exit code of the worker process"; |
| 238 exit_code = CONTROL_C_EXIT; |
| 239 } |
| 240 |
| 241 network_process_.Close(); |
| 242 connected_ = false; |
| 243 launcher_.reset(NULL); |
| 244 |
| 245 // Do not relaunch the network process if the caller has asked us to stop. |
| 246 if (stoppable_state() != Stoppable::kRunning) { |
| 247 Stop(); |
| 248 return; |
| 249 } |
| 250 |
| 251 // Stop trying to restart the worker process if its process exited due to |
| 252 // misconfiguration. |
| 253 if (kMinPermanentErrorExitCode <= exit_code && |
| 254 exit_code <= kMaxPermanentErrorExitCode) { |
| 255 Stop(); |
| 256 return; |
| 257 } |
| 258 |
| 259 // Expand the backoff interval if the process has died quickly or reset it |
| 260 // if it was up longer than the maximum backoff delay. |
| 261 base::TimeDelta delta = base::Time::Now() - launch_time_; |
| 262 if (delta < base::TimeDelta() || |
| 263 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { |
| 264 launch_backoff_ = base::TimeDelta(); |
| 265 } else { |
| 266 launch_backoff_ = std::max( |
| 267 launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); |
| 268 launch_backoff_ = std::min( |
| 269 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); |
| 270 } |
| 271 |
| 272 // Try to launch the worker process. |
| 273 timer_.Start(FROM_HERE, launch_backoff_, |
| 274 this, &DaemonProcessWin::LaunchNetworkProcess); |
| 40 } | 275 } |
| 41 | 276 |
| 42 scoped_ptr<DaemonProcess> DaemonProcess::Create( | 277 scoped_ptr<DaemonProcess> DaemonProcess::Create( |
| 43 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | 278 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 44 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 279 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 45 const base::Closure& stopped_callback) { | 280 const base::Closure& stopped_callback) { |
| 46 scoped_ptr<DaemonProcessWin> daemon_process( | 281 scoped_ptr<DaemonProcessWin> daemon_process( |
| 47 new DaemonProcessWin(main_task_runner, io_task_runner, stopped_callback)); | 282 new DaemonProcessWin(main_task_runner, io_task_runner, stopped_callback)); |
| 48 return daemon_process.PassAs<DaemonProcess>(); | 283 return daemon_process.PassAs<DaemonProcess>(); |
| 49 } | 284 } |
| 50 | 285 |
| 51 } // namespace remoting | 286 } // namespace remoting |
| OLD | NEW |