| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/process_proxy/process_proxy.h" | 5 #include "chromeos/process_proxy/process_proxy.h" |
| 6 | 6 |
| 7 #include <stdlib.h> | 7 #include <stdlib.h> |
| 8 #include <sys/ioctl.h> | 8 #include <sys/ioctl.h> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/posix/eintr_wrapper.h" | 15 #include "base/posix/eintr_wrapper.h" |
| 16 #include "base/process/launch.h" | 16 #include "base/process/launch.h" |
| 17 #include "base/process/process.h" | 17 #include "base/process/process.h" |
| 18 #include "base/single_thread_task_runner.h" | 18 #include "base/single_thread_task_runner.h" |
| 19 #include "base/thread_task_runner_handle.h" | 19 #include "base/thread_task_runner_handle.h" |
| 20 #include "base/threading/thread.h" | |
| 21 #include "third_party/cros_system_api/switches/chrome_switches.h" | 20 #include "third_party/cros_system_api/switches/chrome_switches.h" |
| 22 | 21 |
| 23 namespace { | 22 namespace { |
| 24 | 23 |
| 25 enum PipeEnd { | |
| 26 PIPE_END_READ, | |
| 27 PIPE_END_WRITE | |
| 28 }; | |
| 29 | |
| 30 enum PseudoTerminalFd { | 24 enum PseudoTerminalFd { |
| 31 PT_MASTER_FD, | 25 PT_MASTER_FD, |
| 32 PT_SLAVE_FD | 26 PT_SLAVE_FD |
| 33 }; | 27 }; |
| 34 | 28 |
| 35 const int kInvalidFd = -1; | 29 const int kInvalidFd = -1; |
| 36 | 30 |
| 31 void StopOutputWatcher(scoped_ptr<chromeos::ProcessOutputWatcher> watcher) { |
| 32 // Just deleting |watcher| if sufficient to stop it. |
| 33 } |
| 34 |
| 37 } // namespace | 35 } // namespace |
| 38 | 36 |
| 39 namespace chromeos { | 37 namespace chromeos { |
| 40 | 38 |
| 41 ProcessProxy::ProcessProxy(): process_launched_(false), | 39 ProcessProxy::ProcessProxy() : process_launched_(false), callback_set_(false) { |
| 42 callback_set_(false), | |
| 43 watcher_started_(false) { | |
| 44 // Set pipes to initial, invalid value so we can easily know if a pipe was | 40 // Set pipes to initial, invalid value so we can easily know if a pipe was |
| 45 // opened by us. | 41 // opened by us. |
| 46 ClearAllFdPairs(); | 42 ClearFdPair(pt_pair_); |
| 47 } | 43 } |
| 48 | 44 |
| 49 bool ProcessProxy::Open(const std::string& command, pid_t* pid) { | 45 bool ProcessProxy::Open(const std::string& command, pid_t* pid) { |
| 50 if (process_launched_) | 46 if (process_launched_) |
| 51 return false; | 47 return false; |
| 52 | 48 |
| 53 if (!CreatePseudoTerminalPair(pt_pair_)) { | 49 if (!CreatePseudoTerminalPair(pt_pair_)) { |
| 54 return false; | 50 return false; |
| 55 } | 51 } |
| 56 | 52 |
| 57 process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_); | 53 process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_); |
| 58 | 54 |
| 59 if (process_launched_) { | 55 if (process_launched_) { |
| 60 // We won't need these anymore. These will be used by the launched process. | 56 // We won't need these anymore. These will be used by the launched process. |
| 61 CloseFd(&pt_pair_[PT_SLAVE_FD]); | 57 CloseFd(&pt_pair_[PT_SLAVE_FD]); |
| 62 *pid = pid_; | 58 *pid = pid_; |
| 63 LOG(WARNING) << "Process launched: " << pid_; | 59 LOG(WARNING) << "Process launched: " << pid_; |
| 64 } else { | 60 } else { |
| 65 CloseFdPair(pt_pair_); | 61 CloseFdPair(pt_pair_); |
| 66 } | 62 } |
| 67 return process_launched_; | 63 return process_launched_; |
| 68 } | 64 } |
| 69 | 65 |
| 70 bool ProcessProxy::StartWatchingOnThread( | 66 bool ProcessProxy::StartWatchingOutput( |
| 71 base::Thread* watch_thread, | 67 const scoped_refptr<base::SingleThreadTaskRunner>& watcher_runner, |
| 72 const ProcessOutputCallback& callback) { | 68 const ProcessOutputCallback& callback) { |
| 73 DCHECK(process_launched_); | 69 DCHECK(process_launched_); |
| 74 if (watcher_started_) | 70 CHECK(!output_watcher_.get()); |
| 75 return false; | |
| 76 if (pipe(shutdown_pipe_) || | |
| 77 !ProcessOutputWatcher::VerifyFileDescriptor( | |
| 78 shutdown_pipe_[PIPE_END_READ])) { | |
| 79 return false; | |
| 80 } | |
| 81 | 71 |
| 82 // We give ProcessOutputWatcher a copy of master to make life easier during | 72 // We give ProcessOutputWatcher a copy of master to make life easier during |
| 83 // tear down. | 73 // tear down. |
| 84 // TODO(tbarzic): improve fd managment. | 74 // TODO(tbarzic): improve fd managment. |
| 85 int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD])); | 75 int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD])); |
| 86 if (!ProcessOutputWatcher::VerifyFileDescriptor(master_copy)) | 76 if (master_copy < 0) |
| 87 return false; | 77 return false; |
| 88 | 78 |
| 89 callback_set_ = true; | 79 callback_set_ = true; |
| 90 callback_ = callback; | 80 callback_ = callback; |
| 91 callback_runner_ = base::ThreadTaskRunnerHandle::Get(); | 81 callback_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 82 watcher_runner_ = watcher_runner; |
| 92 | 83 |
| 93 // This object will delete itself once watching is stopped. | 84 // This object will delete itself once watching is stopped. |
| 94 // It also takes ownership of the passed fds. | 85 // It also takes ownership of the passed fds. |
| 95 ProcessOutputWatcher* output_watcher = | 86 output_watcher_.reset(new ProcessOutputWatcher( |
| 96 new ProcessOutputWatcher(master_copy, | 87 master_copy, base::Bind(&ProcessProxy::OnProcessOutput, this))); |
| 97 shutdown_pipe_[PIPE_END_READ], | |
| 98 base::Bind(&ProcessProxy::OnProcessOutput, | |
| 99 this)); | |
| 100 | 88 |
| 101 // Output watcher took ownership of the read end of shutdown pipe. | 89 watcher_runner_->PostTask( |
| 102 shutdown_pipe_[PIPE_END_READ] = -1; | 90 FROM_HERE, base::Bind(&ProcessOutputWatcher::Start, |
| 91 base::Unretained(output_watcher_.get()))); |
| 103 | 92 |
| 104 // |watch| thread is blocked by |output_watcher| from now on. | |
| 105 watch_thread->task_runner()->PostTask( | |
| 106 FROM_HERE, base::Bind(&ProcessOutputWatcher::Start, | |
| 107 base::Unretained(output_watcher))); | |
| 108 watcher_started_ = true; | |
| 109 return true; | 93 return true; |
| 110 } | 94 } |
| 111 | 95 |
| 112 void ProcessProxy::OnProcessOutput(ProcessOutputType type, | 96 void ProcessProxy::OnProcessOutput(ProcessOutputType type, |
| 113 const std::string& output) { | 97 const std::string& output) { |
| 114 if (!callback_runner_.get()) | 98 if (!callback_runner_.get()) |
| 115 return; | 99 return; |
| 116 | 100 |
| 117 callback_runner_->PostTask( | 101 callback_runner_->PostTask( |
| 118 FROM_HERE, | 102 FROM_HERE, |
| 119 base::Bind(&ProcessProxy::CallOnProcessOutputCallback, | 103 base::Bind(&ProcessProxy::CallOnProcessOutputCallback, |
| 120 this, type, output)); | 104 this, type, output)); |
| 121 } | 105 } |
| 122 | 106 |
| 123 void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type, | 107 void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type, |
| 124 const std::string& output) { | 108 const std::string& output) { |
| 125 // We may receive some output even after Close was called (crosh process does | 109 // We may receive some output even after Close was called (crosh process does |
| 126 // not have to quit instantly, or there may be some trailing data left in | 110 // not have to quit instantly, or there may be some trailing data left in |
| 127 // output stream fds). In that case owner of the callback may be gone so we | 111 // output stream fds). In that case owner of the callback may be gone so we |
| 128 // don't want to send it anything. |callback_set_| is reset when this gets | 112 // don't want to send it anything. |callback_set_| is reset when this gets |
| 129 // closed. | 113 // closed. |
| 130 if (callback_set_) | 114 if (callback_set_) |
| 131 callback_.Run(type, output); | 115 callback_.Run(type, output); |
| 132 } | 116 } |
| 133 | 117 |
| 134 bool ProcessProxy::StopWatching() { | 118 void ProcessProxy::StopWatching() { |
| 135 if (!watcher_started_) | 119 if (!output_watcher_.get()) |
| 136 return true; | 120 return; |
| 137 // Signal Watcher that we are done. We use self-pipe trick to unblock watcher. | 121 |
| 138 // Anything may be written to the pipe. | 122 watcher_runner_->PostTask( |
| 139 const char message[] = "q"; | 123 FROM_HERE, |
| 140 return base::WriteFileDescriptor(shutdown_pipe_[PIPE_END_WRITE], | 124 base::Bind(&StopOutputWatcher, base::Passed(&output_watcher_))); |
| 141 message, sizeof(message)); | |
| 142 } | 125 } |
| 143 | 126 |
| 144 void ProcessProxy::Close() { | 127 void ProcessProxy::Close() { |
| 145 if (!process_launched_) | 128 if (!process_launched_) |
| 146 return; | 129 return; |
| 147 | 130 |
| 148 process_launched_ = false; | 131 process_launched_ = false; |
| 149 callback_set_ = false; | 132 callback_set_ = false; |
| 150 callback_ = ProcessOutputCallback(); | 133 callback_ = ProcessOutputCallback(); |
| 151 callback_runner_ = NULL; | 134 callback_runner_ = NULL; |
| 152 | 135 |
| 153 base::Process process = base::Process::DeprecatedGetProcessFromHandle(pid_); | 136 base::Process process = base::Process::DeprecatedGetProcessFromHandle(pid_); |
| 154 process.Terminate(0, true /* wait */); | 137 process.Terminate(0, true /* wait */); |
| 155 | 138 |
| 156 // TODO(tbarzic): What if this fails? | |
| 157 StopWatching(); | 139 StopWatching(); |
| 158 | 140 CloseFdPair(pt_pair_); |
| 159 CloseAllFdPairs(); | |
| 160 } | 141 } |
| 161 | 142 |
| 162 bool ProcessProxy::Write(const std::string& text) { | 143 bool ProcessProxy::Write(const std::string& text) { |
| 163 if (!process_launched_) | 144 if (!process_launched_) |
| 164 return false; | 145 return false; |
| 165 | 146 |
| 166 // We don't want to write '\0' to the pipe. | 147 // We don't want to write '\0' to the pipe. |
| 167 size_t data_size = text.length() * sizeof(*text.c_str()); | 148 size_t data_size = text.length() * sizeof(*text.c_str()); |
| 168 return base::WriteFileDescriptor( | 149 return base::WriteFileDescriptor( |
| 169 pt_pair_[PT_MASTER_FD], text.c_str(), data_size); | 150 pt_pair_[PT_MASTER_FD], text.c_str(), data_size); |
| 170 } | 151 } |
| 171 | 152 |
| 172 bool ProcessProxy::OnTerminalResize(int width, int height) { | 153 bool ProcessProxy::OnTerminalResize(int width, int height) { |
| 173 if (width < 0 || height < 0) | 154 if (width < 0 || height < 0) |
| 174 return false; | 155 return false; |
| 175 | 156 |
| 176 winsize ws; | 157 winsize ws; |
| 177 // Number of rows. | 158 // Number of rows. |
| 178 ws.ws_row = height; | 159 ws.ws_row = height; |
| 179 // Number of columns. | 160 // Number of columns. |
| 180 ws.ws_col = width; | 161 ws.ws_col = width; |
| 181 | 162 |
| 182 return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1); | 163 return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1); |
| 183 } | 164 } |
| 184 | 165 |
| 185 ProcessProxy::~ProcessProxy() { | 166 ProcessProxy::~ProcessProxy() { |
| 186 // In case watcher did not started, we may get deleted without calling Close. | 167 Close(); |
| 187 // In that case we have to clean up created pipes. If watcher had been | |
| 188 // started, there will be a callback with our reference owned by | |
| 189 // process_output_watcher until Close is called, so we know Close has been | |
| 190 // called by now (and pipes have been cleaned). | |
| 191 if (!watcher_started_) | |
| 192 CloseAllFdPairs(); | |
| 193 } | 168 } |
| 194 | 169 |
| 195 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) { | 170 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) { |
| 196 ClearFdPair(pt_pair); | 171 ClearFdPair(pt_pair); |
| 197 | 172 |
| 198 // Open Master. | 173 // Open Master. |
| 199 pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY)); | 174 pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY)); |
| 200 if (pt_pair[PT_MASTER_FD] == -1) | 175 if (pt_pair[PT_MASTER_FD] == -1) |
| 201 return false; | 176 return false; |
| 202 | 177 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 base::Process process = | 214 base::Process process = |
| 240 base::LaunchProcess(base::CommandLine(base::FilePath(command)), options); | 215 base::LaunchProcess(base::CommandLine(base::FilePath(command)), options); |
| 241 | 216 |
| 242 // TODO(rvargas) crbug/417532: This is somewhat wrong but the interface of | 217 // TODO(rvargas) crbug/417532: This is somewhat wrong but the interface of |
| 243 // Open vends pid_t* so ownership is quite vague anyway, and Process::Close | 218 // Open vends pid_t* so ownership is quite vague anyway, and Process::Close |
| 244 // doesn't do much in POSIX. | 219 // doesn't do much in POSIX. |
| 245 *pid = process.Pid(); | 220 *pid = process.Pid(); |
| 246 return process.IsValid(); | 221 return process.IsValid(); |
| 247 } | 222 } |
| 248 | 223 |
| 249 void ProcessProxy::CloseAllFdPairs() { | |
| 250 CloseFdPair(pt_pair_); | |
| 251 CloseFdPair(shutdown_pipe_); | |
| 252 } | |
| 253 | |
| 254 void ProcessProxy::CloseFdPair(int* pipe) { | 224 void ProcessProxy::CloseFdPair(int* pipe) { |
| 255 CloseFd(&(pipe[PIPE_END_READ])); | 225 CloseFd(&(pipe[PT_MASTER_FD])); |
| 256 CloseFd(&(pipe[PIPE_END_WRITE])); | 226 CloseFd(&(pipe[PT_SLAVE_FD])); |
| 257 } | 227 } |
| 258 | 228 |
| 259 void ProcessProxy::CloseFd(int* fd) { | 229 void ProcessProxy::CloseFd(int* fd) { |
| 260 if (*fd != kInvalidFd) { | 230 if (*fd != kInvalidFd) { |
| 261 if (IGNORE_EINTR(close(*fd)) != 0) | 231 if (IGNORE_EINTR(close(*fd)) != 0) |
| 262 DPLOG(WARNING) << "close fd failed."; | 232 DPLOG(WARNING) << "close fd failed."; |
| 263 } | 233 } |
| 264 *fd = kInvalidFd; | 234 *fd = kInvalidFd; |
| 265 } | 235 } |
| 266 | 236 |
| 267 void ProcessProxy::ClearAllFdPairs() { | |
| 268 ClearFdPair(pt_pair_); | |
| 269 ClearFdPair(shutdown_pipe_); | |
| 270 } | |
| 271 | |
| 272 void ProcessProxy::ClearFdPair(int* pipe) { | 237 void ProcessProxy::ClearFdPair(int* pipe) { |
| 273 pipe[PIPE_END_READ] = kInvalidFd; | 238 pipe[PT_MASTER_FD] = kInvalidFd; |
| 274 pipe[PIPE_END_WRITE] = kInvalidFd; | 239 pipe[PT_SLAVE_FD] = kInvalidFd; |
| 275 } | 240 } |
| 276 | 241 |
| 277 } // namespace chromeos | 242 } // namespace chromeos |
| OLD | NEW |