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