| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/process/kill.h" | 5 #include "base/process/kill.h" |
| 6 | 6 |
| 7 #include <signal.h> | 7 #include <signal.h> |
| 8 #include <sys/types.h> | 8 #include <sys/types.h> |
| 9 #include <sys/wait.h> | 9 #include <sys/wait.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); | 77 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 78 | 78 |
| 79 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && | 79 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && |
| 80 (double_sleep_time++ % 4 == 0)) { | 80 (double_sleep_time++ % 4 == 0)) { |
| 81 max_sleep_time_usecs *= 2; | 81 max_sleep_time_usecs *= 2; |
| 82 } | 82 } |
| 83 } | 83 } |
| 84 | 84 |
| 85 return ret_pid > 0; | 85 return ret_pid > 0; |
| 86 } | 86 } |
| 87 |
| 88 #if defined(OS_MACOSX) |
| 89 // Using kqueue on Mac so that we can wait on non-child processes. |
| 90 // We can't use kqueues on child processes because we need to reap |
| 91 // our own children using wait. |
| 92 static bool WaitForSingleNonChildProcess(ProcessHandle handle, |
| 93 TimeDelta wait) { |
| 94 DCHECK_GT(handle, 0); |
| 95 DCHECK(wait.InMilliseconds() == kNoTimeout || wait > TimeDelta()); |
| 96 |
| 97 ScopedFD kq(kqueue()); |
| 98 if (!kq.is_valid()) { |
| 99 DPLOG(ERROR) << "kqueue"; |
| 100 return false; |
| 101 } |
| 102 |
| 103 struct kevent change = {0}; |
| 104 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); |
| 105 int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); |
| 106 if (result == -1) { |
| 107 if (errno == ESRCH) { |
| 108 // If the process wasn't found, it must be dead. |
| 109 return true; |
| 110 } |
| 111 |
| 112 DPLOG(ERROR) << "kevent (setup " << handle << ")"; |
| 113 return false; |
| 114 } |
| 115 |
| 116 // Keep track of the elapsed time to be able to restart kevent if it's |
| 117 // interrupted. |
| 118 bool wait_forever = wait.InMilliseconds() == kNoTimeout; |
| 119 TimeDelta remaining_delta; |
| 120 TimeTicks deadline; |
| 121 if (!wait_forever) { |
| 122 remaining_delta = wait; |
| 123 deadline = TimeTicks::Now() + remaining_delta; |
| 124 } |
| 125 |
| 126 result = -1; |
| 127 struct kevent event = {0}; |
| 128 |
| 129 while (wait_forever || remaining_delta > TimeDelta()) { |
| 130 struct timespec remaining_timespec; |
| 131 struct timespec* remaining_timespec_ptr; |
| 132 if (wait_forever) { |
| 133 remaining_timespec_ptr = NULL; |
| 134 } else { |
| 135 remaining_timespec = remaining_delta.ToTimeSpec(); |
| 136 remaining_timespec_ptr = &remaining_timespec; |
| 137 } |
| 138 |
| 139 result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); |
| 140 |
| 141 if (result == -1 && errno == EINTR) { |
| 142 if (!wait_forever) { |
| 143 remaining_delta = deadline - TimeTicks::Now(); |
| 144 } |
| 145 result = 0; |
| 146 } else { |
| 147 break; |
| 148 } |
| 149 } |
| 150 |
| 151 if (result < 0) { |
| 152 DPLOG(ERROR) << "kevent (wait " << handle << ")"; |
| 153 return false; |
| 154 } else if (result > 1) { |
| 155 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " |
| 156 << result; |
| 157 return false; |
| 158 } else if (result == 0) { |
| 159 // Timed out. |
| 160 return false; |
| 161 } |
| 162 |
| 163 DCHECK_EQ(result, 1); |
| 164 |
| 165 if (event.filter != EVFILT_PROC || |
| 166 (event.fflags & NOTE_EXIT) == 0 || |
| 167 event.ident != static_cast<uintptr_t>(handle)) { |
| 168 DLOG(ERROR) << "kevent (wait " << handle |
| 169 << "): unexpected event: filter=" << event.filter |
| 170 << ", fflags=" << event.fflags |
| 171 << ", ident=" << event.ident; |
| 172 return false; |
| 173 } |
| 174 |
| 175 return true; |
| 176 } |
| 177 #endif // OS_MACOSX |
| 87 #endif // !defined(OS_NACL_NONSFI) | 178 #endif // !defined(OS_NACL_NONSFI) |
| 88 | 179 |
| 89 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, | 180 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, |
| 90 bool can_block, | 181 bool can_block, |
| 91 int* exit_code) { | 182 int* exit_code) { |
| 92 int status = 0; | 183 int status = 0; |
| 93 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, | 184 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, |
| 94 can_block ? 0 : WNOHANG)); | 185 can_block ? 0 : WNOHANG)); |
| 95 if (result == -1) { | 186 if (result == -1) { |
| 96 DPLOG(ERROR) << "waitpid(" << handle << ")"; | 187 DPLOG(ERROR) << "waitpid(" << handle << ")"; |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 return true; | 314 return true; |
| 224 } | 315 } |
| 225 | 316 |
| 226 // If it didn't exit cleanly, it must have been signaled. | 317 // If it didn't exit cleanly, it must have been signaled. |
| 227 DCHECK(WIFSIGNALED(status)); | 318 DCHECK(WIFSIGNALED(status)); |
| 228 return false; | 319 return false; |
| 229 } | 320 } |
| 230 | 321 |
| 231 bool WaitForExitCodeWithTimeout(ProcessHandle handle, | 322 bool WaitForExitCodeWithTimeout(ProcessHandle handle, |
| 232 int* exit_code, | 323 int* exit_code, |
| 233 base::TimeDelta timeout) { | 324 TimeDelta timeout) { |
| 325 ProcessHandle parent_pid = GetParentProcessId(handle); |
| 326 ProcessHandle our_pid = GetCurrentProcessHandle(); |
| 327 if (parent_pid != our_pid) { |
| 328 #if defined(OS_MACOSX) |
| 329 // On Mac we can wait on non child processes. |
| 330 return WaitForSingleNonChildProcess(handle, timeout); |
| 331 #else |
| 332 // Currently on Linux we can't handle non child processes. |
| 333 NOTIMPLEMENTED(); |
| 334 #endif // OS_MACOSX |
| 335 } |
| 336 |
| 234 int status; | 337 int status; |
| 235 if (!WaitpidWithTimeout(handle, &status, timeout)) | 338 if (!WaitpidWithTimeout(handle, &status, timeout)) |
| 236 return false; | 339 return false; |
| 237 if (WIFSIGNALED(status)) { | 340 if (WIFSIGNALED(status)) { |
| 238 *exit_code = -1; | 341 *exit_code = -1; |
| 239 return true; | 342 return true; |
| 240 } | 343 } |
| 241 if (WIFEXITED(status)) { | 344 if (WIFEXITED(status)) { |
| 242 *exit_code = WEXITSTATUS(status); | 345 *exit_code = WEXITSTATUS(status); |
| 243 return true; | 346 return true; |
| 244 } | 347 } |
| 245 return false; | 348 return false; |
| 246 } | 349 } |
| 247 | 350 |
| 248 bool WaitForProcessesToExit(const FilePath::StringType& executable_name, | 351 bool WaitForProcessesToExit(const FilePath::StringType& executable_name, |
| 249 base::TimeDelta wait, | 352 TimeDelta wait, |
| 250 const ProcessFilter* filter) { | 353 const ProcessFilter* filter) { |
| 251 bool result = false; | 354 bool result = false; |
| 252 | 355 |
| 253 // TODO(port): This is inefficient, but works if there are multiple procs. | 356 // TODO(port): This is inefficient, but works if there are multiple procs. |
| 254 // TODO(port): use waitpid to avoid leaving zombies around | 357 // TODO(port): use waitpid to avoid leaving zombies around |
| 255 | 358 |
| 256 base::TimeTicks end_time = base::TimeTicks::Now() + wait; | 359 TimeTicks end_time = TimeTicks::Now() + wait; |
| 257 do { | 360 do { |
| 258 NamedProcessIterator iter(executable_name, filter); | 361 NamedProcessIterator iter(executable_name, filter); |
| 259 if (!iter.NextProcessEntry()) { | 362 if (!iter.NextProcessEntry()) { |
| 260 result = true; | 363 result = true; |
| 261 break; | 364 break; |
| 262 } | 365 } |
| 263 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); | 366 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); |
| 264 } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta()); | 367 } while ((end_time - TimeTicks::Now()) > TimeDelta()); |
| 265 | 368 |
| 266 return result; | 369 return result; |
| 267 } | 370 } |
| 268 | 371 |
| 269 #if defined(OS_MACOSX) | |
| 270 // Using kqueue on Mac so that we can wait on non-child processes. | |
| 271 // We can't use kqueues on child processes because we need to reap | |
| 272 // our own children using wait. | |
| 273 static bool WaitForSingleNonChildProcess(ProcessHandle handle, | |
| 274 base::TimeDelta wait) { | |
| 275 DCHECK_GT(handle, 0); | |
| 276 DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta()); | |
| 277 | |
| 278 ScopedFD kq(kqueue()); | |
| 279 if (!kq.is_valid()) { | |
| 280 DPLOG(ERROR) << "kqueue"; | |
| 281 return false; | |
| 282 } | |
| 283 | |
| 284 struct kevent change = {0}; | |
| 285 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); | |
| 286 int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); | |
| 287 if (result == -1) { | |
| 288 if (errno == ESRCH) { | |
| 289 // If the process wasn't found, it must be dead. | |
| 290 return true; | |
| 291 } | |
| 292 | |
| 293 DPLOG(ERROR) << "kevent (setup " << handle << ")"; | |
| 294 return false; | |
| 295 } | |
| 296 | |
| 297 // Keep track of the elapsed time to be able to restart kevent if it's | |
| 298 // interrupted. | |
| 299 bool wait_forever = wait.InMilliseconds() == base::kNoTimeout; | |
| 300 base::TimeDelta remaining_delta; | |
| 301 base::TimeTicks deadline; | |
| 302 if (!wait_forever) { | |
| 303 remaining_delta = wait; | |
| 304 deadline = base::TimeTicks::Now() + remaining_delta; | |
| 305 } | |
| 306 | |
| 307 result = -1; | |
| 308 struct kevent event = {0}; | |
| 309 | |
| 310 while (wait_forever || remaining_delta > base::TimeDelta()) { | |
| 311 struct timespec remaining_timespec; | |
| 312 struct timespec* remaining_timespec_ptr; | |
| 313 if (wait_forever) { | |
| 314 remaining_timespec_ptr = NULL; | |
| 315 } else { | |
| 316 remaining_timespec = remaining_delta.ToTimeSpec(); | |
| 317 remaining_timespec_ptr = &remaining_timespec; | |
| 318 } | |
| 319 | |
| 320 result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); | |
| 321 | |
| 322 if (result == -1 && errno == EINTR) { | |
| 323 if (!wait_forever) { | |
| 324 remaining_delta = deadline - base::TimeTicks::Now(); | |
| 325 } | |
| 326 result = 0; | |
| 327 } else { | |
| 328 break; | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 if (result < 0) { | |
| 333 DPLOG(ERROR) << "kevent (wait " << handle << ")"; | |
| 334 return false; | |
| 335 } else if (result > 1) { | |
| 336 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " | |
| 337 << result; | |
| 338 return false; | |
| 339 } else if (result == 0) { | |
| 340 // Timed out. | |
| 341 return false; | |
| 342 } | |
| 343 | |
| 344 DCHECK_EQ(result, 1); | |
| 345 | |
| 346 if (event.filter != EVFILT_PROC || | |
| 347 (event.fflags & NOTE_EXIT) == 0 || | |
| 348 event.ident != static_cast<uintptr_t>(handle)) { | |
| 349 DLOG(ERROR) << "kevent (wait " << handle | |
| 350 << "): unexpected event: filter=" << event.filter | |
| 351 << ", fflags=" << event.fflags | |
| 352 << ", ident=" << event.ident; | |
| 353 return false; | |
| 354 } | |
| 355 | |
| 356 return true; | |
| 357 } | |
| 358 #endif // OS_MACOSX | |
| 359 | |
| 360 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { | |
| 361 ProcessHandle parent_pid = GetParentProcessId(handle); | |
| 362 ProcessHandle our_pid = GetCurrentProcessHandle(); | |
| 363 if (parent_pid != our_pid) { | |
| 364 #if defined(OS_MACOSX) | |
| 365 // On Mac we can wait on non child processes. | |
| 366 return WaitForSingleNonChildProcess(handle, wait); | |
| 367 #else | |
| 368 // Currently on Linux we can't handle non child processes. | |
| 369 NOTIMPLEMENTED(); | |
| 370 #endif // OS_MACOSX | |
| 371 } | |
| 372 | |
| 373 int status; | |
| 374 if (!WaitpidWithTimeout(handle, &status, wait)) | |
| 375 return false; | |
| 376 return WIFEXITED(status); | |
| 377 } | |
| 378 | |
| 379 bool CleanupProcesses(const FilePath::StringType& executable_name, | 372 bool CleanupProcesses(const FilePath::StringType& executable_name, |
| 380 base::TimeDelta wait, | 373 TimeDelta wait, |
| 381 int exit_code, | 374 int exit_code, |
| 382 const ProcessFilter* filter) { | 375 const ProcessFilter* filter) { |
| 383 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); | 376 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); |
| 384 if (!exited_cleanly) | 377 if (!exited_cleanly) |
| 385 KillProcesses(executable_name, exit_code, filter); | 378 KillProcesses(executable_name, exit_code, filter); |
| 386 return exited_cleanly; | 379 return exited_cleanly; |
| 387 } | 380 } |
| 388 | 381 |
| 389 #if !defined(OS_MACOSX) | 382 #if !defined(OS_MACOSX) |
| 390 | 383 |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 479 return; | 472 return; |
| 480 | 473 |
| 481 BackgroundReaper* reaper = new BackgroundReaper(pid, 0); | 474 BackgroundReaper* reaper = new BackgroundReaper(pid, 0); |
| 482 PlatformThread::CreateNonJoinable(0, reaper); | 475 PlatformThread::CreateNonJoinable(0, reaper); |
| 483 } | 476 } |
| 484 | 477 |
| 485 #endif // !defined(OS_MACOSX) | 478 #endif // !defined(OS_MACOSX) |
| 486 #endif // !defined(OS_NACL_NONSFI) | 479 #endif // !defined(OS_NACL_NONSFI) |
| 487 | 480 |
| 488 } // namespace base | 481 } // namespace base |
| OLD | NEW |