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/extensions/api/terminal/terminal_private_api.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/sys_info.h"
10 #include "base/values.h"
11 #include "chrome/browser/extensions/api/terminal/terminal_extension_helper.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_system.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/extensions/api/terminal_private.h"
16 #include "chromeos/process_proxy/process_proxy_registry.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "extensions/browser/event_router.h"
19
20 namespace terminal_private = extensions::api::terminal_private;
21 namespace OnTerminalResize =
22 extensions::api::terminal_private::OnTerminalResize;
23 namespace OpenTerminalProcess =
24 extensions::api::terminal_private::OpenTerminalProcess;
25 namespace SendInput = extensions::api::terminal_private::SendInput;
26
27 namespace {
28
29 const char kCroshName[] = "crosh";
30 const char kCroshCommand[] = "/usr/bin/crosh";
31 // We make stubbed crosh just echo back input.
32 const char kStubbedCroshCommand[] = "cat";
33
GetCroshPath()34 const char* GetCroshPath() {
35 if (base::SysInfo::IsRunningOnChromeOS())
36 return kCroshCommand;
37 else
38 return kStubbedCroshCommand;
39 }
40
GetProcessCommandForName(const std::string & name)41 const char* GetProcessCommandForName(const std::string& name) {
42 if (name == kCroshName)
43 return GetCroshPath();
44 else
45 return NULL;
46 }
47
NotifyProcessOutput(Profile * profile,const std::string & extension_id,pid_t pid,const std::string & output_type,const std::string & output)48 void NotifyProcessOutput(Profile* profile,
49 const std::string& extension_id,
50 pid_t pid,
51 const std::string& output_type,
52 const std::string& output) {
53 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
54 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
55 base::Bind(&NotifyProcessOutput, profile, extension_id,
56 pid, output_type, output));
57 return;
58 }
59
60 scoped_ptr<base::ListValue> args(new base::ListValue());
61 args->Append(new base::FundamentalValue(pid));
62 args->Append(new base::StringValue(output_type));
63 args->Append(new base::StringValue(output));
64
65 if (profile &&
66 extensions::ExtensionSystem::Get(profile)->event_router()) {
67 scoped_ptr<extensions::Event> event(new extensions::Event(
68 terminal_private::OnProcessOutput::kEventName, args.Pass()));
69 extensions::ExtensionSystem::Get(profile)->event_router()->
70 DispatchEventToExtension(extension_id, event.Pass());
71 }
72 }
73
74 } // namespace
75
76 namespace extensions {
77
TerminalPrivateFunction()78 TerminalPrivateFunction::TerminalPrivateFunction() {}
79
~TerminalPrivateFunction()80 TerminalPrivateFunction::~TerminalPrivateFunction() {}
81
RunImpl()82 bool TerminalPrivateFunction::RunImpl() {
83 return RunTerminalFunction();
84 }
85
86 TerminalPrivateOpenTerminalProcessFunction::
TerminalPrivateOpenTerminalProcessFunction()87 TerminalPrivateOpenTerminalProcessFunction() : command_(NULL) {}
88
89 TerminalPrivateOpenTerminalProcessFunction::
~TerminalPrivateOpenTerminalProcessFunction()90 ~TerminalPrivateOpenTerminalProcessFunction() {}
91
RunTerminalFunction()92 bool TerminalPrivateOpenTerminalProcessFunction::RunTerminalFunction() {
93 scoped_ptr<OpenTerminalProcess::Params> params(
94 OpenTerminalProcess::Params::Create(*args_));
95 EXTENSION_FUNCTION_VALIDATE(params.get());
96
97 command_ = GetProcessCommandForName(params->process_name);
98 if (!command_) {
99 error_ = "Invalid process name.";
100 return false;
101 }
102
103 // Registry lives on FILE thread.
104 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
105 base::Bind(&TerminalPrivateOpenTerminalProcessFunction::OpenOnFileThread,
106 this));
107 return true;
108 }
109
OpenOnFileThread()110 void TerminalPrivateOpenTerminalProcessFunction::OpenOnFileThread() {
111 DCHECK(command_);
112
113 chromeos::ProcessProxyRegistry* registry =
114 chromeos::ProcessProxyRegistry::Get();
115 pid_t pid;
116 if (!registry->OpenProcess(
117 command_,
118 &pid,
119 base::Bind(&NotifyProcessOutput, GetProfile(), extension_id()))) {
120 // If new process could not be opened, we return -1.
121 pid = -1;
122 }
123
124 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
125 base::Bind(&TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread,
126 this, pid));
127 }
128
~TerminalPrivateSendInputFunction()129 TerminalPrivateSendInputFunction::~TerminalPrivateSendInputFunction() {}
130
RespondOnUIThread(pid_t pid)131 void TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread(pid_t pid) {
132 SetResult(new base::FundamentalValue(pid));
133 SendResponse(true);
134 }
135
RunTerminalFunction()136 bool TerminalPrivateSendInputFunction::RunTerminalFunction() {
137 scoped_ptr<SendInput::Params> params(SendInput::Params::Create(*args_));
138 EXTENSION_FUNCTION_VALIDATE(params.get());
139
140 // Registry lives on the FILE thread.
141 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
142 base::Bind(&TerminalPrivateSendInputFunction::SendInputOnFileThread,
143 this, params->pid, params->input));
144 return true;
145 }
146
SendInputOnFileThread(pid_t pid,const std::string & text)147 void TerminalPrivateSendInputFunction::SendInputOnFileThread(pid_t pid,
148 const std::string& text) {
149 bool success = chromeos::ProcessProxyRegistry::Get()->SendInput(pid, text);
150
151 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
152 base::Bind(&TerminalPrivateSendInputFunction::RespondOnUIThread, this,
153 success));
154 }
155
RespondOnUIThread(bool success)156 void TerminalPrivateSendInputFunction::RespondOnUIThread(bool success) {
157 SetResult(new base::FundamentalValue(success));
158 SendResponse(true);
159 }
160
161 TerminalPrivateCloseTerminalProcessFunction::
~TerminalPrivateCloseTerminalProcessFunction()162 ~TerminalPrivateCloseTerminalProcessFunction() {}
163
RunTerminalFunction()164 bool TerminalPrivateCloseTerminalProcessFunction::RunTerminalFunction() {
165 if (args_->GetSize() != 1)
166 return false;
167 pid_t pid;
168 if (!args_->GetInteger(0, &pid))
169 return false;
170
171 // Registry lives on the FILE thread.
172 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
173 base::Bind(&TerminalPrivateCloseTerminalProcessFunction::
174 CloseOnFileThread, this, pid));
175
176 return true;
177 }
178
CloseOnFileThread(pid_t pid)179 void TerminalPrivateCloseTerminalProcessFunction::CloseOnFileThread(pid_t pid) {
180 bool success = chromeos::ProcessProxyRegistry::Get()->CloseProcess(pid);
181
182 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
183 base::Bind(&TerminalPrivateCloseTerminalProcessFunction::
184 RespondOnUIThread, this, success));
185 }
186
RespondOnUIThread(bool success)187 void TerminalPrivateCloseTerminalProcessFunction::RespondOnUIThread(
188 bool success) {
189 SetResult(new base::FundamentalValue(success));
190 SendResponse(true);
191 }
192
193 TerminalPrivateOnTerminalResizeFunction::
~TerminalPrivateOnTerminalResizeFunction()194 ~TerminalPrivateOnTerminalResizeFunction() {}
195
RunTerminalFunction()196 bool TerminalPrivateOnTerminalResizeFunction::RunTerminalFunction() {
197 scoped_ptr<OnTerminalResize::Params> params(
198 OnTerminalResize::Params::Create(*args_));
199 EXTENSION_FUNCTION_VALIDATE(params.get());
200
201 // Registry lives on the FILE thread.
202 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
203 base::Bind(&TerminalPrivateOnTerminalResizeFunction::OnResizeOnFileThread,
204 this, params->pid, params->width, params->height));
205
206 return true;
207 }
208
OnResizeOnFileThread(pid_t pid,int width,int height)209 void TerminalPrivateOnTerminalResizeFunction::OnResizeOnFileThread(pid_t pid,
210 int width, int height) {
211 bool success = chromeos::ProcessProxyRegistry::Get()->OnTerminalResize(
212 pid, width, height);
213
214 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
215 base::Bind(&TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread,
216 this, success));
217 }
218
RespondOnUIThread(bool success)219 void TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread(bool success) {
220 SetResult(new base::FundamentalValue(success));
221 SendResponse(true);
222 }
223
224 } // namespace extensions
225