This source file includes following definitions.
- watcher_started_
- Open
- StartWatchingOnThread
- OnProcessOutput
- CallOnProcessOutputCallback
- StopWatching
- Close
- Write
- OnTerminalResize
- CreatePseudoTerminalPair
- LaunchProcess
- CloseAllFdPairs
- CloseFdPair
- CloseFd
- ClearAllFdPairs
- ClearFdPair
#include "chromeos/process_proxy/process_proxy.h"
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/threading/thread.h"
#include "chromeos/process_proxy/process_output_watcher.h"
namespace {
enum PipeEnd {
PIPE_END_READ,
PIPE_END_WRITE
};
enum PseudoTerminalFd {
PT_MASTER_FD,
PT_SLAVE_FD
};
const int kInvalidFd = -1;
}
namespace chromeos {
ProcessProxy::ProcessProxy(): process_launched_(false),
callback_set_(false),
watcher_started_(false) {
ClearAllFdPairs();
};
bool ProcessProxy::Open(const std::string& command, pid_t* pid) {
if (process_launched_)
return false;
if (!CreatePseudoTerminalPair(pt_pair_)) {
return false;
}
process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_);
if (process_launched_) {
CloseFd(&pt_pair_[PT_SLAVE_FD]);
*pid = pid_;
LOG(WARNING) << "Process launched: " << pid_;
} else {
CloseFdPair(pt_pair_);
}
return process_launched_;
}
bool ProcessProxy::StartWatchingOnThread(
base::Thread* watch_thread,
const ProcessOutputCallback& callback) {
DCHECK(process_launched_);
if (watcher_started_)
return false;
if (pipe(shutdown_pipe_))
return false;
int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD]));
if (master_copy == -1)
return false;
callback_set_ = true;
callback_ = callback;
callback_runner_ = base::MessageLoopProxy::current();
ProcessOutputWatcher* output_watcher =
new ProcessOutputWatcher(master_copy,
shutdown_pipe_[PIPE_END_READ],
base::Bind(&ProcessProxy::OnProcessOutput,
this));
shutdown_pipe_[PIPE_END_READ] = -1;
watch_thread->message_loop()->PostTask(FROM_HERE,
base::Bind(&ProcessOutputWatcher::Start,
base::Unretained(output_watcher)));
watcher_started_ = true;
return true;
}
void ProcessProxy::OnProcessOutput(ProcessOutputType type,
const std::string& output) {
if (!callback_runner_.get())
return;
callback_runner_->PostTask(
FROM_HERE,
base::Bind(&ProcessProxy::CallOnProcessOutputCallback,
this, type, output));
}
void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type,
const std::string& output) {
if (callback_set_)
callback_.Run(type, output);
}
bool ProcessProxy::StopWatching() {
if (!watcher_started_)
return true;
const char message[] = "q";
return base::WriteFileDescriptor(shutdown_pipe_[PIPE_END_WRITE],
message, sizeof(message));
}
void ProcessProxy::Close() {
if (!process_launched_)
return;
process_launched_ = false;
callback_set_ = false;
callback_ = ProcessOutputCallback();
callback_runner_ = NULL;
base::KillProcess(pid_, 0, true );
StopWatching();
CloseAllFdPairs();
}
bool ProcessProxy::Write(const std::string& text) {
if (!process_launched_)
return false;
size_t data_size = text.length() * sizeof(*text.c_str());
int bytes_written =
base::WriteFileDescriptor(pt_pair_[PT_MASTER_FD],
text.c_str(), data_size);
return (bytes_written == static_cast<int>(data_size));
}
bool ProcessProxy::OnTerminalResize(int width, int height) {
if (width < 0 || height < 0)
return false;
winsize ws;
ws.ws_row = height;
ws.ws_col = width;
return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1);
}
ProcessProxy::~ProcessProxy() {
if (!watcher_started_)
CloseAllFdPairs();
}
bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) {
ClearFdPair(pt_pair);
pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY));
if (pt_pair[PT_MASTER_FD] == -1)
return false;
if (grantpt(pt_pair_[PT_MASTER_FD]) != 0 ||
unlockpt(pt_pair_[PT_MASTER_FD]) != 0) {
CloseFd(&pt_pair[PT_MASTER_FD]);
return false;
}
char* slave_name = NULL;
slave_name = ptsname(pt_pair_[PT_MASTER_FD]);
if (slave_name)
pt_pair_[PT_SLAVE_FD] = HANDLE_EINTR(open(slave_name, O_RDWR | O_NOCTTY));
if (pt_pair_[PT_SLAVE_FD] == -1) {
CloseFdPair(pt_pair);
return false;
}
return true;
}
bool ProcessProxy::LaunchProcess(const std::string& command, int slave_fd,
pid_t* pid) {
base::FileHandleMappingVector fds_mapping;
fds_mapping.push_back(std::make_pair(slave_fd, STDIN_FILENO));
fds_mapping.push_back(std::make_pair(slave_fd, STDOUT_FILENO));
fds_mapping.push_back(std::make_pair(slave_fd, STDERR_FILENO));
base::LaunchOptions options;
options.fds_to_remap = &fds_mapping;
options.ctrl_terminal_fd = slave_fd;
options.environ["TERM"] = "xterm";
return base::LaunchProcess(CommandLine(base::FilePath(command)), options,
pid);
}
void ProcessProxy::CloseAllFdPairs() {
CloseFdPair(pt_pair_);
CloseFdPair(shutdown_pipe_);
}
void ProcessProxy::CloseFdPair(int* pipe) {
CloseFd(&(pipe[PIPE_END_READ]));
CloseFd(&(pipe[PIPE_END_WRITE]));
}
void ProcessProxy::CloseFd(int* fd) {
if (*fd != kInvalidFd) {
if (IGNORE_EINTR(close(*fd)) != 0)
DPLOG(WARNING) << "close fd failed.";
}
*fd = kInvalidFd;
}
void ProcessProxy::ClearAllFdPairs() {
ClearFdPair(pt_pair_);
ClearFdPair(shutdown_pipe_);
}
void ProcessProxy::ClearFdPair(int* pipe) {
pipe[PIPE_END_READ] = kInvalidFd;
pipe[PIPE_END_WRITE] = kInvalidFd;
}
}