- add sources.
[platform/framework/web/crosswalk.git] / src / chromeos / process_proxy / process_proxy_registry.cc
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