Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2011 The Chromium Authors |
[email protected] | 0b100bc8 | 2008-10-14 20:49:16 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 76bea67 | 2013-07-19 16:48:56 | [diff] [blame] | 5 | #include "base/process/process.h" |
[email protected] | 66700d4 | 2010-03-10 07:46:43 | [diff] [blame] | 6 | |
mostynb | 522dfa5 | 2015-11-07 00:57:14 | [diff] [blame] | 7 | #include <errno.h> |
raphael.kubo.da.costa | 531c29b | 2017-04-18 14:11:14 | [diff] [blame] | 8 | #include <signal.h> |
avi | beced7c | 2015-12-24 06:47:59 | [diff] [blame] | 9 | #include <stdint.h> |
[email protected] | 276aa6a | 2009-10-29 17:43:44 | [diff] [blame] | 10 | #include <sys/resource.h> |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 11 | #include <sys/wait.h> |
[email protected] | 276aa6a | 2009-10-29 17:43:44 | [diff] [blame] | 12 | |
S. Ganesh | e4a9986 | 2025-04-29 03:30:45 | [diff] [blame] | 13 | #include <algorithm> |
Robert Sesek | 0e7f165a | 2020-11-20 21:31:45 | [diff] [blame] | 14 | #include <utility> |
| 15 | |
S. Ganesh | e4a9986 | 2025-04-29 03:30:45 | [diff] [blame] | 16 | #include "base/check_op.h" |
Sebastien Marchand | bd02bc29e | 2020-03-11 15:53:36 | [diff] [blame] | 17 | #include "base/clang_profiling_buildflags.h" |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 18 | #include "base/files/scoped_file.h" |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame] | 19 | #include "base/logging.h" |
Peter Boström | 6e2fd08 | 2024-01-23 01:17:55 | [diff] [blame] | 20 | #include "base/notimplemented.h" |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 21 | #include "base/posix/eintr_wrapper.h" |
[email protected] | dd4b5126 | 2013-07-25 21:38:23 | [diff] [blame] | 22 | #include "base/process/kill.h" |
Francois Doray | a678fc1 | 2017-10-30 22:18:06 | [diff] [blame] | 23 | #include "base/threading/thread_restrictions.h" |
Gabriel Charette | d87f10f | 2022-03-31 00:44:22 | [diff] [blame] | 24 | #include "base/time/time.h" |
Etienne Pierre-doray | fc7952f0 | 2025-06-06 00:04:33 | [diff] [blame] | 25 | #include "base/trace_event/trace_event.h" |
avi | beced7c | 2015-12-24 06:47:59 | [diff] [blame] | 26 | #include "build/build_config.h" |
[email protected] | 0b100bc8 | 2008-10-14 20:49:16 | [diff] [blame] | 27 | |
Xiaohan Wang | 37e8161 | 2022-01-15 18:27:00 | [diff] [blame] | 28 | #if BUILDFLAG(IS_MAC) |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 29 | #include <sys/event.h> |
| 30 | #endif |
| 31 | |
Sebastien Marchand | bd02bc29e | 2020-03-11 15:53:36 | [diff] [blame] | 32 | #if BUILDFLAG(CLANG_PROFILING) |
| 33 | #include "base/test/clang_profiling.h" |
Yuke Liao | a1d9b7b | 2019-08-14 16:14:34 | [diff] [blame] | 34 | #endif |
Yannic Bonenberger | 6801e6c | 2019-06-07 10:42:53 | [diff] [blame] | 35 | |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 36 | #if BUILDFLAG(IS_IOS) |
| 37 | #include "TargetConditionals.h" |
| 38 | #endif |
| 39 | |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 40 | namespace { |
| 41 | |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 42 | #if !BUILDFLAG(IS_IOS) || (BUILDFLAG(IS_IOS) && TARGET_OS_SIMULATOR) |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 43 | bool WaitpidWithTimeout(base::ProcessHandle handle, |
| 44 | int* status, |
| 45 | base::TimeDelta wait) { |
S. Ganesh | e4a9986 | 2025-04-29 03:30:45 | [diff] [blame] | 46 | DCHECK_GE(wait, base::TimeDelta()); |
| 47 | |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 48 | // This POSIX version of this function only guarantees that we wait no less |
| 49 | // than |wait| for the process to exit. The child process may |
| 50 | // exit sometime before the timeout has ended but we may still block for up |
| 51 | // to 256 milliseconds after the fact. |
| 52 | // |
| 53 | // waitpid() has no direct support on POSIX for specifying a timeout, you can |
| 54 | // either ask it to block indefinitely or return immediately (WNOHANG). |
| 55 | // When a child process terminates a SIGCHLD signal is sent to the parent. |
| 56 | // Catching this signal would involve installing a signal handler which may |
| 57 | // affect other parts of the application and would be difficult to debug. |
| 58 | // |
| 59 | // Our strategy is to call waitpid() once up front to check if the process |
| 60 | // has already exited, otherwise to loop for |wait|, sleeping for |
| 61 | // at most 256 milliseconds each time using usleep() and then calling |
| 62 | // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and |
| 63 | // we double it every 4 sleep cycles. |
| 64 | // |
| 65 | // usleep() is speced to exit if a signal is received for which a handler |
| 66 | // has been installed. This means that when a SIGCHLD is sent, it will exit |
| 67 | // depending on behavior external to this function. |
| 68 | // |
| 69 | // This function is used primarily for unit tests, if we want to use it in |
| 70 | // the application itself it would probably be best to examine other routes. |
| 71 | |
| 72 | if (wait == base::TimeDelta::Max()) { |
| 73 | return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; |
| 74 | } |
| 75 | |
| 76 | pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
Peter Kasting | 25acd594 | 2022-07-07 17:45:15 | [diff] [blame] | 77 | static const uint32_t kMaxSleepInMicroseconds = 1 << 18; // ~256 ms. |
| 78 | uint32_t max_sleep_time_usecs = 1 << 10; // ~1 ms. |
| 79 | int double_sleep_time = 0; |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 80 | |
| 81 | // If the process hasn't exited yet, then sleep and try again. |
| 82 | base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait; |
| 83 | while (ret_pid == 0) { |
| 84 | base::TimeTicks now = base::TimeTicks::Now(); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 85 | if (now > wakeup_time) { |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 86 | break; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 87 | } |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 88 | |
Peter Kasting | 25acd594 | 2022-07-07 17:45:15 | [diff] [blame] | 89 | const uint32_t sleep_time_usecs = static_cast<uint32_t>( |
| 90 | std::min(static_cast<uint64_t>((wakeup_time - now).InMicroseconds()), |
| 91 | uint64_t{max_sleep_time_usecs})); |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 92 | // usleep() will return 0 and set errno to EINTR on receipt of a signal |
| 93 | // such as SIGCHLD. |
| 94 | usleep(sleep_time_usecs); |
| 95 | ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 96 | |
| 97 | if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && |
| 98 | (double_sleep_time++ % 4 == 0)) { |
| 99 | max_sleep_time_usecs *= 2; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | return ret_pid > 0; |
| 104 | } |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 105 | #endif |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 106 | |
Xiaohan Wang | 37e8161 | 2022-01-15 18:27:00 | [diff] [blame] | 107 | #if BUILDFLAG(IS_MAC) |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 108 | // Using kqueue on Mac so that we can wait on non-child processes. |
| 109 | // We can't use kqueues on child processes because we need to reap |
| 110 | // our own children using wait. |
Wez | 20d36369 | 2018-02-26 22:10:22 | [diff] [blame] | 111 | bool WaitForSingleNonChildProcess(base::ProcessHandle handle, |
| 112 | base::TimeDelta wait) { |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 113 | DCHECK_GT(handle, 0); |
S. Ganesh | e4a9986 | 2025-04-29 03:30:45 | [diff] [blame] | 114 | DCHECK_GE(wait, base::TimeDelta()); |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 115 | |
| 116 | base::ScopedFD kq(kqueue()); |
| 117 | if (!kq.is_valid()) { |
| 118 | DPLOG(ERROR) << "kqueue"; |
| 119 | return false; |
| 120 | } |
| 121 | |
| 122 | struct kevent change = {0}; |
| 123 | EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); |
| 124 | int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); |
| 125 | if (result == -1) { |
| 126 | if (errno == ESRCH) { |
| 127 | // If the process wasn't found, it must be dead. |
| 128 | return true; |
| 129 | } |
| 130 | |
| 131 | DPLOG(ERROR) << "kevent (setup " << handle << ")"; |
| 132 | return false; |
| 133 | } |
| 134 | |
| 135 | // Keep track of the elapsed time to be able to restart kevent if it's |
| 136 | // interrupted. |
| 137 | bool wait_forever = (wait == base::TimeDelta::Max()); |
| 138 | base::TimeDelta remaining_delta; |
| 139 | base::TimeTicks deadline; |
| 140 | if (!wait_forever) { |
| 141 | remaining_delta = wait; |
| 142 | deadline = base::TimeTicks::Now() + remaining_delta; |
| 143 | } |
| 144 | |
| 145 | result = -1; |
| 146 | struct kevent event = {0}; |
| 147 | |
Zijie He | 4dd88ae42 | 2017-09-20 01:30:18 | [diff] [blame] | 148 | do { |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 149 | struct timespec remaining_timespec; |
| 150 | struct timespec* remaining_timespec_ptr; |
| 151 | if (wait_forever) { |
| 152 | remaining_timespec_ptr = NULL; |
| 153 | } else { |
| 154 | remaining_timespec = remaining_delta.ToTimeSpec(); |
| 155 | remaining_timespec_ptr = &remaining_timespec; |
| 156 | } |
| 157 | |
| 158 | result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); |
| 159 | |
| 160 | if (result == -1 && errno == EINTR) { |
| 161 | if (!wait_forever) { |
| 162 | remaining_delta = deadline - base::TimeTicks::Now(); |
| 163 | } |
| 164 | result = 0; |
| 165 | } else { |
| 166 | break; |
| 167 | } |
Xiaohan Wang | abff030 | 2021-10-27 00:42:57 | [diff] [blame] | 168 | } while (wait_forever || remaining_delta.is_positive()); |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 169 | |
| 170 | if (result < 0) { |
| 171 | DPLOG(ERROR) << "kevent (wait " << handle << ")"; |
| 172 | return false; |
| 173 | } else if (result > 1) { |
| 174 | DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " |
| 175 | << result; |
| 176 | return false; |
| 177 | } else if (result == 0) { |
| 178 | // Timed out. |
| 179 | return false; |
| 180 | } |
| 181 | |
| 182 | DCHECK_EQ(result, 1); |
| 183 | |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 184 | if (event.filter != EVFILT_PROC || (event.fflags & NOTE_EXIT) == 0 || |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 185 | event.ident != static_cast<uintptr_t>(handle)) { |
| 186 | DLOG(ERROR) << "kevent (wait " << handle |
| 187 | << "): unexpected event: filter=" << event.filter |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 188 | << ", fflags=" << event.fflags << ", ident=" << event.ident; |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 189 | return false; |
| 190 | } |
| 191 | |
| 192 | return true; |
| 193 | } |
Xiaohan Wang | 37e8161 | 2022-01-15 18:27:00 | [diff] [blame] | 194 | #endif // BUILDFLAG(IS_MAC) |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 195 | |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 196 | } // namespace |
| 197 | |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 198 | namespace base { |
| 199 | |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 200 | Process::Process(ProcessHandle handle) : process_(handle) {} |
thakis | 3096dac | 2015-04-20 16:44:48 | [diff] [blame] | 201 | |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 202 | Process::Process(Process&& other) : process_(other.process_) { |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 203 | #if BUILDFLAG(IS_CHROMEOS) |
| 204 | unique_token_ = std::move(other.unique_token_); |
| 205 | #endif |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 206 | #if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK) && TARGET_OS_SIMULATOR |
| 207 | content_process_ = other.content_process_; |
| 208 | #endif |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 209 | |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 210 | other.Close(); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 211 | } |
| 212 | |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 213 | Process& Process::operator=(Process&& other) { |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 214 | process_ = other.process_; |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 215 | #if BUILDFLAG(IS_CHROMEOS) |
| 216 | unique_token_ = std::move(other.unique_token_); |
| 217 | #endif |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 218 | #if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK) && TARGET_OS_SIMULATOR |
| 219 | content_process_ = other.content_process_; |
| 220 | #endif |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 221 | other.Close(); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 222 | return *this; |
| 223 | } |
| 224 | |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 225 | Process::~Process() = default; |
| 226 | |
[email protected] | b7d0820 | 2011-01-25 17:29:39 | [diff] [blame] | 227 | // static |
| 228 | Process Process::Current() { |
rvargas | d5626f6 | 2015-02-05 19:09:24 | [diff] [blame] | 229 | return Process(GetCurrentProcessHandle()); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 230 | } |
| 231 | |
rvargas | 5779b38 | 2014-11-18 20:44:11 | [diff] [blame] | 232 | // static |
rvargas | 6b039c37 | 2015-02-04 21:11:29 | [diff] [blame] | 233 | Process Process::Open(ProcessId pid) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 234 | if (pid == GetCurrentProcId()) { |
rvargas | 747ff24 | 2015-01-17 02:46:47 | [diff] [blame] | 235 | return Current(); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 236 | } |
rvargas | 747ff24 | 2015-01-17 02:46:47 | [diff] [blame] | 237 | |
rvargas | 6b039c37 | 2015-02-04 21:11:29 | [diff] [blame] | 238 | // On POSIX process handles are the same as PIDs. |
rvargas | 747ff24 | 2015-01-17 02:46:47 | [diff] [blame] | 239 | return Process(pid); |
| 240 | } |
| 241 | |
| 242 | // static |
rvargas | 1c376a8 | 2015-03-16 23:03:52 | [diff] [blame] | 243 | Process Process::OpenWithExtraPrivileges(ProcessId pid) { |
rvargas | 6b039c37 | 2015-02-04 21:11:29 | [diff] [blame] | 244 | // On POSIX there are no privileges to set. |
| 245 | return Open(pid); |
| 246 | } |
| 247 | |
haraken | 940efb9 | 2017-02-08 05:58:15 | [diff] [blame] | 248 | // static |
| 249 | void Process::TerminateCurrentProcessImmediately(int exit_code) { |
Sebastien Marchand | bd02bc29e | 2020-03-11 15:53:36 | [diff] [blame] | 250 | #if BUILDFLAG(CLANG_PROFILING) |
| 251 | WriteClangProfilingProfile(); |
Yuke Liao | a1d9b7b | 2019-08-14 16:14:34 | [diff] [blame] | 252 | #endif |
haraken | 940efb9 | 2017-02-08 05:58:15 | [diff] [blame] | 253 | _exit(exit_code); |
| 254 | } |
| 255 | |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 256 | bool Process::IsValid() const { |
| 257 | return process_ != kNullProcessHandle; |
| 258 | } |
| 259 | |
| 260 | ProcessHandle Process::Handle() const { |
| 261 | return process_; |
| 262 | } |
| 263 | |
| 264 | Process Process::Duplicate() const { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 265 | if (is_current()) { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 266 | return Current(); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 267 | } |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 268 | |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 269 | Process duplicate = Process(process_); |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 270 | #if BUILDFLAG(IS_CHROMEOS) |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 271 | duplicate.unique_token_ = unique_token_; |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 272 | #elif BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK) && TARGET_OS_SIMULATOR |
| 273 | duplicate.content_process_ = content_process_; |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 274 | #endif |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 275 | return duplicate; |
[email protected] | b7d0820 | 2011-01-25 17:29:39 | [diff] [blame] | 276 | } |
| 277 | |
Robert Sesek | 0e7f165a | 2020-11-20 21:31:45 | [diff] [blame] | 278 | ProcessHandle Process::Release() { |
| 279 | return std::exchange(process_, kNullProcessHandle); |
| 280 | } |
| 281 | |
rvargas | 960db88 | 2015-01-24 00:27:25 | [diff] [blame] | 282 | ProcessId Process::Pid() const { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 283 | DCHECK(IsValid()); |
[email protected] | b7d0820 | 2011-01-25 17:29:39 | [diff] [blame] | 284 | return GetProcId(process_); |
| 285 | } |
| 286 | |
| 287 | bool Process::is_current() const { |
| 288 | return process_ == GetCurrentProcessHandle(); |
| 289 | } |
| 290 | |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 291 | void Process::Close() { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 292 | process_ = kNullProcessHandle; |
[email protected] | 276aa6a | 2009-10-29 17:43:44 | [diff] [blame] | 293 | // if the process wasn't terminated (so we waited) or the state |
[email protected] | 1a781364 | 2009-02-05 19:22:01 | [diff] [blame] | 294 | // wasn't already collected w/ a wait from process_utils, we're gonna |
| 295 | // end up w/ a zombie when it does finally exit. |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 296 | } |
| 297 | |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 298 | #if !BUILDFLAG(IS_IOS) |
rvargas | 02ad783 | 2015-04-02 01:36:06 | [diff] [blame] | 299 | bool Process::Terminate(int exit_code, bool wait) const { |
rickyz | 8055dc7 | 2015-07-01 22:50:03 | [diff] [blame] | 300 | // exit_code isn't supportable. |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 301 | DCHECK(IsValid()); |
rickyz | 8055dc7 | 2015-07-01 22:50:03 | [diff] [blame] | 302 | CHECK_GT(process_, 0); |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 303 | return TerminateInternal(exit_code, wait); |
| 304 | } |
| 305 | #endif |
rickyz | 8055dc7 | 2015-07-01 22:50:03 | [diff] [blame] | 306 | |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 307 | #if !BUILDFLAG(IS_IOS) || (BUILDFLAG(USE_BLINK) && TARGET_OS_SIMULATOR) |
| 308 | bool Process::TerminateInternal(int exit_code, bool wait) const { |
Tom Sepez | 8cded807 | 2022-06-27 23:01:43 | [diff] [blame] | 309 | // RESULT_CODE_KILLED_BAD_MESSAGE == 3, but layering prevents its use. |
| 310 | // |wait| is always false when terminating badly-behaved processes. |
| 311 | const bool maybe_compromised = !wait && exit_code == 3; |
| 312 | if (maybe_compromised) { |
| 313 | // Forcibly terminate the process immediately. |
| 314 | const bool was_killed = kill(process_, SIGKILL) != 0; |
| 315 | #if BUILDFLAG(IS_CHROMEOS) |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 316 | if (was_killed) { |
Tom Sepez | 8cded807 | 2022-06-27 23:01:43 | [diff] [blame] | 317 | CleanUpProcessAsync(); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 318 | } |
Tom Sepez | 8cded807 | 2022-06-27 23:01:43 | [diff] [blame] | 319 | #endif |
| 320 | DPLOG_IF(ERROR, !was_killed) << "Unable to terminate process " << process_; |
| 321 | return was_killed; |
| 322 | } |
| 323 | |
| 324 | // Terminate process giving it a chance to clean up. |
Tom Sepez | 75fafe1 | 2022-02-14 18:13:37 | [diff] [blame] | 325 | if (kill(process_, SIGTERM) != 0) { |
rvargas | 02ad783 | 2015-04-02 01:36:06 | [diff] [blame] | 326 | DPLOG(ERROR) << "Unable to terminate process " << process_; |
Tom Sepez | 75fafe1 | 2022-02-14 18:13:37 | [diff] [blame] | 327 | return false; |
| 328 | } |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 329 | |
| 330 | #if BUILDFLAG(IS_CHROMEOS) |
| 331 | CleanUpProcessAsync(); |
| 332 | #endif |
| 333 | |
Tom Sepez | 75fafe1 | 2022-02-14 18:13:37 | [diff] [blame] | 334 | if (!wait || WaitForExitWithTimeout(Seconds(60), nullptr)) { |
| 335 | return true; |
| 336 | } |
| 337 | if (kill(process_, SIGKILL) != 0) { |
| 338 | DPLOG(ERROR) << "Unable to kill process " << process_; |
| 339 | return false; |
| 340 | } |
| 341 | return WaitForExit(nullptr); |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 342 | } |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 343 | #endif |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 344 | |
jcivelli | f4462a35 | 2017-01-10 04:45:59 | [diff] [blame] | 345 | bool Process::WaitForExit(int* exit_code) const { |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 346 | return WaitForExitWithTimeout(TimeDelta::Max(), exit_code); |
rvargas | 126fd582 | 2014-12-12 00:25:14 | [diff] [blame] | 347 | } |
| 348 | |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 349 | #if !BUILDFLAG(IS_IOS) |
jcivelli | f4462a35 | 2017-01-10 04:45:59 | [diff] [blame] | 350 | bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const { |
S. Ganesh | e4a9986 | 2025-04-29 03:30:45 | [diff] [blame] | 351 | timeout = std::max(timeout, TimeDelta()); |
Gabriel Charette | 6836c0d5 | 2021-01-11 17:40:26 | [diff] [blame] | 352 | if (!timeout.is_zero()) { |
Gabriel Charette | 6836c0d5 | 2021-01-11 17:40:26 | [diff] [blame] | 353 | // Assert that this thread is allowed to wait below. This intentionally |
| 354 | // doesn't use ScopedBlockingCallWithBaseSyncPrimitives because the process |
| 355 | // being waited upon tends to itself be using the CPU and considering this |
| 356 | // thread non-busy causes more issue than it fixes: http://crbug.com/905788 |
| 357 | internal::AssertBaseSyncPrimitivesAllowed(); |
| 358 | } |
bcwhite | d970596 | 2016-08-10 03:10:03 | [diff] [blame] | 359 | |
Dmitry Bezheckov | ff22a4d | 2018-07-10 19:42:44 | [diff] [blame] | 360 | int local_exit_code = 0; |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 361 | bool exited = WaitForExitWithTimeoutImpl(Handle(), &local_exit_code, timeout); |
| 362 | if (exited) { |
| 363 | Exited(local_exit_code); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 364 | if (exit_code) { |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 365 | *exit_code = local_exit_code; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 366 | } |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 367 | } |
| 368 | return exited; |
rvargas | 126fd582 | 2014-12-12 00:25:14 | [diff] [blame] | 369 | } |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 370 | #endif |
| 371 | |
| 372 | #if !BUILDFLAG(IS_IOS) || (BUILDFLAG(USE_BLINK) && TARGET_OS_SIMULATOR) |
| 373 | bool Process::WaitForExitWithTimeoutImpl(base::ProcessHandle handle, |
| 374 | int* exit_code, |
| 375 | base::TimeDelta timeout) const { |
S. Ganesh | e4a9986 | 2025-04-29 03:30:45 | [diff] [blame] | 376 | DCHECK_GE(timeout, TimeDelta()); |
| 377 | |
Dave Tapuska | 31c385c | 2024-04-12 18:43:11 | [diff] [blame] | 378 | const base::ProcessHandle our_pid = base::GetCurrentProcessHandle(); |
| 379 | if (handle == our_pid) { |
| 380 | // We won't be able to wait for ourselves to exit. |
| 381 | return false; |
| 382 | } |
| 383 | |
| 384 | TRACE_EVENT0("base", "Process::WaitForExitWithTimeout"); |
| 385 | |
| 386 | const base::ProcessHandle parent_pid = base::GetParentProcessId(handle); |
| 387 | const bool exited = (parent_pid < 0); |
| 388 | |
| 389 | if (!exited && parent_pid != our_pid) { |
| 390 | #if BUILDFLAG(IS_MAC) |
| 391 | // On Mac we can wait on non child processes. |
| 392 | return WaitForSingleNonChildProcess(handle, timeout); |
| 393 | #else |
| 394 | // Currently on Linux we can't handle non child processes. |
| 395 | NOTIMPLEMENTED(); |
| 396 | #endif // BUILDFLAG(IS_MAC) |
| 397 | } |
| 398 | |
| 399 | int status; |
| 400 | if (!WaitpidWithTimeout(handle, &status, timeout)) { |
| 401 | return exited; |
| 402 | } |
| 403 | if (WIFSIGNALED(status)) { |
| 404 | if (exit_code) { |
| 405 | *exit_code = -1; |
| 406 | } |
| 407 | return true; |
| 408 | } |
| 409 | if (WIFEXITED(status)) { |
| 410 | if (exit_code) { |
| 411 | *exit_code = WEXITSTATUS(status); |
| 412 | } |
| 413 | return true; |
| 414 | } |
| 415 | return exited; |
| 416 | } |
| 417 | #endif |
rvargas | 126fd582 | 2014-12-12 00:25:14 | [diff] [blame] | 418 | |
Youssef Esmat | c732bf8 | 2022-05-27 00:47:19 | [diff] [blame] | 419 | void Process::Exited(int exit_code) const { |
| 420 | #if BUILDFLAG(IS_CHROMEOS) |
| 421 | CleanUpProcessAsync(); |
| 422 | #endif |
| 423 | } |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 424 | |
Patrick Monette | 740b81b | 2023-07-21 21:09:09 | [diff] [blame] | 425 | int Process::GetOSPriority() const { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 426 | DCHECK(IsValid()); |
Peter Kasting | 25acd594 | 2022-07-07 17:45:15 | [diff] [blame] | 427 | return getpriority(PRIO_PROCESS, static_cast<id_t>(process_)); |
[email protected] | 276aa6a | 2009-10-29 17:43:44 | [diff] [blame] | 428 | } |
| 429 | |
danakj | c3762b9 | 2015-03-07 01:51:42 | [diff] [blame] | 430 | } // namespace base |