OLD | NEW |
| (Empty) |
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 "chrome/browser/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 (HANDLE_EINTR(close(*fd)) != 0) | |
30 DPLOG(WARNING) << "close fd " << *fd << " failed."; | |
31 } | |
32 *fd = -1; | |
33 } | |
34 | |
35 } // namespace | |
36 | |
37 ProcessOutputWatcher::ProcessOutputWatcher(int out_fd, int stop_fd, | |
38 const ProcessOutputCallback& callback) | |
39 : out_fd_(out_fd), | |
40 stop_fd_(stop_fd), | |
41 on_read_callback_(callback) { | |
42 VerifyFileDescriptor(out_fd_); | |
43 VerifyFileDescriptor(stop_fd_); | |
44 max_fd_ = std::max(out_fd_, stop_fd_); | |
45 // We want to be sure we will be able to add 0 at the end of the input, so -1. | |
46 read_buffer_size_ = arraysize(read_buffer_) - 1; | |
47 } | |
48 | |
49 void ProcessOutputWatcher::Start() { | |
50 WatchProcessOutput(); | |
51 OnStop(); | |
52 } | |
53 | |
54 ProcessOutputWatcher::~ProcessOutputWatcher() { | |
55 CloseFd(&out_fd_); | |
56 CloseFd(&stop_fd_); | |
57 } | |
58 | |
59 void ProcessOutputWatcher::WatchProcessOutput() { | |
60 while (true) { | |
61 // This has to be reset with every watch cycle. | |
62 fd_set rfds; | |
63 DCHECK(stop_fd_ >= 0); | |
64 InitReadFdSet(out_fd_, stop_fd_, &rfds); | |
65 | |
66 int select_result = | |
67 HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL)); | |
68 | |
69 if (select_result < 0) { | |
70 DPLOG(WARNING) << "select failed"; | |
71 return; | |
72 } | |
73 | |
74 // Check if we were stopped. | |
75 if (FD_ISSET(stop_fd_, &rfds)) { | |
76 return; | |
77 } | |
78 | |
79 if (out_fd_ != -1 && FD_ISSET(out_fd_, &rfds)) { | |
80 ReadFromFd(PROCESS_OUTPUT_TYPE_OUT, &out_fd_); | |
81 } | |
82 } | |
83 } | |
84 | |
85 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) { | |
86 CHECK_LE(0, fd); | |
87 CHECK_GT(FD_SETSIZE, fd); | |
88 } | |
89 | |
90 void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) { | |
91 // We don't want to necessary read pipe until it is empty so we don't starve | |
92 // other streams in case data is written faster than we read it. If there is | |
93 // more than read_buffer_size_ bytes in pipe, it will be read in the next | |
94 // iteration. | |
95 ssize_t bytes_read = HANDLE_EINTR(read(*fd, read_buffer_, read_buffer_size_)); | |
96 if (bytes_read < 0) | |
97 DPLOG(WARNING) << "read from buffer failed"; | |
98 | |
99 if (bytes_read > 0) { | |
100 on_read_callback_.Run(type, std::string(read_buffer_, bytes_read)); | |
101 } | |
102 | |
103 // If there is nothing on the output the watched process has exited (slave end | |
104 // of pty is closed). | |
105 if (bytes_read <= 0) { | |
106 // Slave pseudo terminal has been closed, we won't need master fd anymore. | |
107 CloseFd(fd); | |
108 | |
109 // We have lost contact with the process, so report it. | |
110 on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, ""); | |
111 } | |
112 } | |
113 | |
114 void ProcessOutputWatcher::OnStop() { | |
115 delete this; | |
116 } | |
OLD | NEW |