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