• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
~Delegate()51 WorkerProcessLauncher::Delegate::~Delegate() {
52 }
53 
WorkerProcessLauncher(scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate,WorkerProcessIpcDelegate * ipc_handler)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 
~WorkerProcessLauncher()69 WorkerProcessLauncher::~WorkerProcessLauncher() {
70   DCHECK(CalledOnValidThread());
71 
72   ipc_handler_ = NULL;
73   StopWorker();
74 }
75 
Crash(const tracked_objects::Location & location)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 
Send(IPC::Message * message)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 
OnProcessLaunched(base::win::ScopedHandle worker_process)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.Get(), this)) {
117     StopWorker();
118     return;
119   }
120 
121   ipc_enabled_ = true;
122   worker_process_ = worker_process.Pass();
123 }
124 
OnFatalError()125 void WorkerProcessLauncher::OnFatalError() {
126   DCHECK(CalledOnValidThread());
127 
128   StopWorker();
129 }
130 
OnMessageReceived(const IPC::Message & message)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 
OnChannelConnected(int32 peer_pid)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 
OnChannelError()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 
OnObjectSignaled(HANDLE object)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_.Get(), object);
171 
172   // Get exit code of the worker process if it is available.
173   if (!::GetExitCodeProcess(worker_process_.Get(), &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 
LaunchWorker()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 
RecordLaunchResult()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 
RecordSuccessfulLaunchForTest()216 void WorkerProcessLauncher::RecordSuccessfulLaunchForTest() {
217   DCHECK(CalledOnValidThread());
218 
219   if (launch_result_timer_.IsRunning()) {
220     launch_result_timer_.Stop();
221     RecordLaunchResult();
222   }
223 }
224 
SetKillProcessTimeoutForTest(const base::TimeDelta & timeout)225 void WorkerProcessLauncher::SetKillProcessTimeoutForTest(
226     const base::TimeDelta& timeout) {
227   DCHECK(CalledOnValidThread());
228 
229   kill_process_timeout_ = timeout;
230 }
231 
StopWorker()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