Home | History | Annotate | Download | only in process_proxy
      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.
      4 
      5 #include "chromeos/process_proxy/process_proxy_registry.h"
      6 
      7 #include "base/bind.h"
      8 
      9 namespace chromeos {
     10 
     11 namespace {
     12 
     13 const char kWatcherThreadName[] = "ProcessWatcherThread";
     14 
     15 const char kStdoutOutputType[] = "stdout";
     16 const char kStderrOutputType[] = "stderr";
     17 const char kExitOutputType[] = "exit";
     18 
     19 const char* ProcessOutputTypeToString(ProcessOutputType type) {
     20   switch (type) {
     21     case PROCESS_OUTPUT_TYPE_OUT:
     22       return kStdoutOutputType;
     23     case PROCESS_OUTPUT_TYPE_ERR:
     24       return kStderrOutputType;
     25     case PROCESS_OUTPUT_TYPE_EXIT:
     26       return kExitOutputType;
     27     default:
     28       return NULL;
     29   }
     30 }
     31 
     32 static base::LazyInstance<ProcessProxyRegistry> g_process_proxy_registry =
     33     LAZY_INSTANCE_INITIALIZER;
     34 
     35 }  // namespace
     36 
     37 ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo() {
     38 }
     39 
     40 ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo(
     41     const ProcessProxyInfo& other) {
     42   // This should be called with empty info only.
     43   DCHECK(!other.proxy.get() && !other.watcher_thread.get());
     44 }
     45 
     46 ProcessProxyRegistry::ProcessProxyInfo::~ProcessProxyInfo() {
     47 }
     48 
     49 ProcessProxyRegistry::ProcessProxyRegistry() {
     50 }
     51 
     52 ProcessProxyRegistry::~ProcessProxyRegistry() {
     53   // TODO(tbarzic): Fix issue with ProcessProxyRegistry being destroyed
     54   // on a different thread (it's a LazyInstance).
     55   DetachFromThread();
     56 
     57   // Close all proxies we own.
     58   while (!proxy_map_.empty())
     59     CloseProcess(proxy_map_.begin()->first);
     60 }
     61 
     62 // static
     63 ProcessProxyRegistry* ProcessProxyRegistry::Get() {
     64   return g_process_proxy_registry.Pointer();
     65 }
     66 
     67 bool ProcessProxyRegistry::OpenProcess(
     68     const std::string& command,
     69     pid_t* pid,
     70     const ProcessOutputCallbackWithPid& callback) {
     71   DCHECK(CalledOnValidThread());
     72 
     73   // TODO(tbarzic): Instead of creating a new thread for each new process proxy,
     74   // use one thread for all processes.
     75   // We will need new thread for proxy's outpu watcher.
     76   scoped_ptr<base::Thread> watcher_thread(new base::Thread(kWatcherThreadName));
     77   if (!watcher_thread->Start()) {
     78     return false;
     79   }
     80 
     81   // Create and open new proxy.
     82   scoped_refptr<ProcessProxy> proxy(new ProcessProxy());
     83   if (!proxy->Open(command, pid))
     84     return false;
     85 
     86   // Kick off watcher.
     87   // We can use Unretained because proxy will stop calling callback after it is
     88   // closed, which is done befire this object goes away.
     89   if (!proxy->StartWatchingOnThread(watcher_thread.get(),
     90            base::Bind(&ProcessProxyRegistry::OnProcessOutput,
     91                       base::Unretained(this), *pid))) {
     92     proxy->Close();
     93     watcher_thread->Stop();
     94     return false;
     95   }
     96 
     97   DCHECK(proxy_map_.find(*pid) == proxy_map_.end());
     98 
     99   // Save info for newly created proxy. We cannot do this before ProcessProxy is
    100   // created because we don't know |pid| then.
    101   ProcessProxyInfo& info = proxy_map_[*pid];
    102   info.proxy.swap(proxy);
    103   info.watcher_thread.reset(watcher_thread.release());
    104   info.process_id = *pid;
    105   info.callback = callback;
    106   return true;
    107 }
    108 
    109 bool ProcessProxyRegistry::SendInput(pid_t pid, const std::string& data) {
    110   DCHECK(CalledOnValidThread());
    111 
    112   std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
    113   if (it == proxy_map_.end())
    114     return false;
    115   return it->second.proxy->Write(data);
    116 }
    117 
    118 bool ProcessProxyRegistry::CloseProcess(pid_t pid) {
    119   DCHECK(CalledOnValidThread());
    120 
    121   std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
    122   if (it == proxy_map_.end())
    123     return false;
    124 
    125   it->second.proxy->Close();
    126   it->second.watcher_thread->Stop();
    127   proxy_map_.erase(it);
    128   return true;
    129 }
    130 
    131 bool ProcessProxyRegistry::OnTerminalResize(pid_t pid, int width, int height) {
    132   DCHECK(CalledOnValidThread());
    133 
    134   std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
    135   if (it == proxy_map_.end())
    136     return false;
    137 
    138   return it->second.proxy->OnTerminalResize(width, height);
    139 }
    140 
    141 void ProcessProxyRegistry::OnProcessOutput(pid_t pid,
    142     ProcessOutputType type, const std::string& data) {
    143   DCHECK(CalledOnValidThread());
    144 
    145   const char* type_str = ProcessOutputTypeToString(type);
    146   DCHECK(type_str);
    147 
    148   std::map<pid_t, ProcessProxyInfo>::iterator it = proxy_map_.find(pid);
    149   if (it == proxy_map_.end())
    150     return;
    151   it->second.callback.Run(pid, std::string(type_str), data);
    152 
    153   // Contact with the slave end of the terminal has been lost. We have to close
    154   // the process.
    155   if (type == PROCESS_OUTPUT_TYPE_EXIT)
    156     CloseProcess(pid);
    157 }
    158 
    159 }  // namespace chromeos
    160