Index: sandbox/linux/services/scoped_process.cc |
diff --git a/sandbox/linux/services/scoped_process.cc b/sandbox/linux/services/scoped_process.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fd42a2a6e17464c89739a14971fe72c8078ed500 |
--- /dev/null |
+++ b/sandbox/linux/services/scoped_process.cc |
@@ -0,0 +1,119 @@ |
+// 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 |