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
GetTerminationStatusImpl(ProcessHandle handle,bool can_block,int * exit_code)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)
KillProcessGroup(ProcessHandle process_group_id)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
GetTerminationStatus(ProcessHandle handle,int * exit_code)88 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
89 return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
90 }
91
GetKnownDeadTerminationStatus(ProcessHandle handle,int * exit_code)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)
WaitForProcessesToExit(const FilePath::StringType & executable_name,TimeDelta wait,const ProcessFilter * filter)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
CleanupProcesses(const FilePath::StringType & executable_name,TimeDelta wait,int exit_code,const ProcessFilter * filter)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:
BackgroundReaper(base::Process child_process,const TimeDelta & wait_time)140 BackgroundReaper(base::Process child_process, const TimeDelta& wait_time)
141 : child_process_(std::move(child_process)), wait_time_(wait_time) {}
142
ThreadMain()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
EnsureProcessTerminated(Process process)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)
EnsureProcessGetsReaped(Process process)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