|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "sandbox/linux/services/scoped_process.h" | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <signal.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/syscall.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/wait.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "base/basictypes.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/posix/eintr_wrapper.h" | 
|  | #include "build/build_config.h" | 
|  | #include "sandbox/linux/services/thread_helpers.h" | 
|  |  | 
|  | namespace sandbox { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kSynchronisationChar[] = "D"; | 
|  |  | 
|  | void WaitForever() { | 
|  | while(true) { | 
|  | pause(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | ScopedProcess::ScopedProcess(const base::Closure& child_callback) | 
|  | : child_process_id_(-1), process_id_(getpid()) { | 
|  | PCHECK(0 == pipe(pipe_fds_)); | 
|  | #if !defined(THREAD_SANITIZER) | 
|  | // Make sure that we can safely fork(). | 
|  | CHECK(ThreadHelpers::IsSingleThreaded(-1)); | 
|  | #endif | 
|  | child_process_id_ = fork(); | 
|  | PCHECK(0 <= child_process_id_); | 
|  |  | 
|  | if (0 == child_process_id_) { | 
|  | PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0]))); | 
|  | pipe_fds_[0] = -1; | 
|  | child_callback.Run(); | 
|  | // Notify the parent that the closure has run. | 
|  | CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1))); | 
|  | WaitForever(); | 
|  | NOTREACHED(); | 
|  | _exit(1); | 
|  | } | 
|  |  | 
|  | PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1]))); | 
|  | pipe_fds_[1] = -1; | 
|  | } | 
|  |  | 
|  | ScopedProcess::~ScopedProcess() { | 
|  | CHECK(IsOriginalProcess()); | 
|  | if (child_process_id_ >= 0) { | 
|  | PCHECK(0 == kill(child_process_id_, SIGKILL)); | 
|  | siginfo_t process_info; | 
|  |  | 
|  | PCHECK(0 == HANDLE_EINTR( | 
|  | waitid(P_PID, child_process_id_, &process_info, WEXITED))); | 
|  | } | 
|  | if (pipe_fds_[0] >= 0) { | 
|  | PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0]))); | 
|  | } | 
|  | if (pipe_fds_[1] >= 0) { | 
|  | PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1]))); | 
|  | } | 
|  | } | 
|  |  | 
|  | int ScopedProcess::WaitForExit(bool* got_signaled) { | 
|  | DCHECK(got_signaled); | 
|  | CHECK(IsOriginalProcess()); | 
|  | siginfo_t process_info; | 
|  | // WNOWAIT to make sure that the destructor can wait on the child. | 
|  | int ret = HANDLE_EINTR( | 
|  | waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT)); | 
|  | PCHECK(0 == ret) << "Did something else wait on the child?"; | 
|  |  | 
|  | if (process_info.si_code == CLD_EXITED) { | 
|  | *got_signaled = false; | 
|  | } else if (process_info.si_code == CLD_KILLED || | 
|  | process_info.si_code == CLD_DUMPED) { | 
|  | *got_signaled = true; | 
|  | } else { | 
|  | CHECK(false) << "ScopedProcess needs to be extended for si_code " | 
|  | << process_info.si_code; | 
|  | } | 
|  | return process_info.si_status; | 
|  | } | 
|  |  | 
|  | bool ScopedProcess::WaitForClosureToRun() { | 
|  | char c = 0; | 
|  | int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1)); | 
|  | PCHECK(ret >= 0); | 
|  | if (0 == ret) | 
|  | return false; | 
|  |  | 
|  | CHECK_EQ(c, kSynchronisationChar[0]); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // It would be problematic if after a fork(), another process would start using | 
|  | // this object. | 
|  | // This method allows to assert it is not happening. | 
|  | bool ScopedProcess::IsOriginalProcess() { | 
|  | // Make a direct syscall to bypass glibc caching of PIDs. | 
|  | int pid = syscall(__NR_getpid); | 
|  | return pid == process_id_; | 
|  | } | 
|  |  | 
|  | }  // namespace sandbox |