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_proxy.h"
6
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <sys/ioctl.h>
10
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/process/kill.h"
17 #include "base/process/launch.h"
18 #include "base/threading/thread.h"
19 #include "chromeos/process_proxy/process_output_watcher.h"
20
21 namespace {
22
23 enum PipeEnd {
24 PIPE_END_READ,
25 PIPE_END_WRITE
26 };
27
28 enum PseudoTerminalFd {
29 PT_MASTER_FD,
30 PT_SLAVE_FD
31 };
32
33 const int kInvalidFd = -1;
34
35 } // namespace
36
37 namespace chromeos {
38
ProcessProxy()39 ProcessProxy::ProcessProxy(): process_launched_(false),
40 callback_set_(false),
41 watcher_started_(false) {
42 // Set pipes to initial, invalid value so we can easily know if a pipe was
43 // opened by us.
44 ClearAllFdPairs();
45 };
46
Open(const std::string & command,pid_t * pid)47 bool ProcessProxy::Open(const std::string& command, pid_t* pid) {
48 if (process_launched_)
49 return false;
50
51 if (!CreatePseudoTerminalPair(pt_pair_)) {
52 return false;
53 }
54
55 process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_);
56
57 if (process_launched_) {
58 // We won't need these anymore. These will be used by the launched process.
59 CloseFd(&pt_pair_[PT_SLAVE_FD]);
60 *pid = pid_;
61 LOG(WARNING) << "Process launched: " << pid_;
62 } else {
63 CloseFdPair(pt_pair_);
64 }
65 return process_launched_;
66 }
67
StartWatchingOnThread(base::Thread * watch_thread,const ProcessOutputCallback & callback)68 bool ProcessProxy::StartWatchingOnThread(
69 base::Thread* watch_thread,
70 const ProcessOutputCallback& callback) {
71 DCHECK(process_launched_);
72 if (watcher_started_)
73 return false;
74 if (pipe(shutdown_pipe_))
75 return false;
76
77 // We give ProcessOutputWatcher a copy of master to make life easier during
78 // tear down.
79 // TODO(tbarzic): improve fd managment.
80 int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD]));
81 if (master_copy == -1)
82 return false;
83
84 callback_set_ = true;
85 callback_ = callback;
86 callback_runner_ = base::MessageLoopProxy::current();
87
88 // This object will delete itself once watching is stopped.
89 // It also takes ownership of the passed fds.
90 ProcessOutputWatcher* output_watcher =
91 new ProcessOutputWatcher(master_copy,
92 shutdown_pipe_[PIPE_END_READ],
93 base::Bind(&ProcessProxy::OnProcessOutput,
94 this));
95
96 // Output watcher took ownership of the read end of shutdown pipe.
97 shutdown_pipe_[PIPE_END_READ] = -1;
98
99 // |watch| thread is blocked by |output_watcher| from now on.
100 watch_thread->message_loop()->PostTask(FROM_HERE,
101 base::Bind(&ProcessOutputWatcher::Start,
102 base::Unretained(output_watcher)));
103 watcher_started_ = true;
104 return true;
105 }
106
OnProcessOutput(ProcessOutputType type,const std::string & output)107 void ProcessProxy::OnProcessOutput(ProcessOutputType type,
108 const std::string& output) {
109 if (!callback_runner_.get())
110 return;
111
112 callback_runner_->PostTask(
113 FROM_HERE,
114 base::Bind(&ProcessProxy::CallOnProcessOutputCallback,
115 this, type, output));
116 }
117
CallOnProcessOutputCallback(ProcessOutputType type,const std::string & output)118 void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type,
119 const std::string& output) {
120 // We may receive some output even after Close was called (crosh process does
121 // not have to quit instantly, or there may be some trailing data left in
122 // output stream fds). In that case owner of the callback may be gone so we
123 // don't want to send it anything. |callback_set_| is reset when this gets
124 // closed.
125 if (callback_set_)
126 callback_.Run(type, output);
127 }
128
StopWatching()129 bool ProcessProxy::StopWatching() {
130 if (!watcher_started_)
131 return true;
132 // Signal Watcher that we are done. We use self-pipe trick to unblock watcher.
133 // Anything may be written to the pipe.
134 const char message[] = "q";
135 return file_util::WriteFileDescriptor(shutdown_pipe_[PIPE_END_WRITE],
136 message, sizeof(message));
137 }
138
Close()139 void ProcessProxy::Close() {
140 if (!process_launched_)
141 return;
142
143 process_launched_ = false;
144 callback_set_ = false;
145 callback_ = ProcessOutputCallback();
146 callback_runner_ = NULL;
147
148 base::KillProcess(pid_, 0, true /* wait */);
149
150 // TODO(tbarzic): What if this fails?
151 StopWatching();
152
153 CloseAllFdPairs();
154 }
155
Write(const std::string & text)156 bool ProcessProxy::Write(const std::string& text) {
157 if (!process_launched_)
158 return false;
159
160 // We don't want to write '\0' to the pipe.
161 size_t data_size = text.length() * sizeof(*text.c_str());
162 int bytes_written =
163 file_util::WriteFileDescriptor(pt_pair_[PT_MASTER_FD],
164 text.c_str(), data_size);
165 return (bytes_written == static_cast<int>(data_size));
166 }
167
OnTerminalResize(int width,int height)168 bool ProcessProxy::OnTerminalResize(int width, int height) {
169 if (width < 0 || height < 0)
170 return false;
171
172 winsize ws;
173 // Number of rows.
174 ws.ws_row = height;
175 // Number of columns.
176 ws.ws_col = width;
177
178 return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1);
179 }
180
~ProcessProxy()181 ProcessProxy::~ProcessProxy() {
182 // In case watcher did not started, we may get deleted without calling Close.
183 // In that case we have to clean up created pipes. If watcher had been
184 // started, there will be a callback with our reference owned by
185 // process_output_watcher until Close is called, so we know Close has been
186 // called by now (and pipes have been cleaned).
187 if (!watcher_started_)
188 CloseAllFdPairs();
189 }
190
CreatePseudoTerminalPair(int * pt_pair)191 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) {
192 ClearFdPair(pt_pair);
193
194 // Open Master.
195 pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY));
196 if (pt_pair[PT_MASTER_FD] == -1)
197 return false;
198
199 if (grantpt(pt_pair_[PT_MASTER_FD]) != 0 ||
200 unlockpt(pt_pair_[PT_MASTER_FD]) != 0) {
201 CloseFd(&pt_pair[PT_MASTER_FD]);
202 return false;
203 }
204 char* slave_name = NULL;
205 // Per man page, slave_name must not be freed.
206 slave_name = ptsname(pt_pair_[PT_MASTER_FD]);
207 if (slave_name)
208 pt_pair_[PT_SLAVE_FD] = HANDLE_EINTR(open(slave_name, O_RDWR | O_NOCTTY));
209
210 if (pt_pair_[PT_SLAVE_FD] == -1) {
211 CloseFdPair(pt_pair);
212 return false;
213 }
214
215 return true;
216 }
217
LaunchProcess(const std::string & command,int slave_fd,pid_t * pid)218 bool ProcessProxy::LaunchProcess(const std::string& command, int slave_fd,
219 pid_t* pid) {
220 // Redirect crosh process' output and input so we can read it.
221 base::FileHandleMappingVector fds_mapping;
222 fds_mapping.push_back(std::make_pair(slave_fd, STDIN_FILENO));
223 fds_mapping.push_back(std::make_pair(slave_fd, STDOUT_FILENO));
224 fds_mapping.push_back(std::make_pair(slave_fd, STDERR_FILENO));
225 base::LaunchOptions options;
226 options.fds_to_remap = &fds_mapping;
227 options.ctrl_terminal_fd = slave_fd;
228 options.environ["TERM"] = "xterm";
229
230 // Launch the process.
231 return base::LaunchProcess(CommandLine(base::FilePath(command)), options,
232 pid);
233 }
234
CloseAllFdPairs()235 void ProcessProxy::CloseAllFdPairs() {
236 CloseFdPair(pt_pair_);
237 CloseFdPair(shutdown_pipe_);
238 }
239
CloseFdPair(int * pipe)240 void ProcessProxy::CloseFdPair(int* pipe) {
241 CloseFd(&(pipe[PIPE_END_READ]));
242 CloseFd(&(pipe[PIPE_END_WRITE]));
243 }
244
CloseFd(int * fd)245 void ProcessProxy::CloseFd(int* fd) {
246 if (*fd != kInvalidFd) {
247 if (IGNORE_EINTR(close(*fd)) != 0)
248 DPLOG(WARNING) << "close fd failed.";
249 }
250 *fd = kInvalidFd;
251 }
252
ClearAllFdPairs()253 void ProcessProxy::ClearAllFdPairs() {
254 ClearFdPair(pt_pair_);
255 ClearFdPair(shutdown_pipe_);
256 }
257
ClearFdPair(int * pipe)258 void ProcessProxy::ClearFdPair(int* pipe) {
259 pipe[PIPE_END_READ] = kInvalidFd;
260 pipe[PIPE_END_WRITE] = kInvalidFd;
261 }
262
263 } // namespace chromeos
264