15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chromeos/process_proxy/process_output_watcher.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/ioctl.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/select.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <unistd.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <algorithm>
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <cstdio>
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <cstring>
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/posix/eintr_wrapper.h"
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/third_party/icu/icu_utf.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FD_ZERO(set);
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (out_fd != -1)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FD_SET(out_fd, set);
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FD_SET(stop_fd, set);
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CloseFd(int* fd) {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (*fd >= 0) {
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (IGNORE_EINTR(close(*fd)) != 0)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DPLOG(WARNING) << "close fd " << *fd << " failed.";
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *fd = -1;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Gets byte size for a UTF8 character given it's leading byte. The character
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// size is encoded as number of leading '1' bits in the character's leading
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// byte. If the most significant bit is '0', the character is a valid ASCII
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// and it's byte size is 1.
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// The method returns 1 if the provided byte is invalid leading byte.
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)size_t UTF8SizeFromLeadingByte(uint8 leading_byte) {
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  size_t byte_count = 0;
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  uint8 mask = 1 << 7;
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  uint8 error_mask = 1 << (7 - CBU8_MAX_LENGTH);
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  while (leading_byte & mask) {
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (mask & error_mask)
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return 1;
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    mask >>= 1;
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    ++byte_count;
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return byte_count ? byte_count : 1;
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace chromeos {
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ProcessOutputWatcher::ProcessOutputWatcher(
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    int out_fd,
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    int stop_fd,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ProcessOutputCallback& callback)
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    : read_buffer_size_(0),
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      out_fd_(out_fd),
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stop_fd_(stop_fd),
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      on_read_callback_(callback) {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerifyFileDescriptor(out_fd_);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerifyFileDescriptor(stop_fd_);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  max_fd_ = std::max(out_fd_, stop_fd_);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We want to be sure we will be able to add 0 at the end of the input, so -1.
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  read_buffer_capacity_ = arraysize(read_buffer_) - 1;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProcessOutputWatcher::Start() {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WatchProcessOutput();
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnStop();
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ProcessOutputWatcher::~ProcessOutputWatcher() {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseFd(&out_fd_);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseFd(&stop_fd_);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProcessOutputWatcher::WatchProcessOutput() {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (true) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This has to be reset with every watch cycle.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fd_set rfds;
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    DCHECK_GE(stop_fd_, 0);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InitReadFdSet(out_fd_, stop_fd_, &rfds);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int select_result =
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL));
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (select_result < 0) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DPLOG(WARNING) << "select failed";
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Check if we were stopped.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (FD_ISSET(stop_fd_, &rfds)) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (out_fd_ != -1 && FD_ISSET(out_fd_, &rfds)) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReadFromFd(PROCESS_OUTPUT_TYPE_OUT, &out_fd_);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProcessOutputWatcher::VerifyFileDescriptor(int fd) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_LE(0, fd);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_GT(FD_SETSIZE, fd);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't want to necessary read pipe until it is empty so we don't starve
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // other streams in case data is written faster than we read it. If there is
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // more than read_buffer_size_ bytes in pipe, it will be read in the next
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // iteration.
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK_GT(read_buffer_capacity_, read_buffer_size_);
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  ssize_t bytes_read =
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      HANDLE_EINTR(read(*fd,
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        &read_buffer_[read_buffer_size_],
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        read_buffer_capacity_ - read_buffer_size_));
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bytes_read < 0)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(WARNING) << "read from buffer failed";
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (bytes_read > 0)
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    ReportOutput(type, bytes_read);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there is nothing on the output the watched process has exited (slave end
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // of pty is closed).
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bytes_read <= 0) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Slave pseudo terminal has been closed, we won't need master fd anymore.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CloseFd(fd);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We have lost contact with the process, so report it.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, "");
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)size_t ProcessOutputWatcher::OutputSizeWithoutIncompleteUTF8() {
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Find the last non-trailing character byte. This byte should be used to
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // infer the last UTF8 character length.
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  int last_lead_byte = read_buffer_size_ - 1;
145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  while (true) {
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // If the series of trailing bytes is too long, something's not right.
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // Report the whole output, without waiting for further character bytes.
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (read_buffer_size_ - last_lead_byte > CBU8_MAX_LENGTH)
149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return read_buffer_size_;
150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // If there are trailing characters, there must be a leading one in the
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // buffer for a valid UTF8 character. Getting past the buffer begining
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // signals something's wrong, or the buffer is empty. In both cases return
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // the whole current buffer.
155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (last_lead_byte < 0)
156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return read_buffer_size_;
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // Found the starting character byte; stop searching.
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (!CBU8_IS_TRAIL(read_buffer_[last_lead_byte]))
160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    --last_lead_byte;
163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  size_t last_length = UTF8SizeFromLeadingByte(read_buffer_[last_lead_byte]);
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Note that if |last_length| == 0 or
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // |last_length| + |last_read_byte| < |read_buffer_size_|, the string is
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // invalid UTF8. In that case, send the whole read buffer to the observer
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // immediately, just as if there is no trailing incomplete UTF8 bytes.
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!last_length || last_length + last_lead_byte <= read_buffer_size_)
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return read_buffer_size_;
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return last_lead_byte;
175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ProcessOutputWatcher::ReportOutput(ProcessOutputType type,
178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                        size_t new_bytes_count) {
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  read_buffer_size_ += new_bytes_count;
180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  size_t output_to_report = OutputSizeWithoutIncompleteUTF8();
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  on_read_callback_.Run(type, std::string(read_buffer_, output_to_report));
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Move the bytes that were left behind to the beginning of the buffer and
185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // update the buffer size accordingly.
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (output_to_report < read_buffer_size_) {
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    for (size_t i = output_to_report; i < read_buffer_size_; ++i) {
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      read_buffer_[i - output_to_report] = read_buffer_[i];
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  read_buffer_size_ -= output_to_report;
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProcessOutputWatcher::OnStop() {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete this;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace chromeos
199