Chromium Code Reviews| 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 host process. | |
|
Wez
2012/08/21 00:18:57
nit: host process -> network process?
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 39 const char* kCopiedSwitchNames[] = { | |
| 40 "auth-config", "host-config", switches::kV, switches::kVModule }; | |
| 41 | |
| 42 // The security descriptor of the Chromoting IPC channel. It gives full access | |
|
Wez
2012/08/21 00:18:57
nit: Chromoting -> daemon, or just remove qualifie
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 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 |
| 20 // DaemonProcess implementation. | 58 // DaemonProcess implementation. |
| 21 virtual bool LaunchNetworkProcess() OVERRIDE; | 59 virtual void LaunchNetworkProcess() OVERRIDE; |
| 60 | |
| 61 // WorkerProcessDelegate implementation. | |
|
Wez
2012/08/21 00:18:57
nit: WorkerProcessDelegate is inherited via Daemon
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 62 virtual void OnChannelConnected() OVERRIDE; | |
| 63 | |
| 64 // Sends an IPC message to the worker process. This method can be called only | |
| 65 // after successful Start() and until Stop() is called or an error occurred. | |
| 66 virtual void Send(IPC::Message* message) OVERRIDE; | |
| 67 | |
| 68 // WorkerProcessLauncher::Delegate implementation. | |
| 69 virtual bool DoLaunchProcess( | |
| 70 const std::string& channel_name, | |
| 71 ScopedHandle* process_exit_event_out) OVERRIDE; | |
| 72 virtual void DoKillProcess(DWORD exit_code) OVERRIDE; | |
| 73 | |
| 74 protected: | |
| 75 // Stoppable implementation. | |
| 76 virtual void DoStop() OVERRIDE; | |
| 22 | 77 |
| 23 private: | 78 private: |
| 79 // Called when the launcher reports the process to be stopped. | |
|
Wez
2012/08/21 00:18:57
nit: ... reports the worker process has stopped.
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 80 void OnLauncherStopped(); | |
| 81 | |
| 82 // True if the network process is connected. | |
|
Wez
2012/08/21 00:18:57
nit: Connected to the Daemon, connected to Talk, o
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 83 bool connected_; | |
| 84 | |
| 85 // Time of the last launch attempt. | |
| 86 base::Time launch_time_; | |
| 87 | |
| 88 // Current backoff delay. | |
| 89 base::TimeDelta launch_backoff_; | |
| 90 | |
| 91 // Timer used to schedule the next attempt to launch the process. | |
| 92 base::OneShotTimer<DaemonProcessWin> timer_; | |
| 93 | |
| 94 scoped_ptr<WorkerProcessLauncher> launcher_; | |
| 95 | |
| 96 ScopedHandle network_process_; | |
| 97 | |
| 24 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); | 98 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); |
| 25 }; | 99 }; |
| 26 | 100 |
| 27 DaemonProcessWin::DaemonProcessWin( | 101 DaemonProcessWin::DaemonProcessWin( |
| 28 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | 102 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 29 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 103 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 30 const base::Closure& stopped_callback) | 104 const base::Closure& stopped_callback) |
| 31 : DaemonProcess(main_task_runner, io_task_runner, stopped_callback) { | 105 : DaemonProcess(main_task_runner, io_task_runner, stopped_callback), |
| 106 connected_(false) { | |
| 32 } | 107 } |
| 33 | 108 |
| 34 DaemonProcessWin::~DaemonProcessWin() { | 109 DaemonProcessWin::~DaemonProcessWin() { |
| 110 // Make sure that the object is completely stopped. The same check exists | |
| 111 // in Stoppable::~Stoppable() but this one allows us to examine the state of | |
| 112 // the object before destruction. | |
|
Wez
2012/08/21 00:18:57
But you're not examining any state; aren't you rea
alexeypa (please no reviews)
2012/08/21 17:16:36
In this case "us" are human beings equipped with a
Wez
2012/08/24 22:20:37
OK, so the comment is just misleading. ;) It's imp
alexeypa (please no reviews)
2012/08/27 23:16:41
OK, let try another version.
In the cases like th
Wez
2012/08/30 20:36:04
You're right - sorry, I'll try to do that in futur
| |
| 113 CHECK_EQ(stoppable_state(), Stoppable::kStopped); | |
| 35 } | 114 } |
| 36 | 115 |
| 37 bool DaemonProcessWin::LaunchNetworkProcess() { | 116 void DaemonProcessWin::LaunchNetworkProcess() { |
| 38 NOTIMPLEMENTED(); | 117 DCHECK(main_task_runner()->BelongsToCurrentThread()); |
| 39 return false; | 118 DCHECK(launcher_.get() == NULL); |
| 119 DCHECK(!network_process_.IsValid()); | |
| 120 DCHECK(!timer_.IsRunning()); | |
| 121 | |
| 122 launch_time_ = base::Time::Now(); | |
| 123 launcher_.reset(new WorkerProcessLauncher( | |
| 124 this, this, | |
| 125 base::Bind(&DaemonProcessWin::OnLauncherStopped, base::Unretained(this)), | |
| 126 main_task_runner(), | |
| 127 io_task_runner())); | |
| 128 launcher_->Start(kDaemonPipeSecurityDescriptor); | |
| 129 } | |
| 130 | |
| 131 void DaemonProcessWin::OnChannelConnected() { | |
| 132 connected_ = true; | |
| 133 DaemonProcess::OnChannelConnected(); | |
| 134 } | |
| 135 | |
| 136 void DaemonProcessWin::Send(IPC::Message* message) { | |
| 137 if (connected_) { | |
| 138 launcher_->Send(message); | |
| 139 } else { | |
| 140 delete message; | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 bool DaemonProcessWin::DoLaunchProcess( | |
| 145 const std::string& channel_name, | |
| 146 ScopedHandle* process_exit_event_out) { | |
| 147 DCHECK(main_task_runner()->BelongsToCurrentThread()); | |
| 148 DCHECK(!network_process_.IsValid()); | |
| 149 | |
| 150 // Construct the host binary name. | |
| 151 FilePath dir_path; | |
| 152 if (!PathService::Get(base::DIR_EXE, &dir_path)) { | |
| 153 LOG(ERROR) << "Failed to get the executable file name."; | |
| 154 return false; | |
| 155 } | |
| 156 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); | |
| 157 | |
| 158 // Create the host process command line passing the name of the IPC channel | |
| 159 // to use and copying known switches from the service's command line. | |
| 160 CommandLine command_line(host_binary); | |
| 161 command_line.AppendSwitchNative(kDaemonPipeSwitchName, | |
| 162 UTF8ToWide(channel_name)); | |
| 163 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), | |
| 164 kCopiedSwitchNames, | |
| 165 _countof(kCopiedSwitchNames)); | |
| 166 | |
| 167 // TODO(alexeypa): replace with a restricted token. | |
| 168 // See http://crbug.com/134694. | |
|
Wez
2012/08/21 00:18:57
nit: It's not clear what this comment means, even
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 169 ScopedHandle token; | |
| 170 if (!OpenProcessToken(GetCurrentProcess(), | |
| 171 MAXIMUM_ALLOWED, | |
| 172 token.Receive())) { | |
| 173 LOG_GETLASTERROR(ERROR) << "Failed to open process token"; | |
|
Wez
2012/08/21 00:18:57
Is there ever a situation in which this can happen
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 // Try to launch the process and attach an object watcher to the returned | |
| 178 // handle so that we get notified when the process terminates. | |
| 179 ScopedHandle worker_thread; | |
| 180 if (!LaunchProcessWithToken(host_binary, | |
| 181 command_line.GetCommandLineString(), | |
| 182 token, | |
| 183 0, | |
| 184 &network_process_, | |
| 185 &worker_thread)) { | |
| 186 return false; | |
| 187 } | |
| 188 | |
| 189 ScopedHandle process_exit_event; | |
| 190 if (!DuplicateHandle(GetCurrentProcess(), | |
| 191 network_process_, | |
| 192 GetCurrentProcess(), | |
| 193 process_exit_event.Receive(), | |
| 194 SYNCHRONIZE, | |
| 195 FALSE, | |
| 196 0)) { | |
| 197 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; | |
| 198 DoKillProcess(CONTROL_C_EXIT); | |
| 199 return false; | |
| 200 } | |
| 201 | |
| 202 *process_exit_event_out = process_exit_event.Pass(); | |
| 203 return true; | |
| 204 } | |
| 205 | |
| 206 void DaemonProcessWin::DoKillProcess(DWORD exit_code) { | |
| 207 DCHECK(main_task_runner()->BelongsToCurrentThread()); | |
| 208 | |
| 209 if (network_process_.IsValid()) { | |
| 210 TerminateProcess(network_process_, exit_code); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 void DaemonProcessWin::DoStop() { | |
| 215 DCHECK(main_task_runner()->BelongsToCurrentThread()); | |
| 216 | |
| 217 launch_backoff_ = base::TimeDelta(); | |
|
Wez
2012/08/21 00:18:57
Why zero the backoff here? Isn't it useful to let
alexeypa (please no reviews)
2012/08/21 17:16:36
It will not start starting/stopping rapidly becaus
| |
| 218 timer_.Stop(); | |
| 219 | |
| 220 if (launcher_.get() != NULL) { | |
| 221 launcher_->Stop(); | |
| 222 } | |
| 223 | |
| 224 // Wait for |launcher_| to be completely stopped. | |
|
Wez
2012/08/21 00:18:57
nit: Suggest: "Early exit if we're still waiting f
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 225 if (launcher_.get() != NULL) { | |
| 226 return; | |
| 227 } | |
| 228 | |
| 229 DaemonProcess::DoStop(); | |
| 230 } | |
| 231 | |
| 232 void DaemonProcessWin::OnLauncherStopped() { | |
| 233 DCHECK(main_task_runner()->BelongsToCurrentThread()); | |
| 234 | |
| 235 DWORD exit_code = CONTROL_C_EXIT; | |
| 236 if (network_process_.IsValid()) { | |
|
Wez
2012/08/21 00:18:57
Should it be possible for |network_process_| not t
alexeypa (please no reviews)
2012/08/23 23:53:01
Done.
| |
| 237 if (!::GetExitCodeProcess(network_process_, &exit_code)) { | |
| 238 LOG_GETLASTERROR(INFO) | |
| 239 << "Failed to query the exit code of the worker process"; | |
| 240 exit_code = CONTROL_C_EXIT; | |
| 241 } | |
| 242 | |
| 243 network_process_.Close(); | |
| 244 } | |
| 245 | |
| 246 connected_ = false; | |
| 247 launcher_.reset(NULL); | |
| 248 | |
| 249 // Do not relaunch the network process if the caller has asked us to stop. | |
| 250 if (stoppable_state() != Stoppable::kRunning) { | |
| 251 Stop(); | |
| 252 return; | |
| 253 } | |
| 254 | |
| 255 // Stop trying to restart the worker process if its process exited due to | |
| 256 // misconfiguration. | |
| 257 if (kMinPermanentErrorExitCode <= exit_code && | |
| 258 exit_code <= kMaxPermanentErrorExitCode) { | |
| 259 Stop(); | |
| 260 return; | |
| 261 } | |
| 262 | |
| 263 // Expand the backoff interval if the process has died quickly or reset it | |
| 264 // if it was up longer than the maximum backoff delay. | |
| 265 base::TimeDelta delta = base::Time::Now() - launch_time_; | |
| 266 if (delta < base::TimeDelta() || | |
| 267 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { | |
| 268 launch_backoff_ = base::TimeDelta(); | |
| 269 } else { | |
| 270 launch_backoff_ = std::max( | |
| 271 launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); | |
| 272 launch_backoff_ = std::min( | |
| 273 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); | |
| 274 } | |
| 275 | |
| 276 // Try to launch the worker process. | |
| 277 timer_.Start(FROM_HERE, launch_backoff_, | |
| 278 this, &DaemonProcessWin::LaunchNetworkProcess); | |
| 40 } | 279 } |
| 41 | 280 |
| 42 scoped_ptr<DaemonProcess> DaemonProcess::Create( | 281 scoped_ptr<DaemonProcess> DaemonProcess::Create( |
| 43 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | 282 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| 44 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 283 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 45 const base::Closure& stopped_callback) { | 284 const base::Closure& stopped_callback) { |
| 46 scoped_ptr<DaemonProcessWin> daemon_process( | 285 scoped_ptr<DaemonProcessWin> daemon_process( |
| 47 new DaemonProcessWin(main_task_runner, io_task_runner, stopped_callback)); | 286 new DaemonProcessWin(main_task_runner, io_task_runner, stopped_callback)); |
| 48 return daemon_process.PassAs<DaemonProcess>(); | 287 return daemon_process.PassAs<DaemonProcess>(); |
| 49 } | 288 } |
| 50 | 289 |
| 51 } // namespace remoting | 290 } // namespace remoting |
| OLD | NEW |