OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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 "sandbox/linux/services/scoped_process.h" |
| 6 |
| 7 #include <fcntl.h> |
| 8 #include <signal.h> |
| 9 #include <sys/stat.h> |
| 10 #include <sys/syscall.h> |
| 11 #include <sys/types.h> |
| 12 #include <sys/wait.h> |
| 13 #include <unistd.h> |
| 14 |
| 15 #include "base/basictypes.h" |
| 16 #include "base/callback.h" |
| 17 #include "base/logging.h" |
| 18 #include "base/posix/eintr_wrapper.h" |
| 19 #include "build/build_config.h" |
| 20 #include "sandbox/linux/services/thread_helpers.h" |
| 21 |
| 22 namespace sandbox { |
| 23 |
| 24 namespace { |
| 25 |
| 26 const char kSynchronisationChar[] = "D"; |
| 27 |
| 28 void WaitForever() { |
| 29 while(true) { |
| 30 pause(); |
| 31 } |
| 32 } |
| 33 |
| 34 } // namespace |
| 35 |
| 36 ScopedProcess::ScopedProcess(const base::Closure& child_callback) |
| 37 : child_process_id_(-1), process_id_(getpid()) { |
| 38 PCHECK(0 == pipe(pipe_fds_)); |
| 39 #if !defined(THREAD_SANITIZER) |
| 40 // Make sure that we can safely fork(). |
| 41 CHECK(ThreadHelpers::IsSingleThreaded(-1)); |
| 42 #endif |
| 43 child_process_id_ = fork(); |
| 44 PCHECK(0 <= child_process_id_); |
| 45 |
| 46 if (0 == child_process_id_) { |
| 47 PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0]))); |
| 48 pipe_fds_[0] = -1; |
| 49 child_callback.Run(); |
| 50 // Notify the parent that the closure has run. |
| 51 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1))); |
| 52 WaitForever(); |
| 53 NOTREACHED(); |
| 54 _exit(1); |
| 55 } |
| 56 |
| 57 PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1]))); |
| 58 pipe_fds_[1] = -1; |
| 59 } |
| 60 |
| 61 ScopedProcess::~ScopedProcess() { |
| 62 CHECK(IsOriginalProcess()); |
| 63 if (child_process_id_ >= 0) { |
| 64 PCHECK(0 == kill(child_process_id_, SIGKILL)); |
| 65 siginfo_t process_info; |
| 66 |
| 67 PCHECK(0 == HANDLE_EINTR( |
| 68 waitid(P_PID, child_process_id_, &process_info, WEXITED))); |
| 69 } |
| 70 if (pipe_fds_[0] >= 0) { |
| 71 PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0]))); |
| 72 } |
| 73 if (pipe_fds_[1] >= 0) { |
| 74 PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1]))); |
| 75 } |
| 76 } |
| 77 |
| 78 int ScopedProcess::WaitForExit(bool* got_signaled) { |
| 79 DCHECK(got_signaled); |
| 80 CHECK(IsOriginalProcess()); |
| 81 siginfo_t process_info; |
| 82 // WNOWAIT to make sure that the destructor can wait on the child. |
| 83 int ret = HANDLE_EINTR( |
| 84 waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT)); |
| 85 PCHECK(0 == ret) << "Did something else wait on the child?"; |
| 86 |
| 87 if (process_info.si_code == CLD_EXITED) { |
| 88 *got_signaled = false; |
| 89 } else if (process_info.si_code == CLD_KILLED || |
| 90 process_info.si_code == CLD_DUMPED) { |
| 91 *got_signaled = true; |
| 92 } else { |
| 93 CHECK(false) << "ScopedProcess needs to be extended for si_code " |
| 94 << process_info.si_code; |
| 95 } |
| 96 return process_info.si_status; |
| 97 } |
| 98 |
| 99 bool ScopedProcess::WaitForClosureToRun() { |
| 100 char c = 0; |
| 101 int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1)); |
| 102 PCHECK(ret >= 0); |
| 103 if (0 == ret) |
| 104 return false; |
| 105 |
| 106 CHECK_EQ(c, kSynchronisationChar[0]); |
| 107 return true; |
| 108 } |
| 109 |
| 110 // It would be problematic if after a fork(), another process would start using |
| 111 // this object. |
| 112 // This method allows to assert it is not happening. |
| 113 bool ScopedProcess::IsOriginalProcess() { |
| 114 // Make a direct syscall to bypass glibc caching of PIDs. |
| 115 int pid = syscall(__NR_getpid); |
| 116 return pid == process_id_; |
| 117 } |
| 118 |
| 119 } // namespace sandbox |
OLD | NEW |