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 |