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