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