OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/process_proxy/process_output_watcher.h" | 5 #include "chromeos/process_proxy/process_output_watcher.h" |
6 | 6 |
| 7 #include <sys/ioctl.h> |
| 8 #include <sys/select.h> |
| 9 #include <unistd.h> |
| 10 |
7 #include <algorithm> | 11 #include <algorithm> |
8 #include <cstdio> | 12 #include <cstdio> |
9 #include <cstring> | 13 #include <cstring> |
10 | 14 |
11 #include <sys/ioctl.h> | |
12 #include <sys/select.h> | |
13 #include <unistd.h> | |
14 | |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/posix/eintr_wrapper.h" | 16 #include "base/posix/eintr_wrapper.h" |
| 17 #include "base/third_party/icu/icu_utf.h" |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
20 void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) { | 21 void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) { |
21 FD_ZERO(set); | 22 FD_ZERO(set); |
22 if (out_fd != -1) | 23 if (out_fd != -1) |
23 FD_SET(out_fd, set); | 24 FD_SET(out_fd, set); |
24 FD_SET(stop_fd, set); | 25 FD_SET(stop_fd, set); |
25 } | 26 } |
26 | 27 |
27 void CloseFd(int* fd) { | 28 void CloseFd(int* fd) { |
28 if (*fd >= 0) { | 29 if (*fd >= 0) { |
29 if (IGNORE_EINTR(close(*fd)) != 0) | 30 if (IGNORE_EINTR(close(*fd)) != 0) |
30 DPLOG(WARNING) << "close fd " << *fd << " failed."; | 31 DPLOG(WARNING) << "close fd " << *fd << " failed."; |
31 } | 32 } |
32 *fd = -1; | 33 *fd = -1; |
33 } | 34 } |
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 |
35 } // namespace | 54 } // namespace |
36 | 55 |
37 namespace chromeos { | 56 namespace chromeos { |
38 | 57 |
39 ProcessOutputWatcher::ProcessOutputWatcher(int out_fd, int stop_fd, | 58 ProcessOutputWatcher::ProcessOutputWatcher( |
| 59 int out_fd, |
| 60 int stop_fd, |
40 const ProcessOutputCallback& callback) | 61 const ProcessOutputCallback& callback) |
41 : out_fd_(out_fd), | 62 : read_buffer_size_(0), |
| 63 out_fd_(out_fd), |
42 stop_fd_(stop_fd), | 64 stop_fd_(stop_fd), |
43 on_read_callback_(callback) { | 65 on_read_callback_(callback) { |
44 VerifyFileDescriptor(out_fd_); | 66 VerifyFileDescriptor(out_fd_); |
45 VerifyFileDescriptor(stop_fd_); | 67 VerifyFileDescriptor(stop_fd_); |
46 max_fd_ = std::max(out_fd_, stop_fd_); | 68 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. | 69 // 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; | 70 read_buffer_capacity_ = arraysize(read_buffer_) - 1; |
49 } | 71 } |
50 | 72 |
51 void ProcessOutputWatcher::Start() { | 73 void ProcessOutputWatcher::Start() { |
52 WatchProcessOutput(); | 74 WatchProcessOutput(); |
53 OnStop(); | 75 OnStop(); |
54 } | 76 } |
55 | 77 |
56 ProcessOutputWatcher::~ProcessOutputWatcher() { | 78 ProcessOutputWatcher::~ProcessOutputWatcher() { |
57 CloseFd(&out_fd_); | 79 CloseFd(&out_fd_); |
58 CloseFd(&stop_fd_); | 80 CloseFd(&stop_fd_); |
59 } | 81 } |
60 | 82 |
61 void ProcessOutputWatcher::WatchProcessOutput() { | 83 void ProcessOutputWatcher::WatchProcessOutput() { |
62 while (true) { | 84 while (true) { |
63 // This has to be reset with every watch cycle. | 85 // This has to be reset with every watch cycle. |
64 fd_set rfds; | 86 fd_set rfds; |
65 DCHECK(stop_fd_ >= 0); | 87 DCHECK_GE(stop_fd_, 0); |
66 InitReadFdSet(out_fd_, stop_fd_, &rfds); | 88 InitReadFdSet(out_fd_, stop_fd_, &rfds); |
67 | 89 |
68 int select_result = | 90 int select_result = |
69 HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL)); | 91 HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL)); |
70 | 92 |
71 if (select_result < 0) { | 93 if (select_result < 0) { |
72 DPLOG(WARNING) << "select failed"; | 94 DPLOG(WARNING) << "select failed"; |
73 return; | 95 return; |
74 } | 96 } |
75 | 97 |
(...skipping 11 matching lines...) Expand all Loading... |
87 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) { | 109 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) { |
88 CHECK_LE(0, fd); | 110 CHECK_LE(0, fd); |
89 CHECK_GT(FD_SETSIZE, fd); | 111 CHECK_GT(FD_SETSIZE, fd); |
90 } | 112 } |
91 | 113 |
92 void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) { | 114 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 | 115 // 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 | 116 // 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 | 117 // more than read_buffer_size_ bytes in pipe, it will be read in the next |
96 // iteration. | 118 // iteration. |
97 ssize_t bytes_read = HANDLE_EINTR(read(*fd, read_buffer_, read_buffer_size_)); | 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_)); |
98 if (bytes_read < 0) | 124 if (bytes_read < 0) |
99 DPLOG(WARNING) << "read from buffer failed"; | 125 DPLOG(WARNING) << "read from buffer failed"; |
100 | 126 |
101 if (bytes_read > 0) { | 127 if (bytes_read > 0) |
102 on_read_callback_.Run(type, std::string(read_buffer_, bytes_read)); | 128 ReportOutput(type, bytes_read); |
103 } | |
104 | 129 |
105 // If there is nothing on the output the watched process has exited (slave end | 130 // If there is nothing on the output the watched process has exited (slave end |
106 // of pty is closed). | 131 // of pty is closed). |
107 if (bytes_read <= 0) { | 132 if (bytes_read <= 0) { |
108 // Slave pseudo terminal has been closed, we won't need master fd anymore. | 133 // Slave pseudo terminal has been closed, we won't need master fd anymore. |
109 CloseFd(fd); | 134 CloseFd(fd); |
110 | 135 |
111 // We have lost contact with the process, so report it. | 136 // We have lost contact with the process, so report it. |
112 on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, ""); | 137 on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, ""); |
113 } | 138 } |
114 } | 139 } |
115 | 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 |
116 void ProcessOutputWatcher::OnStop() { | 194 void ProcessOutputWatcher::OnStop() { |
117 delete this; | 195 delete this; |
118 } | 196 } |
119 | 197 |
120 } // namespace chromeos | 198 } // namespace chromeos |
OLD | NEW |