Update V8 to version 4.7.52.
[chromium-blink-merge.git] / chromeos / process_proxy / process_proxy.cc
blob8ec9cebabcf991584ac03ad668bfc210b604a19e
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/location.h"
14 #include "base/logging.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/process/launch.h"
17 #include "base/process/process.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "third_party/cros_system_api/switches/chrome_switches.h"
22 namespace {
24 enum PseudoTerminalFd {
25 PT_MASTER_FD,
26 PT_SLAVE_FD
29 const int kInvalidFd = -1;
31 void StopOutputWatcher(scoped_ptr<chromeos::ProcessOutputWatcher> watcher) {
32 // Just deleting |watcher| if sufficient to stop it.
35 } // namespace
37 namespace chromeos {
39 ProcessProxy::ProcessProxy() : process_launched_(false), callback_set_(false) {
40 // Set pipes to initial, invalid value so we can easily know if a pipe was
41 // opened by us.
42 ClearFdPair(pt_pair_);
45 bool ProcessProxy::Open(const std::string& command, pid_t* pid) {
46 if (process_launched_)
47 return false;
49 if (!CreatePseudoTerminalPair(pt_pair_)) {
50 return false;
53 process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_);
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_);
63 return process_launched_;
66 bool ProcessProxy::StartWatchingOutput(
67 const scoped_refptr<base::SingleThreadTaskRunner>& watcher_runner,
68 const ProcessOutputCallback& callback) {
69 DCHECK(process_launched_);
70 CHECK(!output_watcher_.get());
72 // We give ProcessOutputWatcher a copy of master to make life easier during
73 // tear down.
74 // TODO(tbarzic): improve fd managment.
75 int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD]));
76 if (master_copy < 0)
77 return false;
79 callback_set_ = true;
80 callback_ = callback;
81 callback_runner_ = base::ThreadTaskRunnerHandle::Get();
82 watcher_runner_ = watcher_runner;
84 // This object will delete itself once watching is stopped.
85 // It also takes ownership of the passed fds.
86 output_watcher_.reset(new ProcessOutputWatcher(
87 master_copy, base::Bind(&ProcessProxy::OnProcessOutput, this)));
89 watcher_runner_->PostTask(
90 FROM_HERE, base::Bind(&ProcessOutputWatcher::Start,
91 base::Unretained(output_watcher_.get())));
93 return true;
96 void ProcessProxy::OnProcessOutput(ProcessOutputType type,
97 const std::string& output) {
98 if (!callback_runner_.get())
99 return;
101 callback_runner_->PostTask(
102 FROM_HERE,
103 base::Bind(&ProcessProxy::CallOnProcessOutputCallback,
104 this, type, output));
107 void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type,
108 const std::string& output) {
109 // We may receive some output even after Close was called (crosh process does
110 // not have to quit instantly, or there may be some trailing data left in
111 // output stream fds). In that case owner of the callback may be gone so we
112 // don't want to send it anything. |callback_set_| is reset when this gets
113 // closed.
114 if (callback_set_)
115 callback_.Run(type, output);
118 void ProcessProxy::StopWatching() {
119 if (!output_watcher_.get())
120 return;
122 watcher_runner_->PostTask(
123 FROM_HERE,
124 base::Bind(&StopOutputWatcher, base::Passed(&output_watcher_)));
127 void ProcessProxy::Close() {
128 if (!process_launched_)
129 return;
131 process_launched_ = false;
132 callback_set_ = false;
133 callback_ = ProcessOutputCallback();
134 callback_runner_ = NULL;
136 base::Process process = base::Process::DeprecatedGetProcessFromHandle(pid_);
137 process.Terminate(0, true /* wait */);
139 StopWatching();
140 CloseFdPair(pt_pair_);
143 bool ProcessProxy::Write(const std::string& text) {
144 if (!process_launched_)
145 return false;
147 // We don't want to write '\0' to the pipe.
148 size_t data_size = text.length() * sizeof(*text.c_str());
149 return base::WriteFileDescriptor(
150 pt_pair_[PT_MASTER_FD], text.c_str(), data_size);
153 bool ProcessProxy::OnTerminalResize(int width, int height) {
154 if (width < 0 || height < 0)
155 return false;
157 winsize ws;
158 // Number of rows.
159 ws.ws_row = height;
160 // Number of columns.
161 ws.ws_col = width;
163 return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1);
166 ProcessProxy::~ProcessProxy() {
167 Close();
170 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) {
171 ClearFdPair(pt_pair);
173 // Open Master.
174 pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY));
175 if (pt_pair[PT_MASTER_FD] == -1)
176 return false;
178 if (grantpt(pt_pair_[PT_MASTER_FD]) != 0 ||
179 unlockpt(pt_pair_[PT_MASTER_FD]) != 0) {
180 CloseFd(&pt_pair[PT_MASTER_FD]);
181 return false;
183 char* slave_name = NULL;
184 // Per man page, slave_name must not be freed.
185 slave_name = ptsname(pt_pair_[PT_MASTER_FD]);
186 if (slave_name)
187 pt_pair_[PT_SLAVE_FD] = HANDLE_EINTR(open(slave_name, O_RDWR | O_NOCTTY));
189 if (pt_pair_[PT_SLAVE_FD] == -1) {
190 CloseFdPair(pt_pair);
191 return false;
194 return true;
197 bool ProcessProxy::LaunchProcess(const std::string& command, int slave_fd,
198 pid_t* pid) {
199 // Redirect crosh process' output and input so we can read it.
200 base::FileHandleMappingVector fds_mapping;
201 fds_mapping.push_back(std::make_pair(slave_fd, STDIN_FILENO));
202 fds_mapping.push_back(std::make_pair(slave_fd, STDOUT_FILENO));
203 fds_mapping.push_back(std::make_pair(slave_fd, STDERR_FILENO));
204 base::LaunchOptions options;
205 // Do not set NO_NEW_PRIVS on processes if the system is in dev-mode. This
206 // permits sudo in the crosh shell when in developer mode.
207 options.allow_new_privs = base::CommandLine::ForCurrentProcess()->
208 HasSwitch(chromeos::switches::kSystemInDevMode);
209 options.fds_to_remap = &fds_mapping;
210 options.ctrl_terminal_fd = slave_fd;
211 options.environ["TERM"] = "xterm";
213 // Launch the process.
214 base::Process process =
215 base::LaunchProcess(base::CommandLine(base::FilePath(command)), options);
217 // TODO(rvargas) crbug/417532: This is somewhat wrong but the interface of
218 // Open vends pid_t* so ownership is quite vague anyway, and Process::Close
219 // doesn't do much in POSIX.
220 *pid = process.Pid();
221 return process.IsValid();
224 void ProcessProxy::CloseFdPair(int* pipe) {
225 CloseFd(&(pipe[PT_MASTER_FD]));
226 CloseFd(&(pipe[PT_SLAVE_FD]));
229 void ProcessProxy::CloseFd(int* fd) {
230 if (*fd != kInvalidFd) {
231 if (IGNORE_EINTR(close(*fd)) != 0)
232 DPLOG(WARNING) << "close fd failed.";
234 *fd = kInvalidFd;
237 void ProcessProxy::ClearFdPair(int* pipe) {
238 pipe[PT_MASTER_FD] = kInvalidFd;
239 pipe[PT_SLAVE_FD] = kInvalidFd;
242 } // namespace chromeos