- add sources.
[platform/framework/web/crosswalk.git] / src / chromeos / process_proxy / process_output_watcher.cc
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 (HANDLE_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