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.
5 #include "chromeos/process_proxy/process_output_watcher.h"
8 #include <sys/select.h>
15 #include "base/logging.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/third_party/icu/icu_utf.h"
21 void InitReadFdSet(int out_fd
, int stop_fd
, fd_set
* set
) {
28 void CloseFd(int* fd
) {
30 if (IGNORE_EINTR(close(*fd
)) != 0)
31 DPLOG(WARNING
) << "close fd " << *fd
<< " failed.";
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;
44 uint8 error_mask
= 1 << (7 - CBU8_MAX_LENGTH
);
45 while (leading_byte
& mask
) {
46 if (mask
& error_mask
)
51 return byte_count
? byte_count
: 1;
58 ProcessOutputWatcher::ProcessOutputWatcher(
61 const ProcessOutputCallback
& callback
)
62 : read_buffer_size_(0),
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;
73 void ProcessOutputWatcher::Start() {
78 ProcessOutputWatcher::~ProcessOutputWatcher() {
83 void ProcessOutputWatcher::WatchProcessOutput() {
85 // This has to be reset with every watch cycle.
87 DCHECK_GE(stop_fd_
, 0);
88 InitReadFdSet(out_fd_
, stop_fd_
, &rfds
);
91 HANDLE_EINTR(select(max_fd_
+ 1, &rfds
, NULL
, NULL
, NULL
));
93 if (select_result
< 0) {
94 DPLOG(WARNING
) << "select failed";
98 // Check if we were stopped.
99 if (FD_ISSET(stop_fd_
, &rfds
)) {
103 if (out_fd_
!= -1 && FD_ISSET(out_fd_
, &rfds
)) {
104 ReadFromFd(PROCESS_OUTPUT_TYPE_OUT
, &out_fd_
);
109 void ProcessOutputWatcher::VerifyFileDescriptor(int fd
) {
111 CHECK_GT(FD_SETSIZE
, fd
);
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
119 DCHECK_GT(read_buffer_capacity_
, read_buffer_size_
);
121 HANDLE_EINTR(read(*fd
,
122 &read_buffer_
[read_buffer_size_
],
123 read_buffer_capacity_
- read_buffer_size_
));
125 DPLOG(WARNING
) << "read from buffer failed";
128 ReportOutput(type
, bytes_read
);
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.
136 // We have lost contact with the process, so report it.
137 on_read_callback_
.Run(PROCESS_OUTPUT_TYPE_EXIT
, "");
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;
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_
;
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_
;
158 // Found the starting character byte; stop searching.
159 if (!CBU8_IS_TRAIL(read_buffer_
[last_lead_byte
]))
165 size_t last_length
= UTF8SizeFromLeadingByte(read_buffer_
[last_lead_byte
]);
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_
;
174 return last_lead_byte
;
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();
182 on_read_callback_
.Run(type
, std::string(read_buffer_
, output_to_report
));
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
];
191 read_buffer_size_
-= output_to_report
;
194 void ProcessOutputWatcher::OnStop() {
198 } // namespace chromeos