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 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 34 const char* GetCroshPath() { 35 if (base::chromeos::IsRunningOnChromeOS()) 36 return kCroshCommand; 37 else 38 return kStubbedCroshCommand; 39 } 40 41 const char* GetProcessCommandForName(const std::string& name) { 42 if (name == kCroshName) 43 return GetCroshPath(); 44 else 45 return NULL; 46 } 47 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 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 76 TerminalPrivateFunction::TerminalPrivateFunction() {} 77 78 TerminalPrivateFunction::~TerminalPrivateFunction() {} 79 80 bool TerminalPrivateFunction::RunImpl() { 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_, &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 126 TerminalPrivateSendInputFunction::~TerminalPrivateSendInputFunction() {} 127 128 void TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread(pid_t pid) { 129 SetResult(new base::FundamentalValue(pid)); 130 SendResponse(true); 131 } 132 133 bool 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 144 void 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 153 void TerminalPrivateSendInputFunction::RespondOnUIThread(bool success) { 154 SetResult(new base::FundamentalValue(success)); 155 SendResponse(true); 156 } 157 158 TerminalPrivateCloseTerminalProcessFunction:: 159 ~TerminalPrivateCloseTerminalProcessFunction() {} 160 161 bool 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 176 void 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 184 void TerminalPrivateCloseTerminalProcessFunction::RespondOnUIThread( 185 bool success) { 186 SetResult(new base::FundamentalValue(success)); 187 SendResponse(true); 188 } 189 190 TerminalPrivateOnTerminalResizeFunction:: 191 ~TerminalPrivateOnTerminalResizeFunction() {} 192 193 bool 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 206 void 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 216 void TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread(bool success) { 217 SetResult(new base::FundamentalValue(success)); 218 SendResponse(true); 219 } 220