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