xref: /aosp_15_r20/external/cronet/base/process/kill_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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