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