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/files/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 
     50 namespace 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.
     55 class 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       base::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       base::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<base::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         options.replacement_bootstrap_name =
    292             GetBootstrapSandbox()->server_bootstrap_name();
    293         GetBootstrapSandbox()->PrepareToForkWithPolicy(
    294             bootstrap_sandbox_policy);
    295       }
    296 #endif  // defined(OS_MACOSX)
    297 
    298       bool launched = base::LaunchProcess(*cmd_line, options, &handle);
    299       if (!launched)
    300         handle = base::kNullProcessHandle;
    301 
    302 #if defined(OS_MACOSX)
    303       if (ShouldEnableBootstrapSandbox() &&
    304           bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
    305         GetBootstrapSandbox()->FinishedFork(handle);
    306       }
    307 
    308       if (launched)
    309         broker->AddPlaceholderForPid(handle);
    310 
    311       // After updating the broker, release the lock and let the child's
    312       // messasge be processed on the broker's thread.
    313       broker->GetLock().Release();
    314 #endif  // defined(OS_MACOSX)
    315     }
    316 #endif  // else defined(OS_POSIX)
    317 #if !defined(OS_ANDROID)
    318   if (handle)
    319     RecordHistograms(begin_launch_time);
    320   BrowserThread::PostTask(
    321       client_thread_id, FROM_HERE,
    322       base::Bind(
    323           &Context::Notify,
    324           this_object.get(),
    325 #if defined(OS_POSIX) && !defined(OS_MACOSX)
    326           use_zygote,
    327 #endif
    328           handle));
    329 #endif  // !defined(OS_ANDROID)
    330   }
    331 
    332   void Notify(
    333 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    334       bool zygote,
    335 #endif
    336       base::ProcessHandle handle) {
    337 #if defined(OS_ANDROID)
    338     // Finally close the ipcfd
    339     base::ScopedFD ipcfd_closer(ipcfd_);
    340 #endif
    341     starting_ = false;
    342     process_.set_handle(handle);
    343     if (!handle)
    344       LOG(ERROR) << "Failed to launch child process";
    345 
    346 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    347     zygote_ = zygote;
    348 #endif
    349     if (client_) {
    350       if (handle) {
    351         client_->OnProcessLaunched();
    352       } else {
    353         client_->OnProcessLaunchFailed();
    354       }
    355     } else {
    356       Terminate();
    357     }
    358   }
    359 
    360   void Terminate() {
    361     if (!process_.handle())
    362       return;
    363 
    364     if (!terminate_child_on_shutdown_)
    365       return;
    366 
    367     // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep!  So
    368     // don't this on the UI/IO threads.
    369     BrowserThread::PostTask(
    370         BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
    371         base::Bind(
    372             &Context::TerminateInternal,
    373 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    374             zygote_,
    375 #endif
    376             process_.handle()));
    377     process_.set_handle(base::kNullProcessHandle);
    378   }
    379 
    380   static void SetProcessBackgrounded(base::ProcessHandle handle,
    381                                      bool background) {
    382     base::Process process(handle);
    383     process.SetProcessBackgrounded(background);
    384 #if defined(OS_ANDROID)
    385     SetChildProcessInForeground(handle, !background);
    386 #endif
    387   }
    388 
    389   static void TerminateInternal(
    390 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    391       bool zygote,
    392 #endif
    393       base::ProcessHandle handle) {
    394 #if defined(OS_ANDROID)
    395     VLOG(0) << "ChromeProcess: Stopping process with handle " << handle;
    396     StopChildProcess(handle);
    397 #else
    398     base::Process process(handle);
    399      // Client has gone away, so just kill the process.  Using exit code 0
    400     // means that UMA won't treat this as a crash.
    401     process.Terminate(RESULT_CODE_NORMAL_EXIT);
    402     // On POSIX, we must additionally reap the child.
    403 #if defined(OS_POSIX)
    404 #if !defined(OS_MACOSX)
    405     if (zygote) {
    406       // If the renderer was created via a zygote, we have to proxy the reaping
    407       // through the zygote process.
    408       ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle);
    409     } else
    410 #endif  // !OS_MACOSX
    411     {
    412       base::EnsureProcessTerminated(handle);
    413     }
    414 #endif  // OS_POSIX
    415     process.Close();
    416 #endif  // defined(OS_ANDROID)
    417   }
    418 
    419   Client* client_;
    420   BrowserThread::ID client_thread_id_;
    421   base::Process process_;
    422   base::TerminationStatus termination_status_;
    423   int exit_code_;
    424   bool starting_;
    425   // Controls whether the child process should be terminated on browser
    426   // shutdown. Default behavior is to terminate the child.
    427   bool terminate_child_on_shutdown_;
    428 #if defined(OS_ANDROID)
    429   // The fd to close after creating the process.
    430   int ipcfd_;
    431 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
    432   bool zygote_;
    433 #endif
    434 };
    435 
    436 
    437 ChildProcessLauncher::ChildProcessLauncher(
    438     SandboxedProcessLauncherDelegate* delegate,
    439     base::CommandLine* cmd_line,
    440     int child_process_id,
    441     Client* client) {
    442   context_ = new Context();
    443   context_->Launch(
    444       delegate,
    445       cmd_line,
    446       child_process_id,
    447       client);
    448 }
    449 
    450 ChildProcessLauncher::~ChildProcessLauncher() {
    451   context_->ResetClient();
    452 }
    453 
    454 bool ChildProcessLauncher::IsStarting() {
    455   return context_->starting_;
    456 }
    457 
    458 base::ProcessHandle ChildProcessLauncher::GetHandle() {
    459   DCHECK(!context_->starting_);
    460   return context_->process_.handle();
    461 }
    462 
    463 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
    464     bool known_dead,
    465     int* exit_code) {
    466   base::ProcessHandle handle = context_->process_.handle();
    467   if (handle == base::kNullProcessHandle) {
    468     // Process is already gone, so return the cached termination status.
    469     if (exit_code)
    470       *exit_code = context_->exit_code_;
    471     return context_->termination_status_;
    472   }
    473 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    474   if (context_->zygote_) {
    475     context_->termination_status_ = ZygoteHostImpl::GetInstance()->
    476         GetTerminationStatus(handle, known_dead, &context_->exit_code_);
    477   } else if (known_dead) {
    478     context_->termination_status_ =
    479         base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
    480   } else {
    481 #elif defined(OS_MACOSX)
    482   if (known_dead) {
    483     context_->termination_status_ =
    484         base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
    485   } else {
    486 #elif defined(OS_ANDROID)
    487   if (IsChildProcessOomProtected(handle)) {
    488       context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
    489   } else {
    490 #else
    491   {
    492 #endif
    493     context_->termination_status_ =
    494         base::GetTerminationStatus(handle, &context_->exit_code_);
    495   }
    496 
    497   if (exit_code)
    498     *exit_code = context_->exit_code_;
    499 
    500   // POSIX: If the process crashed, then the kernel closed the socket
    501   // for it and so the child has already died by the time we get
    502   // here. Since GetTerminationStatus called waitpid with WNOHANG,
    503   // it'll reap the process.  However, if GetTerminationStatus didn't
    504   // reap the child (because it was still running), we'll need to
    505   // Terminate via ProcessWatcher. So we can't close the handle here.
    506   if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
    507     context_->process_.Close();
    508 
    509   return context_->termination_status_;
    510 }
    511 
    512 void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
    513   BrowserThread::PostTask(
    514      BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
    515      base::Bind(
    516          &ChildProcessLauncher::Context::SetProcessBackgrounded,
    517          GetHandle(), background));
    518 }
    519 
    520 void ChildProcessLauncher::SetTerminateChildOnShutdown(
    521     bool terminate_on_shutdown) {
    522   if (context_.get())
    523     context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
    524 }
    525 
    526 }  // namespace content
    527