• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ProcessOutputTypeToString(ProcessOutputType type)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 
ProcessProxyInfo()37 ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo() {
38 }
39 
ProcessProxyInfo(const ProcessProxyInfo & other)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 
~ProcessProxyInfo()46 ProcessProxyRegistry::ProcessProxyInfo::~ProcessProxyInfo() {
47 }
48 
ProcessProxyRegistry()49 ProcessProxyRegistry::ProcessProxyRegistry() {
50 }
51 
~ProcessProxyRegistry()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
Get()63 ProcessProxyRegistry* ProcessProxyRegistry::Get() {
64   return g_process_proxy_registry.Pointer();
65 }
66 
OpenProcess(const std::string & command,pid_t * pid,const ProcessOutputCallbackWithPid & callback)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 
SendInput(pid_t pid,const std::string & data)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 
CloseProcess(pid_t pid)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 
OnTerminalResize(pid_t pid,int width,int height)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 
OnProcessOutput(pid_t pid,ProcessOutputType type,const std::string & data)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