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.
5 #include "chromeos/process_proxy/process_proxy_registry.h"
13 const char kWatcherThreadName
[] = "ProcessWatcherThread";
15 const char kStdoutOutputType
[] = "stdout";
16 const char kExitOutputType
[] = "exit";
18 const char* ProcessOutputTypeToString(ProcessOutputType type
) {
20 case PROCESS_OUTPUT_TYPE_OUT
:
21 return kStdoutOutputType
;
22 case PROCESS_OUTPUT_TYPE_EXIT
:
23 return kExitOutputType
;
29 static base::LazyInstance
<ProcessProxyRegistry
> g_process_proxy_registry
=
30 LAZY_INSTANCE_INITIALIZER
;
34 ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo() {
37 ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo(
38 const ProcessProxyInfo
& other
) {
39 // This should be called with empty info only.
40 DCHECK(!other
.proxy
.get());
43 ProcessProxyRegistry::ProcessProxyInfo::~ProcessProxyInfo() {
46 ProcessProxyRegistry::ProcessProxyRegistry() {
49 ProcessProxyRegistry::~ProcessProxyRegistry() {
50 // TODO(tbarzic): Fix issue with ProcessProxyRegistry being destroyed
51 // on a different thread (it's a LazyInstance).
57 void ProcessProxyRegistry::ShutDown() {
58 // Close all proxies we own.
59 while (!proxy_map_
.empty())
60 CloseProcess(proxy_map_
.begin()->first
);
62 if (watcher_thread_
) {
63 watcher_thread_
->Stop();
64 watcher_thread_
.reset();
69 ProcessProxyRegistry
* ProcessProxyRegistry::Get() {
70 return g_process_proxy_registry
.Pointer();
73 bool ProcessProxyRegistry::OpenProcess(
74 const std::string
& command
,
76 const ProcessOutputCallbackWithPid
& callback
) {
77 DCHECK(CalledOnValidThread());
79 if (!EnsureWatcherThreadStarted())
82 // Create and open new proxy.
83 scoped_refptr
<ProcessProxy
> proxy(new ProcessProxy());
84 if (!proxy
->Open(command
, pid
))
88 // We can use Unretained because proxy will stop calling callback after it is
89 // closed, which is done before this object goes away.
90 if (!proxy
->StartWatchingOutput(
91 watcher_thread_
->task_runner(),
92 base::Bind(&ProcessProxyRegistry::OnProcessOutput
,
93 base::Unretained(this), *pid
))) {
98 DCHECK(proxy_map_
.find(*pid
) == proxy_map_
.end());
100 // Save info for newly created proxy. We cannot do this before ProcessProxy is
101 // created because we don't know |pid| then.
102 ProcessProxyInfo
& info
= proxy_map_
[*pid
];
103 info
.proxy
.swap(proxy
);
104 info
.process_id
= *pid
;
105 info
.callback
= callback
;
109 bool ProcessProxyRegistry::SendInput(pid_t pid
, const std::string
& data
) {
110 DCHECK(CalledOnValidThread());
112 std::map
<pid_t
, ProcessProxyInfo
>::iterator it
= proxy_map_
.find(pid
);
113 if (it
== proxy_map_
.end())
115 return it
->second
.proxy
->Write(data
);
118 bool ProcessProxyRegistry::CloseProcess(pid_t pid
) {
119 DCHECK(CalledOnValidThread());
121 std::map
<pid_t
, ProcessProxyInfo
>::iterator it
= proxy_map_
.find(pid
);
122 if (it
== proxy_map_
.end())
125 it
->second
.proxy
->Close();
126 proxy_map_
.erase(it
);
130 bool ProcessProxyRegistry::OnTerminalResize(pid_t pid
, int width
, int height
) {
131 DCHECK(CalledOnValidThread());
133 std::map
<pid_t
, ProcessProxyInfo
>::iterator it
= proxy_map_
.find(pid
);
134 if (it
== proxy_map_
.end())
137 return it
->second
.proxy
->OnTerminalResize(width
, height
);
140 void ProcessProxyRegistry::OnProcessOutput(pid_t pid
,
141 ProcessOutputType type
, const std::string
& data
) {
142 DCHECK(CalledOnValidThread());
144 const char* type_str
= ProcessOutputTypeToString(type
);
147 std::map
<pid_t
, ProcessProxyInfo
>::iterator it
= proxy_map_
.find(pid
);
148 if (it
== proxy_map_
.end())
150 it
->second
.callback
.Run(pid
, std::string(type_str
), data
);
152 // Contact with the slave end of the terminal has been lost. We have to close
154 if (type
== PROCESS_OUTPUT_TYPE_EXIT
)
158 bool ProcessProxyRegistry::EnsureWatcherThreadStarted() {
159 if (watcher_thread_
.get())
162 // TODO(tbarzic): Change process output watcher to watch for fd readability on
163 // FILE thread, and move output reading to worker thread instead of
164 // spinning a new thread.
165 watcher_thread_
.reset(new base::Thread(kWatcherThreadName
));
166 return watcher_thread_
->StartWithOptions(
167 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0));
170 } // namespace chromeos