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