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/files/file_util.h"
     14 #include "base/files/scoped_file.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/synchronization/waitable_event.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   int status = 0;
     31   const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
     32                                             can_block ? 0 : WNOHANG));
     33   if (result == -1) {
     34     DPLOG(ERROR) << "waitpid(" << handle << ")";
     35     if (exit_code)
     36       *exit_code = 0;
     37     return TERMINATION_STATUS_NORMAL_TERMINATION;
     38   } else if (result == 0) {
     39     // the child hasn't exited yet.
     40     if (exit_code)
     41       *exit_code = 0;
     42     return TERMINATION_STATUS_STILL_RUNNING;
     43   }
     44 
     45   if (exit_code)
     46     *exit_code = status;
     47 
     48   if (WIFSIGNALED(status)) {
     49     switch (WTERMSIG(status)) {
     50       case SIGABRT:
     51       case SIGBUS:
     52       case SIGFPE:
     53       case SIGILL:
     54       case SIGSEGV:
     55       case SIGTRAP:
     56       case SIGSYS:
     57         return TERMINATION_STATUS_PROCESS_CRASHED;
     58       case SIGKILL:
     59 #if defined(OS_CHROMEOS)
     60         // On ChromeOS, only way a process gets kill by SIGKILL
     61         // is by oom-killer.
     62         return TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM;
     63 #endif
     64       case SIGINT:
     65       case SIGTERM:
     66         return TERMINATION_STATUS_PROCESS_WAS_KILLED;
     67       default:
     68         break;
     69     }
     70   }
     71 
     72   if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
     73     return TERMINATION_STATUS_ABNORMAL_TERMINATION;
     74 
     75   return TERMINATION_STATUS_NORMAL_TERMINATION;
     76 }
     77 
     78 }  // namespace
     79 
     80 #if !defined(OS_NACL_NONSFI)
     81 bool KillProcessGroup(ProcessHandle process_group_id) {
     82   bool result = kill(-1 * process_group_id, SIGKILL) == 0;
     83   if (!result)
     84     DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
     85   return result;
     86 }
     87 #endif  // !defined(OS_NACL_NONSFI)
     88 
     89 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
     90   return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
     91 }
     92 
     93 TerminationStatus GetKnownDeadTerminationStatus(ProcessHandle handle,
     94                                                 int* exit_code) {
     95   bool result = kill(handle, SIGKILL) == 0;
     96 
     97   if (!result)
     98     DPLOG(ERROR) << "Unable to terminate process " << handle;
     99 
    100   return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
    101 }
    102 
    103 #if !defined(OS_NACL_NONSFI)
    104 bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
    105                             TimeDelta wait,
    106                             const ProcessFilter* filter) {
    107   bool result = false;
    108 
    109   // TODO(port): This is inefficient, but works if there are multiple procs.
    110   // TODO(port): use waitpid to avoid leaving zombies around
    111 
    112   TimeTicks end_time = TimeTicks::Now() + wait;
    113   do {
    114     NamedProcessIterator iter(executable_name, filter);
    115     if (!iter.NextProcessEntry()) {
    116       result = true;
    117       break;
    118     }
    119     PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
    120   } while ((end_time - TimeTicks::Now()) > TimeDelta());
    121 
    122   return result;
    123 }
    124 
    125 bool CleanupProcesses(const FilePath::StringType& executable_name,
    126                       TimeDelta wait,
    127                       int exit_code,
    128                       const ProcessFilter* filter) {
    129   bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
    130   if (!exited_cleanly)
    131     KillProcesses(executable_name, exit_code, filter);
    132   return exited_cleanly;
    133 }
    134 
    135 #if !defined(OS_MACOSX)
    136 
    137 namespace {
    138 
    139 // Return true if the given child is dead. This will also reap the process.
    140 // Doesn't block.
    141 static bool IsChildDead(pid_t child) {
    142   const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
    143   if (result == -1) {
    144     DPLOG(ERROR) << "waitpid(" << child << ")";
    145     NOTREACHED();
    146   } else if (result > 0) {
    147     // The child has died.
    148     return true;
    149   }
    150 
    151   return false;
    152 }
    153 
    154 // A thread class which waits for the given child to exit and reaps it.
    155 // If the child doesn't exit within a couple of seconds, kill it.
    156 class BackgroundReaper : public PlatformThread::Delegate {
    157  public:
    158   BackgroundReaper(pid_t child, unsigned timeout)
    159       : child_(child),
    160         timeout_(timeout) {
    161   }
    162 
    163   // Overridden from PlatformThread::Delegate:
    164   void ThreadMain() override {
    165     WaitForChildToDie();
    166     delete this;
    167   }
    168 
    169   void WaitForChildToDie() {
    170     // Wait forever case.
    171     if (timeout_ == 0) {
    172       pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0));
    173       if (r != child_) {
    174         DPLOG(ERROR) << "While waiting for " << child_
    175                      << " to terminate, we got the following result: " << r;
    176       }
    177       return;
    178     }
    179 
    180     // There's no good way to wait for a specific child to exit in a timed
    181     // fashion. (No kqueue on Linux), so we just loop and sleep.
    182 
    183     // Wait for 2 * timeout_ 500 milliseconds intervals.
    184     for (unsigned i = 0; i < 2 * timeout_; ++i) {
    185       PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
    186       if (IsChildDead(child_))
    187         return;
    188     }
    189 
    190     if (kill(child_, SIGKILL) == 0) {
    191       // SIGKILL is uncatchable. Since the signal was delivered, we can
    192       // just wait for the process to die now in a blocking manner.
    193       if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0)
    194         DPLOG(WARNING) << "waitpid";
    195     } else {
    196       DLOG(ERROR) << "While waiting for " << child_ << " to terminate we"
    197                   << " failed to deliver a SIGKILL signal (" << errno << ").";
    198     }
    199   }
    200 
    201  private:
    202   const pid_t child_;
    203   // Number of seconds to wait, if 0 then wait forever and do not attempt to
    204   // kill |child_|.
    205   const unsigned timeout_;
    206 
    207   DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
    208 };
    209 
    210 }  // namespace
    211 
    212 void EnsureProcessTerminated(Process process) {
    213   // If the child is already dead, then there's nothing to do.
    214   if (IsChildDead(process.Pid()))
    215     return;
    216 
    217   const unsigned timeout = 2;  // seconds
    218   BackgroundReaper* reaper = new BackgroundReaper(process.Pid(), timeout);
    219   PlatformThread::CreateNonJoinable(0, reaper);
    220 }
    221 
    222 void EnsureProcessGetsReaped(ProcessId pid) {
    223   // If the child is already dead, then there's nothing to do.
    224   if (IsChildDead(pid))
    225     return;
    226 
    227   BackgroundReaper* reaper = new BackgroundReaper(pid, 0);
    228   PlatformThread::CreateNonJoinable(0, reaper);
    229 }
    230 
    231 #endif  // !defined(OS_MACOSX)
    232 #endif  // !defined(OS_NACL_NONSFI)
    233 
    234 }  // namespace base
    235