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 <sys/ioctl.h>
      8 #include <sys/select.h>
      9 #include <unistd.h>
     10 
     11 #include <algorithm>
     12 #include <cstdio>
     13 #include <cstring>
     14 
     15 #include "base/logging.h"
     16 #include "base/posix/eintr_wrapper.h"
     17 #include "base/third_party/icu/icu_utf.h"
     18 
     19 namespace {
     20 
     21 void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) {
     22   FD_ZERO(set);
     23   if (out_fd != -1)
     24     FD_SET(out_fd, set);
     25   FD_SET(stop_fd, set);
     26 }
     27 
     28 void CloseFd(int* fd) {
     29   if (*fd >= 0) {
     30     if (IGNORE_EINTR(close(*fd)) != 0)
     31       DPLOG(WARNING) << "close fd " << *fd << " failed.";
     32   }
     33   *fd = -1;
     34 }
     35 
     36 // Gets byte size for a UTF8 character given it's leading byte. The character
     37 // size is encoded as number of leading '1' bits in the character's leading
     38 // byte. If the most significant bit is '0', the character is a valid ASCII
     39 // and it's byte size is 1.
     40 // The method returns 1 if the provided byte is invalid leading byte.
     41 size_t UTF8SizeFromLeadingByte(uint8 leading_byte) {
     42   size_t byte_count = 0;
     43   uint8 mask = 1 << 7;
     44   uint8 error_mask = 1 << (7 - CBU8_MAX_LENGTH);
     45   while (leading_byte & mask) {
     46     if (mask & error_mask)
     47       return 1;
     48     mask >>= 1;
     49     ++byte_count;
     50   }
     51   return byte_count ? byte_count : 1;
     52 }
     53 
     54 }  // namespace
     55 
     56 namespace chromeos {
     57 
     58 ProcessOutputWatcher::ProcessOutputWatcher(
     59     int out_fd,
     60     int stop_fd,
     61     const ProcessOutputCallback& callback)
     62     : read_buffer_size_(0),
     63       out_fd_(out_fd),
     64       stop_fd_(stop_fd),
     65       on_read_callback_(callback) {
     66   VerifyFileDescriptor(out_fd_);
     67   VerifyFileDescriptor(stop_fd_);
     68   max_fd_ = std::max(out_fd_, stop_fd_);
     69   // We want to be sure we will be able to add 0 at the end of the input, so -1.
     70   read_buffer_capacity_ = arraysize(read_buffer_) - 1;
     71 }
     72 
     73 void ProcessOutputWatcher::Start() {
     74   WatchProcessOutput();
     75   OnStop();
     76 }
     77 
     78 ProcessOutputWatcher::~ProcessOutputWatcher() {
     79   CloseFd(&out_fd_);
     80   CloseFd(&stop_fd_);
     81 }
     82 
     83 void ProcessOutputWatcher::WatchProcessOutput() {
     84   while (true) {
     85     // This has to be reset with every watch cycle.
     86     fd_set rfds;
     87     DCHECK_GE(stop_fd_, 0);
     88     InitReadFdSet(out_fd_, stop_fd_, &rfds);
     89 
     90     int select_result =
     91         HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL));
     92 
     93     if (select_result < 0) {
     94       DPLOG(WARNING) << "select failed";
     95       return;
     96     }
     97 
     98     // Check if we were stopped.
     99     if (FD_ISSET(stop_fd_, &rfds)) {
    100       return;
    101     }
    102 
    103     if (out_fd_ != -1 && FD_ISSET(out_fd_, &rfds)) {
    104       ReadFromFd(PROCESS_OUTPUT_TYPE_OUT, &out_fd_);
    105     }
    106   }
    107 }
    108 
    109 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) {
    110   CHECK_LE(0, fd);
    111   CHECK_GT(FD_SETSIZE, fd);
    112 }
    113 
    114 void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) {
    115   // We don't want to necessary read pipe until it is empty so we don't starve
    116   // other streams in case data is written faster than we read it. If there is
    117   // more than read_buffer_size_ bytes in pipe, it will be read in the next
    118   // iteration.
    119   DCHECK_GT(read_buffer_capacity_, read_buffer_size_);
    120   ssize_t bytes_read =
    121       HANDLE_EINTR(read(*fd,
    122                         &read_buffer_[read_buffer_size_],
    123                         read_buffer_capacity_ - read_buffer_size_));
    124   if (bytes_read < 0)
    125     DPLOG(WARNING) << "read from buffer failed";
    126 
    127   if (bytes_read > 0)
    128     ReportOutput(type, bytes_read);
    129 
    130   // If there is nothing on the output the watched process has exited (slave end
    131   // of pty is closed).
    132   if (bytes_read <= 0) {
    133     // Slave pseudo terminal has been closed, we won't need master fd anymore.
    134     CloseFd(fd);
    135 
    136     // We have lost contact with the process, so report it.
    137     on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, "");
    138   }
    139 }
    140 
    141 size_t ProcessOutputWatcher::OutputSizeWithoutIncompleteUTF8() {
    142   // Find the last non-trailing character byte. This byte should be used to
    143   // infer the last UTF8 character length.
    144   int last_lead_byte = read_buffer_size_ - 1;
    145   while (true) {
    146     // If the series of trailing bytes is too long, something's not right.
    147     // Report the whole output, without waiting for further character bytes.
    148     if (read_buffer_size_ - last_lead_byte > CBU8_MAX_LENGTH)
    149       return read_buffer_size_;
    150 
    151     // If there are trailing characters, there must be a leading one in the
    152     // buffer for a valid UTF8 character. Getting past the buffer begining
    153     // signals something's wrong, or the buffer is empty. In both cases return
    154     // the whole current buffer.
    155     if (last_lead_byte < 0)
    156       return read_buffer_size_;
    157 
    158     // Found the starting character byte; stop searching.
    159     if (!CBU8_IS_TRAIL(read_buffer_[last_lead_byte]))
    160       break;
    161 
    162     --last_lead_byte;
    163   }
    164 
    165   size_t last_length = UTF8SizeFromLeadingByte(read_buffer_[last_lead_byte]);
    166 
    167   // Note that if |last_length| == 0 or
    168   // |last_length| + |last_read_byte| < |read_buffer_size_|, the string is
    169   // invalid UTF8. In that case, send the whole read buffer to the observer
    170   // immediately, just as if there is no trailing incomplete UTF8 bytes.
    171   if (!last_length || last_length + last_lead_byte <= read_buffer_size_)
    172     return read_buffer_size_;
    173 
    174   return last_lead_byte;
    175 }
    176 
    177 void ProcessOutputWatcher::ReportOutput(ProcessOutputType type,
    178                                         size_t new_bytes_count) {
    179   read_buffer_size_ += new_bytes_count;
    180   size_t output_to_report = OutputSizeWithoutIncompleteUTF8();
    181 
    182   on_read_callback_.Run(type, std::string(read_buffer_, output_to_report));
    183 
    184   // Move the bytes that were left behind to the beginning of the buffer and
    185   // update the buffer size accordingly.
    186   if (output_to_report < read_buffer_size_) {
    187     for (size_t i = output_to_report; i < read_buffer_size_; ++i) {
    188       read_buffer_[i - output_to_report] = read_buffer_[i];
    189     }
    190   }
    191   read_buffer_size_ -= output_to_report;
    192 }
    193 
    194 void ProcessOutputWatcher::OnStop() {
    195   delete this;
    196 }
    197 
    198 }  // namespace chromeos
    199