Home | History | Annotate | Download | only in process
      1 // Copyright (c) 2013 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 "base/process/kill.h"
      6 
      7 #include <errno.h>
      8 #include <signal.h>
      9 #include <sys/types.h>
     10 #include <sys/wait.h>
     11 #include <unistd.h>
     12 
     13 #include "base/debug/activity_tracker.h"
     14 #include "base/files/file_util.h"
     15 #include "base/logging.h"
     16 #include "base/macros.h"
     17 #include "base/posix/eintr_wrapper.h"
     18 #include "base/process/process_iterator.h"
     19 #include "base/task_scheduler/post_task.h"
     20 #include "base/threading/platform_thread.h"
     21 #include "build/build_config.h"
     22 
     23 namespace base {
     24 
     25 namespace {
     26 
     27 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
     28                                            bool can_block,
     29                                            int* exit_code) {
     30   DCHECK(exit_code);
     31 
     32   int status = 0;
     33   const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
     34                                             can_block ? 0 : WNOHANG));
     35   if (result == -1) {
     36     DPLOG(ERROR) << "waitpid(" << handle << ")";
     37     *exit_code = 0;
     38     return TERMINATION_STATUS_NORMAL_TERMINATION;
     39   } else if (result == 0) {
     40     // the child hasn't exited yet.
     41     *exit_code = 0;
     42     return TERMINATION_STATUS_STILL_RUNNING;
     43   }
     44 
     45   *exit_code = status;
     46 
     47   if (WIFSIGNALED(status)) {
     48     switch (WTERMSIG(status)) {
     49       case SIGABRT:
     50       case SIGBUS:
     51       case SIGFPE:
     52       case SIGILL:
     53       case SIGSEGV:
     54       case SIGTRAP:
     55       case SIGSYS:
     56         return TERMINATION_STATUS_PROCESS_CRASHED;
     57       case SIGKILL:
     58 #if defined(OS_CHROMEOS)
     59         // On ChromeOS, only way a process gets kill by SIGKILL
     60         // is by oom-killer.
     61         return TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM;
     62 #endif
     63       case SIGINT:
     64       case SIGTERM:
     65         return TERMINATION_STATUS_PROCESS_WAS_KILLED;
     66       default:
     67         break;
     68     }
     69   }
     70 
     71   if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
     72     return TERMINATION_STATUS_ABNORMAL_TERMINATION;
     73 
     74   return TERMINATION_STATUS_NORMAL_TERMINATION;
     75 }
     76 
     77 }  // namespace
     78 
     79 #if !defined(OS_NACL_NONSFI)
     80 bool KillProcessGroup(ProcessHandle process_group_id) {
     81   bool result = kill(-1 * process_group_id, SIGKILL) == 0;
     82   if (!result)
     83     DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
     84   return result;
     85 }
     86 #endif  // !defined(OS_NACL_NONSFI)
     87 
     88 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
     89   return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
     90 }
     91 
     92 TerminationStatus GetKnownDeadTerminationStatus(ProcessHandle handle,
     93                                                 int* exit_code) {
     94   bool result = kill(handle, SIGKILL) == 0;
     95 
     96   if (!result)
     97     DPLOG(ERROR) << "Unable to terminate process " << handle;
     98 
     99   return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
    100 }
    101 
    102 #if !defined(OS_NACL_NONSFI)
    103 bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
    104                             TimeDelta wait,
    105                             const ProcessFilter* filter) {
    106   bool result = false;
    107 
    108   // TODO(port): This is inefficient, but works if there are multiple procs.
    109   // TODO(port): use waitpid to avoid leaving zombies around
    110 
    111   TimeTicks end_time = TimeTicks::Now() + wait;
    112   do {
    113     NamedProcessIterator iter(executable_name, filter);
    114     if (!iter.NextProcessEntry()) {
    115       result = true;
    116       break;
    117     }
    118     PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
    119   } while ((end_time - TimeTicks::Now()) > TimeDelta());
    120 
    121   return result;
    122 }
    123 
    124 bool CleanupProcesses(const FilePath::StringType& executable_name,
    125                       TimeDelta wait,
    126                       int exit_code,
    127                       const ProcessFilter* filter) {
    128   bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
    129   if (!exited_cleanly)
    130     KillProcesses(executable_name, exit_code, filter);
    131   return exited_cleanly;
    132 }
    133 
    134 #if !defined(OS_MACOSX)
    135 
    136 namespace {
    137 
    138 class BackgroundReaper : public PlatformThread::Delegate {
    139  public:
    140   BackgroundReaper(base::Process child_process, const TimeDelta& wait_time)
    141       : child_process_(std::move(child_process)), wait_time_(wait_time) {}
    142 
    143   void ThreadMain() override {
    144     if (!wait_time_.is_zero()) {
    145       child_process_.WaitForExitWithTimeout(wait_time_, nullptr);
    146       kill(child_process_.Handle(), SIGKILL);
    147     }
    148     child_process_.WaitForExit(nullptr);
    149     delete this;
    150   }
    151 
    152  private:
    153   Process child_process_;
    154   const TimeDelta wait_time_;
    155   DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
    156 };
    157 
    158 }  // namespace
    159 
    160 void EnsureProcessTerminated(Process process) {
    161   DCHECK(!process.is_current());
    162 
    163   if (process.WaitForExitWithTimeout(TimeDelta(), nullptr))
    164     return;
    165 
    166   PlatformThread::CreateNonJoinable(
    167       0, new BackgroundReaper(std::move(process), TimeDelta::FromSeconds(2)));
    168 }
    169 
    170 #if defined(OS_LINUX)
    171 void EnsureProcessGetsReaped(Process process) {
    172   DCHECK(!process.is_current());
    173 
    174   // If the child is already dead, then there's nothing to do.
    175   if (process.WaitForExitWithTimeout(TimeDelta(), nullptr))
    176     return;
    177 
    178   PlatformThread::CreateNonJoinable(
    179       0, new BackgroundReaper(std::move(process), TimeDelta()));
    180 }
    181 #endif  // defined(OS_LINUX)
    182 
    183 #endif  // !defined(OS_MACOSX)
    184 #endif  // !defined(OS_NACL_NONSFI)
    185 
    186 }  // namespace base
    187