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