OLD | NEW |
(Empty) | |
| 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 <signal.h> |
| 8 #include <sys/types.h> |
| 9 #include <sys/wait.h> |
| 10 #include <unistd.h> |
| 11 |
| 12 #include "base/file_util.h" |
| 13 #include "base/logging.h" |
| 14 #include "base/posix/eintr_wrapper.h" |
| 15 #include "base/process/process_iterator.h" |
| 16 #include "base/process_util.h" |
| 17 #include "base/synchronization/waitable_event.h" |
| 18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
| 19 #include "base/threading/platform_thread.h" |
| 20 |
| 21 namespace base { |
| 22 |
| 23 namespace { |
| 24 |
| 25 int WaitpidWithTimeout(ProcessHandle handle, |
| 26 int64 wait_milliseconds, |
| 27 bool* success) { |
| 28 // This POSIX version of this function only guarantees that we wait no less |
| 29 // than |wait_milliseconds| for the process to exit. The child process may |
| 30 // exit sometime before the timeout has ended but we may still block for up |
| 31 // to 256 milliseconds after the fact. |
| 32 // |
| 33 // waitpid() has no direct support on POSIX for specifying a timeout, you can |
| 34 // either ask it to block indefinitely or return immediately (WNOHANG). |
| 35 // When a child process terminates a SIGCHLD signal is sent to the parent. |
| 36 // Catching this signal would involve installing a signal handler which may |
| 37 // affect other parts of the application and would be difficult to debug. |
| 38 // |
| 39 // Our strategy is to call waitpid() once up front to check if the process |
| 40 // has already exited, otherwise to loop for wait_milliseconds, sleeping for |
| 41 // at most 256 milliseconds each time using usleep() and then calling |
| 42 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and |
| 43 // we double it every 4 sleep cycles. |
| 44 // |
| 45 // usleep() is speced to exit if a signal is received for which a handler |
| 46 // has been installed. This means that when a SIGCHLD is sent, it will exit |
| 47 // depending on behavior external to this function. |
| 48 // |
| 49 // This function is used primarily for unit tests, if we want to use it in |
| 50 // the application itself it would probably be best to examine other routes. |
| 51 int status = -1; |
| 52 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); |
| 53 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. |
| 54 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. |
| 55 int64 double_sleep_time = 0; |
| 56 |
| 57 // If the process hasn't exited yet, then sleep and try again. |
| 58 TimeTicks wakeup_time = TimeTicks::Now() + |
| 59 TimeDelta::FromMilliseconds(wait_milliseconds); |
| 60 while (ret_pid == 0) { |
| 61 TimeTicks now = TimeTicks::Now(); |
| 62 if (now > wakeup_time) |
| 63 break; |
| 64 // Guaranteed to be non-negative! |
| 65 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); |
| 66 // Sleep for a bit while we wait for the process to finish. |
| 67 if (sleep_time_usecs > max_sleep_time_usecs) |
| 68 sleep_time_usecs = max_sleep_time_usecs; |
| 69 |
| 70 // usleep() will return 0 and set errno to EINTR on receipt of a signal |
| 71 // such as SIGCHLD. |
| 72 usleep(sleep_time_usecs); |
| 73 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); |
| 74 |
| 75 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && |
| 76 (double_sleep_time++ % 4 == 0)) { |
| 77 max_sleep_time_usecs *= 2; |
| 78 } |
| 79 } |
| 80 |
| 81 if (success) |
| 82 *success = (ret_pid != -1); |
| 83 |
| 84 return status; |
| 85 } |
| 86 |
| 87 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, |
| 88 bool can_block, |
| 89 int* exit_code) { |
| 90 int status = 0; |
| 91 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, |
| 92 can_block ? 0 : WNOHANG)); |
| 93 if (result == -1) { |
| 94 DPLOG(ERROR) << "waitpid(" << handle << ")"; |
| 95 if (exit_code) |
| 96 *exit_code = 0; |
| 97 return TERMINATION_STATUS_NORMAL_TERMINATION; |
| 98 } else if (result == 0) { |
| 99 // the child hasn't exited yet. |
| 100 if (exit_code) |
| 101 *exit_code = 0; |
| 102 return TERMINATION_STATUS_STILL_RUNNING; |
| 103 } |
| 104 |
| 105 if (exit_code) |
| 106 *exit_code = status; |
| 107 |
| 108 if (WIFSIGNALED(status)) { |
| 109 switch (WTERMSIG(status)) { |
| 110 case SIGABRT: |
| 111 case SIGBUS: |
| 112 case SIGFPE: |
| 113 case SIGILL: |
| 114 case SIGSEGV: |
| 115 return TERMINATION_STATUS_PROCESS_CRASHED; |
| 116 case SIGINT: |
| 117 case SIGKILL: |
| 118 case SIGTERM: |
| 119 return TERMINATION_STATUS_PROCESS_WAS_KILLED; |
| 120 default: |
| 121 break; |
| 122 } |
| 123 } |
| 124 |
| 125 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) |
| 126 return TERMINATION_STATUS_ABNORMAL_TERMINATION; |
| 127 |
| 128 return TERMINATION_STATUS_NORMAL_TERMINATION; |
| 129 } |
| 130 |
| 131 } // namespace |
| 132 |
| 133 // Attempts to kill the process identified by the given process |
| 134 // entry structure. Ignores specified exit_code; posix can't force that. |
| 135 // Returns true if this is successful, false otherwise. |
| 136 bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) { |
| 137 DCHECK_GT(process_id, 1) << " tried to kill invalid process_id"; |
| 138 if (process_id <= 1) |
| 139 return false; |
| 140 bool result = kill(process_id, SIGTERM) == 0; |
| 141 if (result && wait) { |
| 142 int tries = 60; |
| 143 |
| 144 if (RunningOnValgrind()) { |
| 145 // Wait for some extra time when running under Valgrind since the child |
| 146 // processes may take some time doing leak checking. |
| 147 tries *= 2; |
| 148 } |
| 149 |
| 150 unsigned sleep_ms = 4; |
| 151 |
| 152 // The process may not end immediately due to pending I/O |
| 153 bool exited = false; |
| 154 while (tries-- > 0) { |
| 155 pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG)); |
| 156 if (pid == process_id) { |
| 157 exited = true; |
| 158 break; |
| 159 } |
| 160 if (pid == -1) { |
| 161 if (errno == ECHILD) { |
| 162 // The wait may fail with ECHILD if another process also waited for |
| 163 // the same pid, causing the process state to get cleaned up. |
| 164 exited = true; |
| 165 break; |
| 166 } |
| 167 DPLOG(ERROR) << "Error waiting for process " << process_id; |
| 168 } |
| 169 |
| 170 usleep(sleep_ms * 1000); |
| 171 const unsigned kMaxSleepMs = 1000; |
| 172 if (sleep_ms < kMaxSleepMs) |
| 173 sleep_ms *= 2; |
| 174 } |
| 175 |
| 176 // If we're waiting and the child hasn't died by now, force it |
| 177 // with a SIGKILL. |
| 178 if (!exited) |
| 179 result = kill(process_id, SIGKILL) == 0; |
| 180 } |
| 181 |
| 182 if (!result) |
| 183 DPLOG(ERROR) << "Unable to terminate process " << process_id; |
| 184 |
| 185 return result; |
| 186 } |
| 187 |
| 188 bool KillProcessGroup(ProcessHandle process_group_id) { |
| 189 bool result = kill(-1 * process_group_id, SIGKILL) == 0; |
| 190 if (!result) |
| 191 DPLOG(ERROR) << "Unable to terminate process group " << process_group_id; |
| 192 return result; |
| 193 } |
| 194 |
| 195 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { |
| 196 return GetTerminationStatusImpl(handle, false /* can_block */, exit_code); |
| 197 } |
| 198 |
| 199 TerminationStatus WaitForTerminationStatus(ProcessHandle handle, |
| 200 int* exit_code) { |
| 201 return GetTerminationStatusImpl(handle, true /* can_block */, exit_code); |
| 202 } |
| 203 |
| 204 bool WaitForExitCode(ProcessHandle handle, int* exit_code) { |
| 205 int status; |
| 206 if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) { |
| 207 NOTREACHED(); |
| 208 return false; |
| 209 } |
| 210 |
| 211 if (WIFEXITED(status)) { |
| 212 *exit_code = WEXITSTATUS(status); |
| 213 return true; |
| 214 } |
| 215 |
| 216 // If it didn't exit cleanly, it must have been signaled. |
| 217 DCHECK(WIFSIGNALED(status)); |
| 218 return false; |
| 219 } |
| 220 |
| 221 bool WaitForExitCodeWithTimeout(ProcessHandle handle, |
| 222 int* exit_code, |
| 223 base::TimeDelta timeout) { |
| 224 bool waitpid_success = false; |
| 225 int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(), |
| 226 &waitpid_success); |
| 227 if (status == -1) |
| 228 return false; |
| 229 if (!waitpid_success) |
| 230 return false; |
| 231 if (WIFSIGNALED(status)) { |
| 232 *exit_code = -1; |
| 233 return true; |
| 234 } |
| 235 if (WIFEXITED(status)) { |
| 236 *exit_code = WEXITSTATUS(status); |
| 237 return true; |
| 238 } |
| 239 return false; |
| 240 } |
| 241 |
| 242 bool WaitForProcessesToExit(const FilePath::StringType& executable_name, |
| 243 base::TimeDelta wait, |
| 244 const ProcessFilter* filter) { |
| 245 bool result = false; |
| 246 |
| 247 // TODO(port): This is inefficient, but works if there are multiple procs. |
| 248 // TODO(port): use waitpid to avoid leaving zombies around |
| 249 |
| 250 base::TimeTicks end_time = base::TimeTicks::Now() + wait; |
| 251 do { |
| 252 NamedProcessIterator iter(executable_name, filter); |
| 253 if (!iter.NextProcessEntry()) { |
| 254 result = true; |
| 255 break; |
| 256 } |
| 257 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); |
| 258 } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta()); |
| 259 |
| 260 return result; |
| 261 } |
| 262 |
| 263 #if defined(OS_MACOSX) |
| 264 // Using kqueue on Mac so that we can wait on non-child processes. |
| 265 // We can't use kqueues on child processes because we need to reap |
| 266 // our own children using wait. |
| 267 static bool WaitForSingleNonChildProcess(ProcessHandle handle, |
| 268 base::TimeDelta wait) { |
| 269 DCHECK_GT(handle, 0); |
| 270 DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta()); |
| 271 |
| 272 int kq = kqueue(); |
| 273 if (kq == -1) { |
| 274 DPLOG(ERROR) << "kqueue"; |
| 275 return false; |
| 276 } |
| 277 file_util::ScopedFD kq_closer(&kq); |
| 278 |
| 279 struct kevent change = {0}; |
| 280 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); |
| 281 int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); |
| 282 if (result == -1) { |
| 283 if (errno == ESRCH) { |
| 284 // If the process wasn't found, it must be dead. |
| 285 return true; |
| 286 } |
| 287 |
| 288 DPLOG(ERROR) << "kevent (setup " << handle << ")"; |
| 289 return false; |
| 290 } |
| 291 |
| 292 // Keep track of the elapsed time to be able to restart kevent if it's |
| 293 // interrupted. |
| 294 bool wait_forever = wait.InMilliseconds() == base::kNoTimeout; |
| 295 base::TimeDelta remaining_delta; |
| 296 base::TimeTicks deadline; |
| 297 if (!wait_forever) { |
| 298 remaining_delta = wait; |
| 299 deadline = base::TimeTicks::Now() + remaining_delta; |
| 300 } |
| 301 |
| 302 result = -1; |
| 303 struct kevent event = {0}; |
| 304 |
| 305 while (wait_forever || remaining_delta > base::TimeDelta()) { |
| 306 struct timespec remaining_timespec; |
| 307 struct timespec* remaining_timespec_ptr; |
| 308 if (wait_forever) { |
| 309 remaining_timespec_ptr = NULL; |
| 310 } else { |
| 311 remaining_timespec = remaining_delta.ToTimeSpec(); |
| 312 remaining_timespec_ptr = &remaining_timespec; |
| 313 } |
| 314 |
| 315 result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr); |
| 316 |
| 317 if (result == -1 && errno == EINTR) { |
| 318 if (!wait_forever) { |
| 319 remaining_delta = deadline - base::TimeTicks::Now(); |
| 320 } |
| 321 result = 0; |
| 322 } else { |
| 323 break; |
| 324 } |
| 325 } |
| 326 |
| 327 if (result < 0) { |
| 328 DPLOG(ERROR) << "kevent (wait " << handle << ")"; |
| 329 return false; |
| 330 } else if (result > 1) { |
| 331 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " |
| 332 << result; |
| 333 return false; |
| 334 } else if (result == 0) { |
| 335 // Timed out. |
| 336 return false; |
| 337 } |
| 338 |
| 339 DCHECK_EQ(result, 1); |
| 340 |
| 341 if (event.filter != EVFILT_PROC || |
| 342 (event.fflags & NOTE_EXIT) == 0 || |
| 343 event.ident != static_cast<uintptr_t>(handle)) { |
| 344 DLOG(ERROR) << "kevent (wait " << handle |
| 345 << "): unexpected event: filter=" << event.filter |
| 346 << ", fflags=" << event.fflags |
| 347 << ", ident=" << event.ident; |
| 348 return false; |
| 349 } |
| 350 |
| 351 return true; |
| 352 } |
| 353 #endif // OS_MACOSX |
| 354 |
| 355 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { |
| 356 ProcessHandle parent_pid = GetParentProcessId(handle); |
| 357 ProcessHandle our_pid = Process::Current().handle(); |
| 358 if (parent_pid != our_pid) { |
| 359 #if defined(OS_MACOSX) |
| 360 // On Mac we can wait on non child processes. |
| 361 return WaitForSingleNonChildProcess(handle, wait); |
| 362 #else |
| 363 // Currently on Linux we can't handle non child processes. |
| 364 NOTIMPLEMENTED(); |
| 365 #endif // OS_MACOSX |
| 366 } |
| 367 |
| 368 bool waitpid_success; |
| 369 int status = -1; |
| 370 if (wait.InMilliseconds() == base::kNoTimeout) { |
| 371 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); |
| 372 } else { |
| 373 status = WaitpidWithTimeout( |
| 374 handle, wait.InMilliseconds(), &waitpid_success); |
| 375 } |
| 376 |
| 377 if (status != -1) { |
| 378 DCHECK(waitpid_success); |
| 379 return WIFEXITED(status); |
| 380 } else { |
| 381 return false; |
| 382 } |
| 383 } |
| 384 |
| 385 bool CleanupProcesses(const FilePath::StringType& executable_name, |
| 386 base::TimeDelta wait, |
| 387 int exit_code, |
| 388 const ProcessFilter* filter) { |
| 389 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); |
| 390 if (!exited_cleanly) |
| 391 KillProcesses(executable_name, exit_code, filter); |
| 392 return exited_cleanly; |
| 393 } |
| 394 |
| 395 #if !defined(OS_MACOSX) |
| 396 |
| 397 namespace { |
| 398 |
| 399 // Return true if the given child is dead. This will also reap the process. |
| 400 // Doesn't block. |
| 401 static bool IsChildDead(pid_t child) { |
| 402 const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); |
| 403 if (result == -1) { |
| 404 DPLOG(ERROR) << "waitpid(" << child << ")"; |
| 405 NOTREACHED(); |
| 406 } else if (result > 0) { |
| 407 // The child has died. |
| 408 return true; |
| 409 } |
| 410 |
| 411 return false; |
| 412 } |
| 413 |
| 414 // A thread class which waits for the given child to exit and reaps it. |
| 415 // If the child doesn't exit within a couple of seconds, kill it. |
| 416 class BackgroundReaper : public PlatformThread::Delegate { |
| 417 public: |
| 418 BackgroundReaper(pid_t child, unsigned timeout) |
| 419 : child_(child), |
| 420 timeout_(timeout) { |
| 421 } |
| 422 |
| 423 // Overridden from PlatformThread::Delegate: |
| 424 virtual void ThreadMain() OVERRIDE { |
| 425 WaitForChildToDie(); |
| 426 delete this; |
| 427 } |
| 428 |
| 429 void WaitForChildToDie() { |
| 430 // Wait forever case. |
| 431 if (timeout_ == 0) { |
| 432 pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0)); |
| 433 if (r != child_) { |
| 434 DPLOG(ERROR) << "While waiting for " << child_ |
| 435 << " to terminate, we got the following result: " << r; |
| 436 } |
| 437 return; |
| 438 } |
| 439 |
| 440 // There's no good way to wait for a specific child to exit in a timed |
| 441 // fashion. (No kqueue on Linux), so we just loop and sleep. |
| 442 |
| 443 // Wait for 2 * timeout_ 500 milliseconds intervals. |
| 444 for (unsigned i = 0; i < 2 * timeout_; ++i) { |
| 445 PlatformThread::Sleep(TimeDelta::FromMilliseconds(500)); |
| 446 if (IsChildDead(child_)) |
| 447 return; |
| 448 } |
| 449 |
| 450 if (kill(child_, SIGKILL) == 0) { |
| 451 // SIGKILL is uncatchable. Since the signal was delivered, we can |
| 452 // just wait for the process to die now in a blocking manner. |
| 453 if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0) |
| 454 DPLOG(WARNING) << "waitpid"; |
| 455 } else { |
| 456 DLOG(ERROR) << "While waiting for " << child_ << " to terminate we" |
| 457 << " failed to deliver a SIGKILL signal (" << errno << ")."; |
| 458 } |
| 459 } |
| 460 |
| 461 private: |
| 462 const pid_t child_; |
| 463 // Number of seconds to wait, if 0 then wait forever and do not attempt to |
| 464 // kill |child_|. |
| 465 const unsigned timeout_; |
| 466 |
| 467 DISALLOW_COPY_AND_ASSIGN(BackgroundReaper); |
| 468 }; |
| 469 |
| 470 } // namespace |
| 471 |
| 472 void EnsureProcessTerminated(ProcessHandle process) { |
| 473 // If the child is already dead, then there's nothing to do. |
| 474 if (IsChildDead(process)) |
| 475 return; |
| 476 |
| 477 const unsigned timeout = 2; // seconds |
| 478 BackgroundReaper* reaper = new BackgroundReaper(process, timeout); |
| 479 PlatformThread::CreateNonJoinable(0, reaper); |
| 480 } |
| 481 |
| 482 void EnsureProcessGetsReaped(ProcessHandle process) { |
| 483 // If the child is already dead, then there's nothing to do. |
| 484 if (IsChildDead(process)) |
| 485 return; |
| 486 |
| 487 BackgroundReaper* reaper = new BackgroundReaper(process, 0); |
| 488 PlatformThread::CreateNonJoinable(0, reaper); |
| 489 } |
| 490 |
| 491 #endif // !defined(OS_MACOSX) |
| 492 |
| 493 } // namespace base |
OLD | NEW |