// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/process_proxy/process_proxy_registry.h"
#include "base/bind.h"
namespace chromeos {
namespace {
const char kWatcherThreadName[] = "ProcessWatcherThread";
const char kStdoutOutputType[] = "stdout";
const char kStderrOutputType[] = "stderr";
const char kExitOutputType[] = "exit";
const char* ProcessOutputTypeToString(ProcessOutputType type) {
switch (type) {
case PROCESS_OUTPUT_TYPE_OUT:
return kStdoutOutputType;
case PROCESS_OUTPUT_TYPE_ERR:
return kStderrOutputType;
case PROCESS_OUTPUT_TYPE_EXIT:
return kExitOutputType;
default:
return NULL;
}
}
static base::LazyInstance<ProcessProxyRegistry> g_process_proxy_registry =
LAZY_INSTANCE_INITIALIZER;
} // namespace
ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo() {
}
ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo(
const ProcessProxyInfo& other) {
// This should be called with empty info only.
DCHECK(!other.proxy.get() && !other.watcher_thread.get());
}
ProcessProxyRegistry::ProcessProxyInfo::~ProcessProxyInfo() {
}
ProcessProxyRegistry::ProcessProxyRegistry() {
}
ProcessProxyRegistry::~ProcessProxyRegistry() {
// TODO(tbarzic): Fix issue with ProcessProxyRegistry being destroyed
// on a different thread (it's a LazyInstance).
DetachFromThread();
// Close all proxies we own.
while (!proxy_map_.empty())
CloseProcess(proxy_map_.begin()->first);
}
// static
ProcessProxyRegistry* ProcessProxyRegistry::Get() {
return g_process_proxy_registry.Pointer();
}
bool ProcessProxyRegistry::OpenProcess(
const std::string& command,
pid_t* pid,
const ProcessOutputCallbackWithPid& callback) {
DCHECK(CalledOnValidThread());
// TODO(tbarzic): Instead of creating a new thread for each new process proxy,
// use one thread for all processes.
// We will need new thread for proxy's outpu watcher.
scoped_ptr<base::Thread> watcher_thread(new base::Thread(kWatcherThreadName));
if (!watcher_thread->Start()) {
return false;
}
// Create and open new proxy.
scoped_refptr<ProcessProxy> proxy(new ProcessProxy());
if (!proxy->Open(command, pid))
return false;
// Kick off watcher.
// We can use Unretained because proxy will stop calling callback after it is
// closed, which is done befire this object goes away.
if (!proxy->StartWatchingOnThread(watcher_thread.get(),
base::Bind(&ProcessProxyRegistry::OnProcessOutput,
base::Unretained(this), *pid))) {
proxy->Close();
watcher_thread->Stop();
return false;
}
DCHECK(proxy_map_.find(*pid) == proxy_map_.end());
// Save info for newly created proxy. We cannot do this before ProcessProxy is
// created because we don't know |pid| then.
ProcessProxyInfo& info = proxy_map_[*pid];
info.proxy.swap(proxy);
info.watcher_thread.reset(watcher_thread.release());
info.process_id = *pid;
info.callback = callback;
return true;
}
bool ProcessProxyRegistry::SendInput(pid_t pid, const std::string& data) {
DCHECK(CalledOnValidThread());
std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
if (it == proxy_map_.end())
return false;
return it->second.proxy->Write(data);
}
bool ProcessProxyRegistry::CloseProcess(pid_t pid) {
DCHECK(CalledOnValidThread());
std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
if (it == proxy_map_.end())
return false;
it->second.proxy->Close();
it->second.watcher_thread->Stop();
proxy_map_.erase(it);
return true;
}
bool ProcessProxyRegistry::OnTerminalResize(pid_t pid, int width, int height) {
DCHECK(CalledOnValidThread());
std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
if (it == proxy_map_.end())
return false;
return it->second.proxy->OnTerminalResize(width, height);
}
void ProcessProxyRegistry::OnProcessOutput(pid_t pid,
ProcessOutputType type, const std::string& data) {
DCHECK(CalledOnValidThread());
const char* type_str = ProcessOutputTypeToString(type);
DCHECK(type_str);
std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
if (it == proxy_map_.end())
return;
it->second.callback.Run(pid, std::string(type_str), data);
// Contact with the slave end of the terminal has been lost. We have to close
// the process.
if (type == PROCESS_OUTPUT_TYPE_EXIT)
CloseProcess(pid);
}
} // namespace chromeos