child_process_launcher.cc revision 4ad1aa43a48567659193a298fad74f55e00b3dd9
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 "content/browser/child_process_launcher.h"
6
7#include <utility>  // For std::pair.
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/file_util.h"
12#include "base/files/scoped_file.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/metrics/histogram.h"
16#include "base/process/process.h"
17#include "base/synchronization/lock.h"
18#include "base/threading/thread.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/browser/content_browser_client.h"
21#include "content/public/common/content_descriptors.h"
22#include "content/public/common/content_switches.h"
23#include "content/public/common/result_codes.h"
24#include "content/public/common/sandboxed_process_launcher_delegate.h"
25
26#if defined(OS_WIN)
27#include "base/files/file_path.h"
28#include "content/common/sandbox_win.h"
29#include "content/public/common/sandbox_init.h"
30#elif defined(OS_MACOSX)
31#include "content/browser/mach_broker_mac.h"
32#elif defined(OS_ANDROID)
33#include "base/android/jni_android.h"
34#include "content/browser/android/child_process_launcher_android.h"
35#elif defined(OS_POSIX)
36#include "base/memory/shared_memory.h"
37#include "base/memory/singleton.h"
38#include "content/browser/renderer_host/render_sandbox_host_linux.h"
39#include "content/browser/zygote_host/zygote_host_impl_linux.h"
40#include "content/common/child_process_sandbox_support_impl_linux.h"
41#endif
42
43#if defined(OS_POSIX)
44#include "base/metrics/stats_table.h"
45#include "base/posix/global_descriptors.h"
46#endif
47
48namespace content {
49
50// Having the functionality of ChildProcessLauncher be in an internal
51// ref counted object allows us to automatically terminate the process when the
52// parent class destructs, while still holding on to state that we need.
53class ChildProcessLauncher::Context
54    : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
55 public:
56  Context()
57      : client_(NULL),
58        client_thread_id_(BrowserThread::UI),
59        termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
60        exit_code_(RESULT_CODE_NORMAL_EXIT),
61        starting_(true),
62        terminate_child_on_shutdown_(true)
63#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
64        , zygote_(false)
65#endif
66        {
67  }
68
69  void Launch(
70      SandboxedProcessLauncherDelegate* delegate,
71      CommandLine* cmd_line,
72      int child_process_id,
73      Client* client) {
74    client_ = client;
75
76    CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
77
78#if defined(OS_ANDROID)
79    // We need to close the client end of the IPC channel to reliably detect
80    // child termination. We will close this fd after we create the child
81    // process which is asynchronous on Android.
82    ipcfd_ = delegate->GetIpcFd();
83#endif
84    BrowserThread::PostTask(
85        BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
86        base::Bind(
87            &Context::LaunchInternal,
88            make_scoped_refptr(this),
89            client_thread_id_,
90            child_process_id,
91            delegate,
92            cmd_line));
93  }
94
95#if defined(OS_ANDROID)
96  static void OnChildProcessStarted(
97      // |this_object| is NOT thread safe. Only use it to post a task back.
98      scoped_refptr<Context> this_object,
99      BrowserThread::ID client_thread_id,
100      const base::TimeTicks begin_launch_time,
101      base::ProcessHandle handle) {
102    RecordHistograms(begin_launch_time);
103    if (BrowserThread::CurrentlyOn(client_thread_id)) {
104      // This is always invoked on the UI thread which is commonly the
105      // |client_thread_id| so we can shortcut one PostTask.
106      this_object->Notify(handle);
107    } else {
108      BrowserThread::PostTask(
109          client_thread_id, FROM_HERE,
110          base::Bind(
111              &ChildProcessLauncher::Context::Notify,
112              this_object,
113              handle));
114    }
115  }
116#endif
117
118  void ResetClient() {
119    // No need for locking as this function gets called on the same thread that
120    // client_ would be used.
121    CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
122    client_ = NULL;
123  }
124
125  void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
126    terminate_child_on_shutdown_ = terminate_on_shutdown;
127  }
128
129 private:
130  friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
131  friend class ChildProcessLauncher;
132
133  ~Context() {
134    Terminate();
135  }
136
137  static void RecordHistograms(const base::TimeTicks begin_launch_time) {
138    base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
139    if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
140      RecordLaunchHistograms(launch_time);
141    } else {
142      BrowserThread::PostTask(
143          BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
144          base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms,
145                     launch_time));
146    }
147  }
148
149  static void RecordLaunchHistograms(const base::TimeDelta launch_time) {
150    // Log the launch time, separating out the first one (which will likely be
151    // slower due to the rest of the browser initializing at the same time).
152    static bool done_first_launch = false;
153    if (done_first_launch) {
154      UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
155    } else {
156      UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
157      done_first_launch = true;
158    }
159  }
160
161  static void LaunchInternal(
162      // |this_object| is NOT thread safe. Only use it to post a task back.
163      scoped_refptr<Context> this_object,
164      BrowserThread::ID client_thread_id,
165      int child_process_id,
166      SandboxedProcessLauncherDelegate* delegate,
167      CommandLine* cmd_line) {
168    scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
169#if defined(OS_WIN)
170    bool launch_elevated = delegate->ShouldLaunchElevated();
171#elif defined(OS_ANDROID)
172    int ipcfd = delegate->GetIpcFd();
173#elif defined(OS_MACOSX)
174    base::EnvironmentMap env = delegate->GetEnvironment();
175    int ipcfd = delegate->GetIpcFd();
176#elif defined(OS_POSIX)
177    bool use_zygote = delegate->ShouldUseZygote();
178    base::EnvironmentMap env = delegate->GetEnvironment();
179    int ipcfd = delegate->GetIpcFd();
180#endif
181    scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
182    base::TimeTicks begin_launch_time = base::TimeTicks::Now();
183
184#if defined(OS_WIN)
185    base::ProcessHandle handle = base::kNullProcessHandle;
186    if (launch_elevated) {
187      base::LaunchOptions options;
188      options.start_hidden = true;
189      base::LaunchElevatedProcess(*cmd_line, options, &handle);
190    } else {
191      handle = StartSandboxedProcess(delegate, cmd_line);
192    }
193#elif defined(OS_POSIX)
194    std::string process_type =
195        cmd_line->GetSwitchValueASCII(switches::kProcessType);
196    std::vector<FileDescriptorInfo> files_to_register;
197    files_to_register.push_back(
198        FileDescriptorInfo(kPrimaryIPCChannel,
199                           base::FileDescriptor(ipcfd, false)));
200    base::StatsTable* stats_table = base::StatsTable::current();
201    if (stats_table &&
202        base::SharedMemory::IsHandleValid(
203            stats_table->GetSharedMemoryHandle())) {
204      files_to_register.push_back(
205          FileDescriptorInfo(kStatsTableSharedMemFd,
206                             stats_table->GetSharedMemoryHandle()));
207    }
208#endif
209
210#if defined(OS_ANDROID)
211    // Android WebView runs in single process, ensure that we never get here
212    // when running in single process mode.
213    CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
214
215    GetContentClient()->browser()->
216        GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
217                                                &files_to_register);
218
219    StartChildProcess(cmd_line->argv(), child_process_id, files_to_register,
220        base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted,
221                   this_object, client_thread_id, begin_launch_time));
222
223#elif defined(OS_POSIX)
224    base::ProcessHandle handle = base::kNullProcessHandle;
225    // We need to close the client end of the IPC channel to reliably detect
226    // child termination.
227    base::ScopedFD ipcfd_closer(ipcfd);
228
229#if !defined(OS_MACOSX)
230    GetContentClient()->browser()->
231        GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
232                                                &files_to_register);
233    if (use_zygote) {
234      handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(),
235                                                          files_to_register,
236                                                          process_type);
237    } else
238    // Fall through to the normal posix case below when we're not zygoting.
239#endif  // !defined(OS_MACOSX)
240    {
241      // Convert FD mapping to FileHandleMappingVector
242      base::FileHandleMappingVector fds_to_map;
243      for (size_t i = 0; i < files_to_register.size(); ++i) {
244        fds_to_map.push_back(std::make_pair(
245            files_to_register[i].fd.fd,
246            files_to_register[i].id +
247                base::GlobalDescriptors::kBaseDescriptor));
248      }
249
250#if !defined(OS_MACOSX)
251      if (process_type == switches::kRendererProcess) {
252        const int sandbox_fd =
253            RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
254        fds_to_map.push_back(std::make_pair(
255            sandbox_fd,
256            GetSandboxFD()));
257      }
258#endif  // defined(OS_MACOSX)
259
260      // Actually launch the app.
261      base::LaunchOptions options;
262      options.environ = env;
263      options.fds_to_remap = &fds_to_map;
264
265#if defined(OS_MACOSX)
266      // Hold the MachBroker lock for the duration of LaunchProcess. The child
267      // will send its task port to the parent almost immediately after startup.
268      // The Mach message will be delivered to the parent, but updating the
269      // record of the launch will wait until after the placeholder PID is
270      // inserted below. This ensures that while the child process may send its
271      // port to the parent prior to the parent leaving LaunchProcess, the
272      // order in which the record in MachBroker is updated is correct.
273      MachBroker* broker = MachBroker::GetInstance();
274      broker->GetLock().Acquire();
275
276      // Make sure the MachBroker is running, and inform it to expect a
277      // check-in from the new process.
278      broker->EnsureRunning();
279#endif  // defined(OS_MACOSX)
280
281      bool launched = base::LaunchProcess(*cmd_line, options, &handle);
282
283#if defined(OS_MACOSX)
284      if (launched)
285        broker->AddPlaceholderForPid(handle);
286
287      // After updating the broker, release the lock and let the child's
288      // messasge be processed on the broker's thread.
289      broker->GetLock().Release();
290#endif  // defined(OS_MACOSX)
291
292      if (!launched)
293        handle = base::kNullProcessHandle;
294    }
295#endif  // else defined(OS_POSIX)
296#if !defined(OS_ANDROID)
297  if (handle)
298    RecordHistograms(begin_launch_time);
299  BrowserThread::PostTask(
300      client_thread_id, FROM_HERE,
301      base::Bind(
302          &Context::Notify,
303          this_object.get(),
304#if defined(OS_POSIX) && !defined(OS_MACOSX)
305          use_zygote,
306#endif
307          handle));
308#endif  // !defined(OS_ANDROID)
309  }
310
311  void Notify(
312#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
313      bool zygote,
314#endif
315      base::ProcessHandle handle) {
316#if defined(OS_ANDROID)
317    // Finally close the ipcfd
318    base::ScopedFD ipcfd_closer(ipcfd_);
319#endif
320    starting_ = false;
321    process_.set_handle(handle);
322    if (!handle)
323      LOG(ERROR) << "Failed to launch child process";
324
325#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
326    zygote_ = zygote;
327#endif
328    if (client_) {
329      if (handle) {
330        client_->OnProcessLaunched();
331      } else {
332        client_->OnProcessLaunchFailed();
333      }
334    } else {
335      Terminate();
336    }
337  }
338
339  void Terminate() {
340    if (!process_.handle())
341      return;
342
343    if (!terminate_child_on_shutdown_)
344      return;
345
346    // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep!  So
347    // don't this on the UI/IO threads.
348    BrowserThread::PostTask(
349        BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
350        base::Bind(
351            &Context::TerminateInternal,
352#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
353            zygote_,
354#endif
355            process_.handle()));
356    process_.set_handle(base::kNullProcessHandle);
357  }
358
359  static void SetProcessBackgrounded(base::ProcessHandle handle,
360                                     bool background) {
361    base::Process process(handle);
362    process.SetProcessBackgrounded(background);
363  }
364
365  static void TerminateInternal(
366#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
367      bool zygote,
368#endif
369      base::ProcessHandle handle) {
370#if defined(OS_ANDROID)
371    VLOG(0) << "ChromeProcess: Stopping process with handle " << handle;
372    StopChildProcess(handle);
373#else
374    base::Process process(handle);
375     // Client has gone away, so just kill the process.  Using exit code 0
376    // means that UMA won't treat this as a crash.
377    process.Terminate(RESULT_CODE_NORMAL_EXIT);
378    // On POSIX, we must additionally reap the child.
379#if defined(OS_POSIX)
380#if !defined(OS_MACOSX)
381    if (zygote) {
382      // If the renderer was created via a zygote, we have to proxy the reaping
383      // through the zygote process.
384      ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle);
385    } else
386#endif  // !OS_MACOSX
387    {
388      base::EnsureProcessTerminated(handle);
389    }
390#endif  // OS_POSIX
391    process.Close();
392#endif  // defined(OS_ANDROID)
393  }
394
395  Client* client_;
396  BrowserThread::ID client_thread_id_;
397  base::Process process_;
398  base::TerminationStatus termination_status_;
399  int exit_code_;
400  bool starting_;
401  // Controls whether the child process should be terminated on browser
402  // shutdown. Default behavior is to terminate the child.
403  bool terminate_child_on_shutdown_;
404#if defined(OS_ANDROID)
405  // The fd to close after creating the process.
406  int ipcfd_;
407#elif defined(OS_POSIX) && !defined(OS_MACOSX)
408  bool zygote_;
409#endif
410};
411
412
413ChildProcessLauncher::ChildProcessLauncher(
414    SandboxedProcessLauncherDelegate* delegate,
415    CommandLine* cmd_line,
416    int child_process_id,
417    Client* client) {
418  context_ = new Context();
419  context_->Launch(
420      delegate,
421      cmd_line,
422      child_process_id,
423      client);
424}
425
426ChildProcessLauncher::~ChildProcessLauncher() {
427  context_->ResetClient();
428}
429
430bool ChildProcessLauncher::IsStarting() {
431  return context_->starting_;
432}
433
434base::ProcessHandle ChildProcessLauncher::GetHandle() {
435  DCHECK(!context_->starting_);
436  return context_->process_.handle();
437}
438
439base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
440    bool known_dead,
441    int* exit_code) {
442  base::ProcessHandle handle = context_->process_.handle();
443  if (handle == base::kNullProcessHandle) {
444    // Process is already gone, so return the cached termination status.
445    if (exit_code)
446      *exit_code = context_->exit_code_;
447    return context_->termination_status_;
448  }
449#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
450  if (context_->zygote_) {
451    context_->termination_status_ = ZygoteHostImpl::GetInstance()->
452        GetTerminationStatus(handle, known_dead, &context_->exit_code_);
453  } else if (known_dead) {
454    context_->termination_status_ =
455        base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
456  } else {
457#elif defined(OS_MACOSX)
458  if (known_dead) {
459    context_->termination_status_ =
460        base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
461  } else {
462#elif defined(OS_ANDROID)
463  if (IsChildProcessOomProtected(handle)) {
464      context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
465  } else {
466#else
467  {
468#endif
469    context_->termination_status_ =
470        base::GetTerminationStatus(handle, &context_->exit_code_);
471  }
472
473  if (exit_code)
474    *exit_code = context_->exit_code_;
475
476  // POSIX: If the process crashed, then the kernel closed the socket
477  // for it and so the child has already died by the time we get
478  // here. Since GetTerminationStatus called waitpid with WNOHANG,
479  // it'll reap the process.  However, if GetTerminationStatus didn't
480  // reap the child (because it was still running), we'll need to
481  // Terminate via ProcessWatcher. So we can't close the handle here.
482  if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
483    context_->process_.Close();
484
485  return context_->termination_status_;
486}
487
488void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
489  BrowserThread::PostTask(
490      BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
491      base::Bind(
492          &ChildProcessLauncher::Context::SetProcessBackgrounded,
493          GetHandle(), background));
494}
495
496void ChildProcessLauncher::SetTerminateChildOnShutdown(
497    bool terminate_on_shutdown) {
498  if (context_.get())
499    context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
500}
501
502}  // namespace content
503