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