Home | History | Annotate | Download | only in process_proxy
      1 // Copyright (c) 2012 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 "chromeos/process_proxy/process_output_watcher.h"
      6 
      7 #include <algorithm>
      8 #include <cstdio>
      9 #include <cstring>
     10 
     11 #include <sys/ioctl.h>
     12 #include <sys/select.h>
     13 #include <unistd.h>
     14 
     15 #include "base/logging.h"
     16 #include "base/posix/eintr_wrapper.h"
     17 
     18 namespace {
     19 
     20 void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) {
     21   FD_ZERO(set);
     22   if (out_fd != -1)
     23     FD_SET(out_fd, set);
     24   FD_SET(stop_fd, set);
     25 }
     26 
     27 void CloseFd(int* fd) {
     28   if (*fd >= 0) {
     29     if (IGNORE_EINTR(close(*fd)) != 0)
     30       DPLOG(WARNING) << "close fd " << *fd << " failed.";
     31   }
     32   *fd = -1;
     33 }
     34 
     35 }  // namespace
     36 
     37 namespace chromeos {
     38 
     39 ProcessOutputWatcher::ProcessOutputWatcher(int out_fd, int stop_fd,
     40     const ProcessOutputCallback& callback)
     41     : out_fd_(out_fd),
     42       stop_fd_(stop_fd),
     43       on_read_callback_(callback)  {
     44   VerifyFileDescriptor(out_fd_);
     45   VerifyFileDescriptor(stop_fd_);
     46   max_fd_ = std::max(out_fd_, stop_fd_);
     47   // We want to be sure we will be able to add 0 at the end of the input, so -1.
     48   read_buffer_size_ = arraysize(read_buffer_) - 1;
     49 }
     50 
     51 void ProcessOutputWatcher::Start() {
     52   WatchProcessOutput();
     53   OnStop();
     54 }
     55 
     56 ProcessOutputWatcher::~ProcessOutputWatcher() {
     57   CloseFd(&out_fd_);
     58   CloseFd(&stop_fd_);
     59 }
     60 
     61 void ProcessOutputWatcher::WatchProcessOutput() {
     62   while (true) {
     63     // This has to be reset with every watch cycle.
     64     fd_set rfds;
     65     DCHECK(stop_fd_ >= 0);
     66     InitReadFdSet(out_fd_, stop_fd_, &rfds);
     67 
     68     int select_result =
     69         HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL));
     70 
     71     if (select_result < 0) {
     72       DPLOG(WARNING) << "select failed";
     73       return;
     74     }
     75 
     76     // Check if we were stopped.
     77     if (FD_ISSET(stop_fd_, &rfds)) {
     78       return;
     79     }
     80 
     81     if (out_fd_ != -1 && FD_ISSET(out_fd_, &rfds)) {
     82       ReadFromFd(PROCESS_OUTPUT_TYPE_OUT, &out_fd_);
     83     }
     84   }
     85 }
     86 
     87 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) {
     88   CHECK_LE(0, fd);
     89   CHECK_GT(FD_SETSIZE, fd);
     90 }
     91 
     92 void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) {
     93   // We don't want to necessary read pipe until it is empty so we don't starve
     94   // other streams in case data is written faster than we read it. If there is
     95   // more than read_buffer_size_ bytes in pipe, it will be read in the next
     96   // iteration.
     97   ssize_t bytes_read = HANDLE_EINTR(read(*fd, read_buffer_, read_buffer_size_));
     98   if (bytes_read < 0)
     99     DPLOG(WARNING) << "read from buffer failed";
    100 
    101   if (bytes_read > 0) {
    102     on_read_callback_.Run(type, std::string(read_buffer_, bytes_read));
    103   }
    104 
    105   // If there is nothing on the output the watched process has exited (slave end
    106   // of pty is closed).
    107   if (bytes_read <= 0) {
    108     // Slave pseudo terminal has been closed, we won't need master fd anymore.
    109     CloseFd(fd);
    110 
    111     // We have lost contact with the process, so report it.
    112     on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, "");
    113   }
    114 }
    115 
    116 void ProcessOutputWatcher::OnStop() {
    117   delete this;
    118 }
    119 
    120 }  // namespace chromeos
    121