Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: chrome/browser/chromeos/process_proxy/process_proxy.cc

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

Powered by Google App Engine
This is Rietveld 408576698