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 3 // found in the LICENSE file. 4 5 #include "remoting/host/win/worker_process_launcher.h" 6 7 #include "base/location.h" 8 #include "base/logging.h" 9 #include "base/single_thread_task_runner.h" 10 #include "base/time/time.h" 11 #include "base/win/windows_version.h" 12 #include "ipc/ipc_message.h" 13 #include "remoting/host/chromoting_messages.h" 14 #include "remoting/host/host_exit_codes.h" 15 #include "remoting/host/worker_process_ipc_delegate.h" 16 17 using base::TimeDelta; 18 using base::win::ScopedHandle; 19 20 const net::BackoffEntry::Policy kDefaultBackoffPolicy = { 21 // Number of initial errors (in sequence) to ignore before applying 22 // exponential back-off rules. 23 0, 24 25 // Initial delay for exponential back-off in ms. 26 100, 27 28 // Factor by which the waiting time will be multiplied. 29 2, 30 31 // Fuzzing percentage. ex: 10% will spread requests randomly 32 // between 90%-100% of the calculated time. 33 0, 34 35 // Maximum amount of time we are willing to delay our request in ms. 36 60000, 37 38 // Time to keep an entry from being discarded even when it 39 // has no significant state, -1 to never discard. 40 -1, 41 42 // Don't use initial delay unless the last request was an error. 43 false, 44 }; 45 46 const int kKillProcessTimeoutSeconds = 5; 47 const int kLaunchResultTimeoutSeconds = 5; 48 49 namespace remoting { 50 51 WorkerProcessLauncher::Delegate::~Delegate() { 52 } 53 54 WorkerProcessLauncher::WorkerProcessLauncher( 55 scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate, 56 WorkerProcessIpcDelegate* ipc_handler) 57 : ipc_handler_(ipc_handler), 58 launcher_delegate_(launcher_delegate.Pass()), 59 exit_code_(CONTROL_C_EXIT), 60 ipc_enabled_(false), 61 kill_process_timeout_( 62 base::TimeDelta::FromSeconds(kKillProcessTimeoutSeconds)), 63 launch_backoff_(&kDefaultBackoffPolicy) { 64 DCHECK(ipc_handler_ != NULL); 65 66 LaunchWorker(); 67 } 68 69 WorkerProcessLauncher::~WorkerProcessLauncher() { 70 DCHECK(CalledOnValidThread()); 71 72 ipc_handler_ = NULL; 73 StopWorker(); 74 } 75 76 void WorkerProcessLauncher::Crash( 77 const tracked_objects::Location& location) { 78 DCHECK(CalledOnValidThread()); 79 80 // Ask the worker process to crash voluntarily if it is still connected. 81 if (ipc_enabled_) { 82 Send(new ChromotingDaemonMsg_Crash(location.function_name(), 83 location.file_name(), 84 location.line_number())); 85 } 86 87 // Close the channel and ignore any not yet processed messages. 88 launcher_delegate_->CloseChannel(); 89 ipc_enabled_ = false; 90 91 // Give the worker process some time to crash. 92 if (!kill_process_timer_.IsRunning()) { 93 kill_process_timer_.Start(FROM_HERE, kill_process_timeout_, this, 94 &WorkerProcessLauncher::StopWorker); 95 } 96 } 97 98 void WorkerProcessLauncher::Send(IPC::Message* message) { 99 DCHECK(CalledOnValidThread()); 100 101 if (ipc_enabled_) { 102 launcher_delegate_->Send(message); 103 } else { 104 delete message; 105 } 106 } 107 108 void WorkerProcessLauncher::OnProcessLaunched( 109 base::win::ScopedHandle worker_process) { 110 DCHECK(CalledOnValidThread()); 111 DCHECK(!ipc_enabled_); 112 DCHECK(!launch_timer_.IsRunning()); 113 DCHECK(!process_watcher_.GetWatchedObject()); 114 DCHECK(!worker_process_.IsValid()); 115 116 if (!process_watcher_.StartWatching(worker_process, this)) { 117 StopWorker(); 118 return; 119 } 120 121 ipc_enabled_ = true; 122 worker_process_ = worker_process.Pass(); 123 } 124 125 void WorkerProcessLauncher::OnFatalError() { 126 DCHECK(CalledOnValidThread()); 127 128 StopWorker(); 129 } 130 131 bool WorkerProcessLauncher::OnMessageReceived( 132 const IPC::Message& message) { 133 DCHECK(CalledOnValidThread()); 134 135 if (!ipc_enabled_) 136 return false; 137 138 return ipc_handler_->OnMessageReceived(message); 139 } 140 141 void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid) { 142 DCHECK(CalledOnValidThread()); 143 144 if (!ipc_enabled_) 145 return; 146 147 // This can result in |this| being deleted, so this call must be the last in 148 // this method. 149 ipc_handler_->OnChannelConnected(peer_pid); 150 } 151 152 void WorkerProcessLauncher::OnChannelError() { 153 DCHECK(CalledOnValidThread()); 154 155 // Schedule a delayed termination of the worker process. Usually, the pipe is 156 // disconnected when the worker process is about to exit. Waiting a little bit 157 // here allows the worker to exit completely and so, notify 158 // |process_watcher_|. As the result KillProcess() will not be called and 159 // the original exit code reported by the worker process will be retrieved. 160 if (!kill_process_timer_.IsRunning()) { 161 kill_process_timer_.Start(FROM_HERE, kill_process_timeout_, this, 162 &WorkerProcessLauncher::StopWorker); 163 } 164 } 165 166 void WorkerProcessLauncher::OnObjectSignaled(HANDLE object) { 167 DCHECK(CalledOnValidThread()); 168 DCHECK(!process_watcher_.GetWatchedObject()); 169 DCHECK_EQ(exit_code_, CONTROL_C_EXIT); 170 DCHECK_EQ(worker_process_, object); 171 172 // Get exit code of the worker process if it is available. 173 if (!::GetExitCodeProcess(worker_process_, &exit_code_)) { 174 PLOG(INFO) << "Failed to query the exit code of the worker process"; 175 exit_code_ = CONTROL_C_EXIT; 176 } 177 178 worker_process_.Close(); 179 StopWorker(); 180 } 181 182 void WorkerProcessLauncher::LaunchWorker() { 183 DCHECK(CalledOnValidThread()); 184 DCHECK(!ipc_enabled_); 185 DCHECK(!kill_process_timer_.IsRunning()); 186 DCHECK(!launch_timer_.IsRunning()); 187 DCHECK(!process_watcher_.GetWatchedObject()); 188 DCHECK(!launch_result_timer_.IsRunning()); 189 190 exit_code_ = CONTROL_C_EXIT; 191 192 // Make sure launching a process will not take forever. 193 launch_result_timer_.Start( 194 FROM_HERE, base::TimeDelta::FromSeconds(kLaunchResultTimeoutSeconds), 195 this, &WorkerProcessLauncher::RecordLaunchResult); 196 197 launcher_delegate_->LaunchProcess(this); 198 } 199 200 void WorkerProcessLauncher::RecordLaunchResult() { 201 DCHECK(CalledOnValidThread()); 202 203 if (!worker_process_.IsValid()) { 204 LOG(WARNING) << "A worker process failed to start within " 205 << kLaunchResultTimeoutSeconds << " seconds."; 206 207 launch_backoff_.InformOfRequest(false); 208 StopWorker(); 209 return; 210 } 211 212 // Assume success if the worker process has been running for a few seconds. 213 launch_backoff_.InformOfRequest(true); 214 } 215 216 void WorkerProcessLauncher::RecordSuccessfulLaunchForTest() { 217 DCHECK(CalledOnValidThread()); 218 219 if (launch_result_timer_.IsRunning()) { 220 launch_result_timer_.Stop(); 221 RecordLaunchResult(); 222 } 223 } 224 225 void WorkerProcessLauncher::SetKillProcessTimeoutForTest( 226 const base::TimeDelta& timeout) { 227 DCHECK(CalledOnValidThread()); 228 229 kill_process_timeout_ = timeout; 230 } 231 232 void WorkerProcessLauncher::StopWorker() { 233 DCHECK(CalledOnValidThread()); 234 235 // Record a launch failure if the process exited too soon. 236 if (launch_result_timer_.IsRunning()) { 237 launch_backoff_.InformOfRequest(false); 238 launch_result_timer_.Stop(); 239 } 240 241 // Ignore any remaining IPC messages. 242 ipc_enabled_ = false; 243 244 // Stop monitoring the worker process. 245 process_watcher_.StopWatching(); 246 worker_process_.Close(); 247 248 kill_process_timer_.Stop(); 249 launcher_delegate_->KillProcess(); 250 251 // Do not relaunch the worker process if the caller has asked us to stop. 252 if (stopping()) 253 return; 254 255 // Stop trying to restart the worker process if it exited due to 256 // misconfiguration. 257 if (kMinPermanentErrorExitCode <= exit_code_ && 258 exit_code_ <= kMaxPermanentErrorExitCode) { 259 ipc_handler_->OnPermanentError(exit_code_); 260 return; 261 } 262 263 // Schedule the next attempt to launch the worker process. 264 launch_timer_.Start(FROM_HERE, launch_backoff_.GetTimeUntilRelease(), this, 265 &WorkerProcessLauncher::LaunchWorker); 266 } 267 268 } // namespace remoting 269