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