| OLD | NEW |
| (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 } | |
| OLD | NEW |