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