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"
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"
28 enum PseudoTerminalFd
{
33 const int kInvalidFd
= -1;
39 ProcessProxy::ProcessProxy(): process_launched_(false),
41 watcher_started_(false) {
42 // Set pipes to initial, invalid value so we can easily know if a pipe was
47 bool ProcessProxy::Open(const std::string
& command
, pid_t
* pid
) {
48 if (process_launched_
)
51 if (!CreatePseudoTerminalPair(pt_pair_
)) {
55 process_launched_
= LaunchProcess(command
, pt_pair_
[PT_SLAVE_FD
], &pid_
);
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
]);
61 LOG(WARNING
) << "Process launched: " << pid_
;
63 CloseFdPair(pt_pair_
);
65 return process_launched_
;
68 bool ProcessProxy::StartWatchingOnThread(
69 base::Thread
* watch_thread
,
70 const ProcessOutputCallback
& callback
) {
71 DCHECK(process_launched_
);
74 if (pipe(shutdown_pipe_
))
77 // We give ProcessOutputWatcher a copy of master to make life easier during
79 // TODO(tbarzic): improve fd managment.
80 int master_copy
= HANDLE_EINTR(dup(pt_pair_
[PT_MASTER_FD
]));
81 if (master_copy
== -1)
86 callback_runner_
= base::MessageLoopProxy::current();
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
,
96 // Output watcher took ownership of the read end of shutdown pipe.
97 shutdown_pipe_
[PIPE_END_READ
] = -1;
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;
107 void ProcessProxy::OnProcessOutput(ProcessOutputType type
,
108 const std::string
& output
) {
109 if (!callback_runner_
.get())
112 callback_runner_
->PostTask(
114 base::Bind(&ProcessProxy::CallOnProcessOutputCallback
,
115 this, type
, output
));
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
126 callback_
.Run(type
, output
);
129 bool ProcessProxy::StopWatching() {
130 if (!watcher_started_
)
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
));
139 void ProcessProxy::Close() {
140 if (!process_launched_
)
143 process_launched_
= false;
144 callback_set_
= false;
145 callback_
= ProcessOutputCallback();
146 callback_runner_
= NULL
;
148 base::KillProcess(pid_
, 0, true /* wait */);
150 // TODO(tbarzic): What if this fails?
156 bool ProcessProxy::Write(const std::string
& text
) {
157 if (!process_launched_
)
160 // We don't want to write '\0' to the pipe.
161 size_t data_size
= text
.length() * sizeof(*text
.c_str());
163 file_util::WriteFileDescriptor(pt_pair_
[PT_MASTER_FD
],
164 text
.c_str(), data_size
);
165 return (bytes_written
== static_cast<int>(data_size
));
168 bool ProcessProxy::OnTerminalResize(int width
, int height
) {
169 if (width
< 0 || height
< 0)
175 // Number of columns.
178 return (HANDLE_EINTR(ioctl(pt_pair_
[PT_MASTER_FD
], TIOCSWINSZ
, &ws
)) != -1);
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_
)
191 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair
) {
192 ClearFdPair(pt_pair
);
195 pt_pair
[PT_MASTER_FD
] = HANDLE_EINTR(posix_openpt(O_RDWR
| O_NOCTTY
));
196 if (pt_pair
[PT_MASTER_FD
] == -1)
199 if (grantpt(pt_pair_
[PT_MASTER_FD
]) != 0 ||
200 unlockpt(pt_pair_
[PT_MASTER_FD
]) != 0) {
201 CloseFd(&pt_pair
[PT_MASTER_FD
]);
204 char* slave_name
= NULL
;
205 // Per man page, slave_name must not be freed.
206 slave_name
= ptsname(pt_pair_
[PT_MASTER_FD
]);
208 pt_pair_
[PT_SLAVE_FD
] = HANDLE_EINTR(open(slave_name
, O_RDWR
| O_NOCTTY
));
210 if (pt_pair_
[PT_SLAVE_FD
] == -1) {
211 CloseFdPair(pt_pair
);
218 bool ProcessProxy::LaunchProcess(const std::string
& command
, int slave_fd
,
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
;
229 base::EnvironmentVector environ
;
230 environ
.push_back(std::make_pair("TERM", "xterm"));
231 options
.environ
= &environ
;
233 // Launch the process.
234 return base::LaunchProcess(CommandLine(base::FilePath(command
)), options
,
238 void ProcessProxy::CloseAllFdPairs() {
239 CloseFdPair(pt_pair_
);
240 CloseFdPair(shutdown_pipe_
);
243 void ProcessProxy::CloseFdPair(int* pipe
) {
244 CloseFd(&(pipe
[PIPE_END_READ
]));
245 CloseFd(&(pipe
[PIPE_END_WRITE
]));
248 void ProcessProxy::CloseFd(int* fd
) {
249 if (*fd
!= kInvalidFd
) {
250 if (HANDLE_EINTR(close(*fd
)) != 0)
251 DPLOG(WARNING
) << "close fd failed.";
256 void ProcessProxy::ClearAllFdPairs() {
257 ClearFdPair(pt_pair_
);
258 ClearFdPair(shutdown_pipe_
);
261 void ProcessProxy::ClearFdPair(int* pipe
) {
262 pipe
[PIPE_END_READ
] = kInvalidFd
;
263 pipe
[PIPE_END_WRITE
] = kInvalidFd
;
266 } // namespace chromeos