Home | History | Annotate | Download | only in services
      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
    120