• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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