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 LOG_GETLASTERROR(INFO) 175 << "Failed to query the exit code of the worker process"; 176 exit_code_ = CONTROL_C_EXIT; 177 } 178 179 worker_process_.Close(); 180 StopWorker(); 181 } 182 183 void WorkerProcessLauncher::LaunchWorker() { 184 DCHECK(CalledOnValidThread()); 185 DCHECK(!ipc_enabled_); 186 DCHECK(!kill_process_timer_.IsRunning()); 187 DCHECK(!launch_timer_.IsRunning()); 188 DCHECK(!process_watcher_.GetWatchedObject()); 189 DCHECK(!launch_result_timer_.IsRunning()); 190 191 exit_code_ = CONTROL_C_EXIT; 192 193 // Make sure launching a process will not take forever. 194 launch_result_timer_.Start( 195 FROM_HERE, base::TimeDelta::FromSeconds(kLaunchResultTimeoutSeconds), 196 this, &WorkerProcessLauncher::RecordLaunchResult); 197 198 launcher_delegate_->LaunchProcess(this); 199 } 200 201 void WorkerProcessLauncher::RecordLaunchResult() { 202 DCHECK(CalledOnValidThread()); 203 204 if (!worker_process_.IsValid()) { 205 LOG(WARNING) << "A worker process failed to start within " 206 << kLaunchResultTimeoutSeconds << " seconds."; 207 208 launch_backoff_.InformOfRequest(false); 209 StopWorker(); 210 return; 211 } 212 213 // Assume success if the worker process has been running for a few seconds. 214 launch_backoff_.InformOfRequest(true); 215 } 216 217 void WorkerProcessLauncher::RecordSuccessfulLaunchForTest() { 218 DCHECK(CalledOnValidThread()); 219 220 if (launch_result_timer_.IsRunning()) { 221 launch_result_timer_.Stop(); 222 RecordLaunchResult(); 223 } 224 } 225 226 void WorkerProcessLauncher::SetKillProcessTimeoutForTest( 227 const base::TimeDelta& timeout) { 228 DCHECK(CalledOnValidThread()); 229 230 kill_process_timeout_ = timeout; 231 } 232 233 void WorkerProcessLauncher::StopWorker() { 234 DCHECK(CalledOnValidThread()); 235 236 // Record a launch failure if the process exited too soon. 237 if (launch_result_timer_.IsRunning()) { 238 launch_backoff_.InformOfRequest(false); 239 launch_result_timer_.Stop(); 240 } 241 242 // Ignore any remaining IPC messages. 243 ipc_enabled_ = false; 244 245 // Stop monitoring the worker process. 246 process_watcher_.StopWatching(); 247 worker_process_.Close(); 248 249 kill_process_timer_.Stop(); 250 launcher_delegate_->KillProcess(); 251 252 // Do not relaunch the worker process if the caller has asked us to stop. 253 if (stopping()) 254 return; 255 256 // Stop trying to restart the worker process if it exited due to 257 // misconfiguration. 258 if (kMinPermanentErrorExitCode <= exit_code_ && 259 exit_code_ <= kMaxPermanentErrorExitCode) { 260 ipc_handler_->OnPermanentError(exit_code_); 261 return; 262 } 263 264 // Schedule the next attempt to launch the worker process. 265 launch_timer_.Start(FROM_HERE, launch_backoff_.GetTimeUntilRelease(), this, 266 &WorkerProcessLauncher::LaunchWorker); 267 } 268 269 } // namespace remoting 270