| OLD | NEW |
| (Empty) |
| 1 // Copyright 2011 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/process.h" | |
| 6 | |
| 7 #include <sys/resource.h> | |
| 8 #include <sys/wait.h> | |
| 9 | |
| 10 #include "base/files/scoped_file.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/posix/eintr_wrapper.h" | |
| 13 #include "base/process/kill.h" | |
| 14 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
| 15 | |
| 16 #if defined(OS_MACOSX) | |
| 17 #include <sys/event.h> | |
| 18 #endif | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 #if !defined(OS_NACL_NONSFI) | |
| 23 | |
| 24 bool WaitpidWithTimeout(base::ProcessHandle handle, | |
| 25 int* status, | |
| 26 base::TimeDelta wait) { | |
| 27 // This POSIX version of this function only guarantees that we wait no less | |
| 28 // than |wait| for the process to exit. The child process may | |
| 29 // exit sometime before the timeout has ended but we may still block for up | |
| 30 // to 256 milliseconds after the fact. | |
| 31 // | |
| 32 // waitpid() has no direct support on POSIX for specifying a timeout, you can | |
| 33 // either ask it to block indefinitely or return immediately (WNOHANG). | |
| 34 // When a child process terminates a SIGCHLD signal is sent to the parent. | |
| 35 // Catching this signal would involve installing a signal handler which may | |
| 36 // affect other parts of the application and would be difficult to debug. | |
| 37 // | |
| 38 // Our strategy is to call waitpid() once up front to check if the process | |
| 39 // has already exited, otherwise to loop for |wait|, sleeping for | |
| 40 // at most 256 milliseconds each time using usleep() and then calling | |
| 41 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and | |
| 42 // we double it every 4 sleep cycles. | |
| 43 // | |
| 44 // usleep() is speced to exit if a signal is received for which a handler | |
| 45 // has been installed. This means that when a SIGCHLD is sent, it will exit | |
| 46 // depending on behavior external to this function. | |
| 47 // | |
| 48 // This function is used primarily for unit tests, if we want to use it in | |
| 49 // the application itself it would probably be best to examine other routes. | |
| 50 | |
| 51 if (wait == base::TimeDelta::Max()) { | |
| 52 return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; | |
| 53 } | |
| 54 | |
| 55 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); | |
| 56 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. | |
| 57 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. | |
| 58 int64 double_sleep_time = 0; | |
| 59 | |
| 60 // If the process hasn't exited yet, then sleep and try again. | |
| 61 base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait; | |
| 62 while (ret_pid == 0) { | |
| 63 base::TimeTicks now = base::TimeTicks::Now(); | |
| 64 if (now > wakeup_time) | |
| 65 break; | |
| 66 // Guaranteed to be non-negative! | |
| 67 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); | |
| 68 // Sleep for a bit while we wait for the process to finish. | |
| 69 if (sleep_time_usecs > max_sleep_time_usecs) | |
| 70 sleep_time_usecs = max_sleep_time_usecs; | |
| 71 | |
| 72 // usleep() will return 0 and set errno to EINTR on receipt of a signal | |
| 73 // such as SIGCHLD. | |
| 74 usleep(sleep_time_usecs); | |
| 75 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); | |
| 76 | |
| 77 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && | |
| 78 (double_sleep_time++ % 4 == 0)) { | |
| 79 max_sleep_time_usecs *= 2; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 return ret_pid > 0; | |
| 84 } | |
| 85 | |
| 86 #if defined(OS_MACOSX) | |
| 87 // Using kqueue on Mac so that we can wait on non-child processes. | |
| 88 // We can't use kqueues on child processes because we need to reap | |
| 89 // our own children using wait. | |
| 90 static bool WaitForSingleNonChildProcess(base::ProcessHandle handle, | |
| 91 base::TimeDelta wait) { | |
| 92 DCHECK_GT(handle, 0); | |
| 93 DCHECK_GT(wait, base::TimeDelta()); | |
| 94 | |
| 95 base::ScopedFD kq(kqueue()); | |
| 96 if (!kq.is_valid()) { | |
| 97 DPLOG(ERROR) << "kqueue"; | |
| 98 return false; | |
| 99 } | |
| 100 | |
| 101 struct kevent change = {0}; | |
| 102 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); | |
| 103 int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); | |
| 104 if (result == -1) { | |
| 105 if (errno == ESRCH) { | |
| 106 // If the process wasn't found, it must be dead. | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 DPLOG(ERROR) << "kevent (setup " << handle << ")"; | |
| 111 return false; | |
| 112 } | |
| 113 | |
| 114 // Keep track of the elapsed time to be able to restart kevent if it's | |
| 115 // interrupted. | |
| 116 bool wait_forever = (wait == base::TimeDelta::Max()); | |
| 117 base::TimeDelta remaining_delta; | |
| 118 base::TimeTicks deadline; | |
| 119 if (!wait_forever) { | |
| 120 remaining_delta = wait; | |
| 121 deadline = base::TimeTicks::Now() + remaining_delta; | |
| 122 } | |
| 123 | |
| 124 result = -1; | |
| 125 struct kevent event = {0}; | |
| 126 | |
| 127 while (wait_forever || remaining_delta > base::TimeDelta()) { | |
| 128 struct timespec remaining_timespec; | |
| 129 struct timespec* remaining_timespec_ptr; | |
| 130 if (wait_forever) { | |
| 131 remaining_timespec_ptr = NULL; | |
| 132 } else { | |
| 133 remaining_timespec = remaining_delta.ToTimeSpec(); | |
| 134 remaining_timespec_ptr = &remaining_timespec; | |
| 135 } | |
| 136 | |
| 137 result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); | |
| 138 | |
| 139 if (result == -1 && errno == EINTR) { | |
| 140 if (!wait_forever) { | |
| 141 remaining_delta = deadline - base::TimeTicks::Now(); | |
| 142 } | |
| 143 result = 0; | |
| 144 } else { | |
| 145 break; | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 if (result < 0) { | |
| 150 DPLOG(ERROR) << "kevent (wait " << handle << ")"; | |
| 151 return false; | |
| 152 } else if (result > 1) { | |
| 153 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " | |
| 154 << result; | |
| 155 return false; | |
| 156 } else if (result == 0) { | |
| 157 // Timed out. | |
| 158 return false; | |
| 159 } | |
| 160 | |
| 161 DCHECK_EQ(result, 1); | |
| 162 | |
| 163 if (event.filter != EVFILT_PROC || | |
| 164 (event.fflags & NOTE_EXIT) == 0 || | |
| 165 event.ident != static_cast<uintptr_t>(handle)) { | |
| 166 DLOG(ERROR) << "kevent (wait " << handle | |
| 167 << "): unexpected event: filter=" << event.filter | |
| 168 << ", fflags=" << event.fflags | |
| 169 << ", ident=" << event.ident; | |
| 170 return false; | |
| 171 } | |
| 172 | |
| 173 return true; | |
| 174 } | |
| 175 #endif // OS_MACOSX | |
| 176 | |
| 177 bool WaitForExitWithTimeoutImpl(base::ProcessHandle handle, | |
| 178 int* exit_code, | |
| 179 base::TimeDelta timeout) { | |
| 180 base::ProcessHandle parent_pid = base::GetParentProcessId(handle); | |
| 181 base::ProcessHandle our_pid = base::GetCurrentProcessHandle(); | |
| 182 if (parent_pid != our_pid) { | |
| 183 #if defined(OS_MACOSX) | |
| 184 // On Mac we can wait on non child processes. | |
| 185 return WaitForSingleNonChildProcess(handle, timeout); | |
| 186 #else | |
| 187 // Currently on Linux we can't handle non child processes. | |
| 188 NOTIMPLEMENTED(); | |
| 189 #endif // OS_MACOSX | |
| 190 } | |
| 191 | |
| 192 int status; | |
| 193 if (!WaitpidWithTimeout(handle, &status, timeout)) | |
| 194 return false; | |
| 195 if (WIFSIGNALED(status)) { | |
| 196 if (exit_code) | |
| 197 *exit_code = -1; | |
| 198 return true; | |
| 199 } | |
| 200 if (WIFEXITED(status)) { | |
| 201 if (exit_code) | |
| 202 *exit_code = WEXITSTATUS(status); | |
| 203 return true; | |
| 204 } | |
| 205 return false; | |
| 206 } | |
| 207 #endif // !defined(OS_NACL_NONSFI) | |
| 208 | |
| 209 } // namespace | |
| 210 | |
| 211 namespace base { | |
| 212 | |
| 213 Process::Process(ProcessHandle handle) : process_(handle) { | |
| 214 } | |
| 215 | |
| 216 Process::~Process() { | |
| 217 } | |
| 218 | |
| 219 Process::Process(RValue other) | |
| 220 : process_(other.object->process_) { | |
| 221 other.object->Close(); | |
| 222 } | |
| 223 | |
| 224 Process& Process::operator=(RValue other) { | |
| 225 if (this != other.object) { | |
| 226 process_ = other.object->process_; | |
| 227 other.object->Close(); | |
| 228 } | |
| 229 return *this; | |
| 230 } | |
| 231 | |
| 232 // static | |
| 233 Process Process::Current() { | |
| 234 return Process(GetCurrentProcessHandle()); | |
| 235 } | |
| 236 | |
| 237 // static | |
| 238 Process Process::Open(ProcessId pid) { | |
| 239 if (pid == GetCurrentProcId()) | |
| 240 return Current(); | |
| 241 | |
| 242 // On POSIX process handles are the same as PIDs. | |
| 243 return Process(pid); | |
| 244 } | |
| 245 | |
| 246 // static | |
| 247 Process Process::OpenWithExtraPrivileges(ProcessId pid) { | |
| 248 // On POSIX there are no privileges to set. | |
| 249 return Open(pid); | |
| 250 } | |
| 251 | |
| 252 // static | |
| 253 Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) { | |
| 254 DCHECK_NE(handle, GetCurrentProcessHandle()); | |
| 255 return Process(handle); | |
| 256 } | |
| 257 | |
| 258 #if !defined(OS_LINUX) && !defined(OS_MACOSX) | |
| 259 // static | |
| 260 bool Process::CanBackgroundProcesses() { | |
| 261 return false; | |
| 262 } | |
| 263 #endif // !defined(OS_LINUX) && !defined(OS_MACOSX) | |
| 264 | |
| 265 bool Process::IsValid() const { | |
| 266 return process_ != kNullProcessHandle; | |
| 267 } | |
| 268 | |
| 269 ProcessHandle Process::Handle() const { | |
| 270 return process_; | |
| 271 } | |
| 272 | |
| 273 Process Process::Duplicate() const { | |
| 274 if (is_current()) | |
| 275 return Current(); | |
| 276 | |
| 277 return Process(process_); | |
| 278 } | |
| 279 | |
| 280 ProcessId Process::Pid() const { | |
| 281 DCHECK(IsValid()); | |
| 282 return GetProcId(process_); | |
| 283 } | |
| 284 | |
| 285 bool Process::is_current() const { | |
| 286 return process_ == GetCurrentProcessHandle(); | |
| 287 } | |
| 288 | |
| 289 void Process::Close() { | |
| 290 process_ = kNullProcessHandle; | |
| 291 // if the process wasn't terminated (so we waited) or the state | |
| 292 // wasn't already collected w/ a wait from process_utils, we're gonna | |
| 293 // end up w/ a zombie when it does finally exit. | |
| 294 } | |
| 295 | |
| 296 #if !defined(OS_NACL_NONSFI) | |
| 297 bool Process::Terminate(int exit_code, bool wait) const { | |
| 298 // exit_code isn't supportable. | |
| 299 DCHECK(IsValid()); | |
| 300 CHECK_GT(process_, 0); | |
| 301 | |
| 302 bool result = kill(process_, SIGTERM) == 0; | |
| 303 if (result && wait) { | |
| 304 int tries = 60; | |
| 305 | |
| 306 if (RunningOnValgrind()) { | |
| 307 // Wait for some extra time when running under Valgrind since the child | |
| 308 // processes may take some time doing leak checking. | |
| 309 tries *= 2; | |
| 310 } | |
| 311 | |
| 312 unsigned sleep_ms = 4; | |
| 313 | |
| 314 // The process may not end immediately due to pending I/O | |
| 315 bool exited = false; | |
| 316 while (tries-- > 0) { | |
| 317 pid_t pid = HANDLE_EINTR(waitpid(process_, NULL, WNOHANG)); | |
| 318 if (pid == process_) { | |
| 319 exited = true; | |
| 320 break; | |
| 321 } | |
| 322 if (pid == -1) { | |
| 323 if (errno == ECHILD) { | |
| 324 // The wait may fail with ECHILD if another process also waited for | |
| 325 // the same pid, causing the process state to get cleaned up. | |
| 326 exited = true; | |
| 327 break; | |
| 328 } | |
| 329 DPLOG(ERROR) << "Error waiting for process " << process_; | |
| 330 } | |
| 331 | |
| 332 usleep(sleep_ms * 1000); | |
| 333 const unsigned kMaxSleepMs = 1000; | |
| 334 if (sleep_ms < kMaxSleepMs) | |
| 335 sleep_ms *= 2; | |
| 336 } | |
| 337 | |
| 338 // If we're waiting and the child hasn't died by now, force it | |
| 339 // with a SIGKILL. | |
| 340 if (!exited) | |
| 341 result = kill(process_, SIGKILL) == 0; | |
| 342 } | |
| 343 | |
| 344 if (!result) | |
| 345 DPLOG(ERROR) << "Unable to terminate process " << process_; | |
| 346 | |
| 347 return result; | |
| 348 } | |
| 349 #endif // !defined(OS_NACL_NONSFI) | |
| 350 | |
| 351 bool Process::WaitForExit(int* exit_code) { | |
| 352 return WaitForExitWithTimeout(TimeDelta::Max(), exit_code); | |
| 353 } | |
| 354 | |
| 355 bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { | |
| 356 return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout); | |
| 357 } | |
| 358 | |
| 359 #if !defined(OS_LINUX) && !defined(OS_MACOSX) | |
| 360 bool Process::IsProcessBackgrounded() const { | |
| 361 // See SetProcessBackgrounded(). | |
| 362 DCHECK(IsValid()); | |
| 363 return false; | |
| 364 } | |
| 365 | |
| 366 bool Process::SetProcessBackgrounded(bool value) { | |
| 367 // Not implemented for POSIX systems other than Mac and Linux. With POSIX, if | |
| 368 // we were to lower the process priority we wouldn't be able to raise it back | |
| 369 // to its initial priority. | |
| 370 NOTIMPLEMENTED(); | |
| 371 return false; | |
| 372 } | |
| 373 #endif // !defined(OS_LINUX) && !defined(OS_MACOSX) | |
| 374 | |
| 375 int Process::GetPriority() const { | |
| 376 DCHECK(IsValid()); | |
| 377 return getpriority(PRIO_PROCESS, process_); | |
| 378 } | |
| 379 | |
| 380 } // namespace base | |
| OLD | NEW |