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 19 namespace terminal_private = extensions::api::terminal_private; 20 namespace OnTerminalResize = 21 extensions::api::terminal_private::OnTerminalResize; 22 namespace OpenTerminalProcess = 23 extensions::api::terminal_private::OpenTerminalProcess; 24 namespace SendInput = extensions::api::terminal_private::SendInput; 25 26 namespace { 27 28 const char kCroshName[] = "crosh"; 29 const char kCroshCommand[] = "/usr/bin/crosh"; 30 // We make stubbed crosh just echo back input. 31 const char kStubbedCroshCommand[] = "cat"; 32 33 const char* GetCroshPath() { 34 if (base::SysInfo::IsRunningOnChromeOS()) 35 return kCroshCommand; 36 else 37 return kStubbedCroshCommand; 38 } 39 40 const char* GetProcessCommandForName(const std::string& name) { 41 if (name == kCroshName) 42 return GetCroshPath(); 43 else 44 return NULL; 45 } 46 47 void 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 74 namespace extensions { 75 76 TerminalPrivateFunction::TerminalPrivateFunction() {} 77 78 TerminalPrivateFunction::~TerminalPrivateFunction() {} 79 80 bool TerminalPrivateFunction::RunAsync() { 81 return RunTerminalFunction(); 82 } 83 84 TerminalPrivateOpenTerminalProcessFunction:: 85 TerminalPrivateOpenTerminalProcessFunction() : command_(NULL) {} 86 87 TerminalPrivateOpenTerminalProcessFunction:: 88 ~TerminalPrivateOpenTerminalProcessFunction() {} 89 90 bool 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 108 void 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 127 TerminalPrivateSendInputFunction::~TerminalPrivateSendInputFunction() {} 128 129 void TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread(pid_t pid) { 130 SetResult(new base::FundamentalValue(pid)); 131 SendResponse(true); 132 } 133 134 bool 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 145 void 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 154 void TerminalPrivateSendInputFunction::RespondOnUIThread(bool success) { 155 SetResult(new base::FundamentalValue(success)); 156 SendResponse(true); 157 } 158 159 TerminalPrivateCloseTerminalProcessFunction:: 160 ~TerminalPrivateCloseTerminalProcessFunction() {} 161 162 bool 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 177 void 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 185 void TerminalPrivateCloseTerminalProcessFunction::RespondOnUIThread( 186 bool success) { 187 SetResult(new base::FundamentalValue(success)); 188 SendResponse(true); 189 } 190 191 TerminalPrivateOnTerminalResizeFunction:: 192 ~TerminalPrivateOnTerminalResizeFunction() {} 193 194 bool 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 207 void 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 217 void TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread(bool success) { 218 SetResult(new base::FundamentalValue(success)); 219 SendResponse(true); 220 } 221 222 } // namespace extensions 223