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