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