terminal_private_api.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/chromeos/chromeos_version.h"
9#include "base/json/json_writer.h"
10#include "base/values.h"
11#include "chrome/browser/extensions/api/terminal/terminal_extension_helper.h"
12#include "chrome/browser/extensions/event_names.h"
13#include "chrome/browser/extensions/event_router.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/extension_system.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/common/extensions/api/terminal_private.h"
18#include "chromeos/process_proxy/process_proxy_registry.h"
19#include "content/public/browser/browser_thread.h"
20
21namespace OnTerminalResize =
22    extensions::api::terminal_private::OnTerminalResize;
23namespace OpenTerminalProcess =
24    extensions::api::terminal_private::OpenTerminalProcess;
25namespace SendInput = extensions::api::terminal_private::SendInput;
26
27namespace {
28
29const char kCroshName[] = "crosh";
30const char kCroshCommand[] = "/usr/bin/crosh";
31// We make stubbed crosh just echo back input.
32const char kStubbedCroshCommand[] = "cat";
33
34const char* GetCroshPath() {
35  if (base::chromeos::IsRunningOnChromeOS())
36    return kCroshCommand;
37  else
38    return kStubbedCroshCommand;
39}
40
41const char* GetProcessCommandForName(const std::string& name) {
42  if (name == kCroshName)
43    return GetCroshPath();
44  else
45    return NULL;
46}
47
48void 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        extensions::event_names::kOnTerminalProcessOutput, args.Pass()));
69    extensions::ExtensionSystem::Get(profile)->event_router()->
70        DispatchEventToExtension(extension_id, event.Pass());
71  }
72}
73
74}  // namespace
75
76TerminalPrivateFunction::TerminalPrivateFunction() {}
77
78TerminalPrivateFunction::~TerminalPrivateFunction() {}
79
80bool TerminalPrivateFunction::RunImpl() {
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_, &pid,
116          base::Bind(&NotifyProcessOutput, profile_, extension_id()))) {
117    // If new process could not be opened, we return -1.
118    pid = -1;
119  }
120
121  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
122      base::Bind(&TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread,
123                 this, pid));
124}
125
126TerminalPrivateSendInputFunction::~TerminalPrivateSendInputFunction() {}
127
128void TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread(pid_t pid) {
129  SetResult(new base::FundamentalValue(pid));
130  SendResponse(true);
131}
132
133bool TerminalPrivateSendInputFunction::RunTerminalFunction() {
134  scoped_ptr<SendInput::Params> params(SendInput::Params::Create(*args_));
135  EXTENSION_FUNCTION_VALIDATE(params.get());
136
137  // Registry lives on the FILE thread.
138  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
139      base::Bind(&TerminalPrivateSendInputFunction::SendInputOnFileThread,
140                 this, params->pid, params->input));
141  return true;
142}
143
144void TerminalPrivateSendInputFunction::SendInputOnFileThread(pid_t pid,
145    const std::string& text) {
146  bool success = chromeos::ProcessProxyRegistry::Get()->SendInput(pid, text);
147
148  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
149      base::Bind(&TerminalPrivateSendInputFunction::RespondOnUIThread, this,
150      success));
151}
152
153void TerminalPrivateSendInputFunction::RespondOnUIThread(bool success) {
154  SetResult(new base::FundamentalValue(success));
155  SendResponse(true);
156}
157
158TerminalPrivateCloseTerminalProcessFunction::
159    ~TerminalPrivateCloseTerminalProcessFunction() {}
160
161bool TerminalPrivateCloseTerminalProcessFunction::RunTerminalFunction() {
162  if (args_->GetSize() != 1)
163    return false;
164  pid_t pid;
165  if (!args_->GetInteger(0, &pid))
166    return false;
167
168  // Registry lives on the FILE thread.
169  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
170      base::Bind(&TerminalPrivateCloseTerminalProcessFunction::
171                 CloseOnFileThread, this, pid));
172
173  return true;
174}
175
176void TerminalPrivateCloseTerminalProcessFunction::CloseOnFileThread(pid_t pid) {
177  bool success = chromeos::ProcessProxyRegistry::Get()->CloseProcess(pid);
178
179  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
180      base::Bind(&TerminalPrivateCloseTerminalProcessFunction::
181                 RespondOnUIThread, this, success));
182}
183
184void TerminalPrivateCloseTerminalProcessFunction::RespondOnUIThread(
185    bool success) {
186  SetResult(new base::FundamentalValue(success));
187  SendResponse(true);
188}
189
190TerminalPrivateOnTerminalResizeFunction::
191    ~TerminalPrivateOnTerminalResizeFunction() {}
192
193bool TerminalPrivateOnTerminalResizeFunction::RunTerminalFunction() {
194  scoped_ptr<OnTerminalResize::Params> params(
195      OnTerminalResize::Params::Create(*args_));
196  EXTENSION_FUNCTION_VALIDATE(params.get());
197
198  // Registry lives on the FILE thread.
199  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
200      base::Bind(&TerminalPrivateOnTerminalResizeFunction::OnResizeOnFileThread,
201                 this, params->pid, params->width, params->height));
202
203  return true;
204}
205
206void TerminalPrivateOnTerminalResizeFunction::OnResizeOnFileThread(pid_t pid,
207                                                    int width, int height) {
208  bool success = chromeos::ProcessProxyRegistry::Get()->OnTerminalResize(
209      pid, width, height);
210
211  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
212      base::Bind(&TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread,
213                 this, success));
214}
215
216void TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread(bool success) {
217  SetResult(new base::FundamentalValue(success));
218  SendResponse(true);
219}
220