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