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