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 "content/browser/utility_process_host_impl.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/command_line.h" 10 #include "base/lazy_instance.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/run_loop.h" 13 #include "base/sequenced_task_runner.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/synchronization/lock.h" 16 #include "base/synchronization/waitable_event.h" 17 #include "content/browser/browser_child_process_host_impl.h" 18 #include "content/browser/renderer_host/render_process_host_impl.h" 19 #include "content/child/child_process.h" 20 #include "content/common/child_process_host_impl.h" 21 #include "content/common/utility_messages.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/content_browser_client.h" 24 #include "content/public/browser/utility_process_host_client.h" 25 #include "content/public/common/content_switches.h" 26 #include "content/public/common/process_type.h" 27 #include "content/utility/utility_thread_impl.h" 28 #include "ipc/ipc_switches.h" 29 #include "ui/base/ui_base_switches.h" 30 31 #if defined(OS_WIN) 32 #include "content/public/common/sandboxed_process_launcher_delegate.h" 33 #endif 34 35 namespace content { 36 37 #if defined(OS_WIN) 38 // NOTE: changes to this class need to be reviewed by the security team. 39 class UtilitySandboxedProcessLauncherDelegate 40 : public SandboxedProcessLauncherDelegate { 41 public: 42 explicit UtilitySandboxedProcessLauncherDelegate( 43 const base::FilePath& exposed_dir) : exposed_dir_(exposed_dir) {} 44 virtual ~UtilitySandboxedProcessLauncherDelegate() {} 45 46 virtual void PreSandbox(bool* disable_default_policy, 47 base::FilePath* exposed_dir) OVERRIDE { 48 *exposed_dir = exposed_dir_; 49 } 50 51 private: 52 base::FilePath exposed_dir_; 53 }; 54 #endif 55 56 // We want to ensure there's only one utility thread running at a time, as there 57 // are many globals used in the utility process. 58 static base::LazyInstance<base::Lock> g_one_utility_thread_lock; 59 60 // Single process not supported in multiple dll mode currently. 61 #if !defined(CHROME_MULTIPLE_DLL) 62 class UtilityMainThread : public base::Thread { 63 public: 64 UtilityMainThread(const std::string& channel_id) 65 : Thread("Chrome_InProcUtilityThread"), 66 channel_id_(channel_id) { 67 } 68 69 virtual ~UtilityMainThread() { 70 Stop(); 71 } 72 73 private: 74 // base::Thread implementation: 75 virtual void Init() OVERRIDE { 76 // We need to return right away or else the main thread that started us will 77 // hang. 78 base::MessageLoop::current()->PostTask( 79 FROM_HERE, 80 base::Bind(&UtilityMainThread::InitInternal, base::Unretained(this))); 81 } 82 83 virtual void CleanUp() OVERRIDE { 84 child_process_.reset(); 85 86 // See comment in RendererMainThread. 87 SetThreadWasQuitProperly(true); 88 g_one_utility_thread_lock.Get().Release(); 89 } 90 91 void InitInternal() { 92 g_one_utility_thread_lock.Get().Acquire(); 93 child_process_.reset(new ChildProcess()); 94 child_process_->set_main_thread(new UtilityThreadImpl(channel_id_)); 95 } 96 97 std::string channel_id_; 98 scoped_ptr<ChildProcess> child_process_; 99 100 DISALLOW_COPY_AND_ASSIGN(UtilityMainThread); 101 }; 102 #endif // !CHROME_MULTIPLE_DLL 103 104 UtilityProcessHost* UtilityProcessHost::Create( 105 UtilityProcessHostClient* client, 106 base::SequencedTaskRunner* client_task_runner) { 107 return new UtilityProcessHostImpl(client, client_task_runner); 108 } 109 110 UtilityProcessHostImpl::UtilityProcessHostImpl( 111 UtilityProcessHostClient* client, 112 base::SequencedTaskRunner* client_task_runner) 113 : client_(client), 114 client_task_runner_(client_task_runner), 115 is_batch_mode_(false), 116 is_mdns_enabled_(false), 117 no_sandbox_(false), 118 #if defined(OS_LINUX) 119 child_flags_(ChildProcessHost::CHILD_ALLOW_SELF), 120 #else 121 child_flags_(ChildProcessHost::CHILD_NORMAL), 122 #endif 123 use_linux_zygote_(false), 124 started_(false) { 125 } 126 127 UtilityProcessHostImpl::~UtilityProcessHostImpl() { 128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 129 if (is_batch_mode_) 130 EndBatchMode(); 131 } 132 133 bool UtilityProcessHostImpl::Send(IPC::Message* message) { 134 if (!StartProcess()) 135 return false; 136 137 return process_->Send(message); 138 } 139 140 bool UtilityProcessHostImpl::StartBatchMode() { 141 CHECK(!is_batch_mode_); 142 is_batch_mode_ = StartProcess(); 143 Send(new UtilityMsg_BatchMode_Started()); 144 return is_batch_mode_; 145 } 146 147 void UtilityProcessHostImpl::EndBatchMode() { 148 CHECK(is_batch_mode_); 149 is_batch_mode_ = false; 150 Send(new UtilityMsg_BatchMode_Finished()); 151 } 152 153 void UtilityProcessHostImpl::SetExposedDir(const base::FilePath& dir) { 154 exposed_dir_ = dir; 155 } 156 157 void UtilityProcessHostImpl::EnableMDns() { 158 is_mdns_enabled_ = true; 159 } 160 161 void UtilityProcessHostImpl::DisableSandbox() { 162 no_sandbox_ = true; 163 } 164 165 void UtilityProcessHostImpl::EnableZygote() { 166 use_linux_zygote_ = true; 167 } 168 169 const ChildProcessData& UtilityProcessHostImpl::GetData() { 170 return process_->GetData(); 171 } 172 173 #if defined(OS_POSIX) 174 175 void UtilityProcessHostImpl::SetEnv(const base::EnvironmentVector& env) { 176 env_ = env; 177 } 178 179 #endif // OS_POSIX 180 181 bool UtilityProcessHostImpl::StartProcess() { 182 if (started_) 183 return true; 184 started_ = true; 185 186 if (is_batch_mode_) 187 return true; 188 189 // Name must be set or metrics_service will crash in any test which 190 // launches a UtilityProcessHost. 191 process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_UTILITY, this)); 192 process_->SetName(ASCIIToUTF16("utility process")); 193 194 std::string channel_id = process_->GetHost()->CreateChannel(); 195 if (channel_id.empty()) 196 return false; 197 198 // Single process not supported in multiple dll mode currently. 199 #if !defined(CHROME_MULTIPLE_DLL) 200 if (RenderProcessHost::run_renderer_in_process()) { 201 // See comment in RenderProcessHostImpl::Init() for the background on why we 202 // support single process mode this way. 203 in_process_thread_.reset(new UtilityMainThread(channel_id)); 204 in_process_thread_->Start(); 205 } else 206 #endif // !CHROME_MULTIPLE_DLL 207 { 208 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 209 int child_flags = child_flags_; 210 211 #if defined(OS_POSIX) 212 bool has_cmd_prefix = browser_command_line.HasSwitch( 213 switches::kUtilityCmdPrefix); 214 215 // When running under gdb, forking /proc/self/exe ends up forking the gdb 216 // executable instead of Chromium. It is almost safe to assume that no 217 // updates will happen while a developer is running with 218 // |switches::kUtilityCmdPrefix|. See ChildProcessHost::GetChildPath() for 219 // a similar case with Valgrind. 220 if (has_cmd_prefix) 221 child_flags = ChildProcessHost::CHILD_NORMAL; 222 #endif 223 224 base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags); 225 if (exe_path.empty()) { 226 NOTREACHED() << "Unable to get utility process binary name."; 227 return false; 228 } 229 230 CommandLine* cmd_line = new CommandLine(exe_path); 231 cmd_line->AppendSwitchASCII(switches::kProcessType, 232 switches::kUtilityProcess); 233 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); 234 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); 235 cmd_line->AppendSwitchASCII(switches::kLang, locale); 236 237 if (no_sandbox_ || browser_command_line.HasSwitch(switches::kNoSandbox)) 238 cmd_line->AppendSwitch(switches::kNoSandbox); 239 #if defined(OS_MACOSX) 240 if (browser_command_line.HasSwitch(switches::kEnableSandboxLogging)) 241 cmd_line->AppendSwitch(switches::kEnableSandboxLogging); 242 #endif 243 if (browser_command_line.HasSwitch(switches::kDebugPluginLoading)) 244 cmd_line->AppendSwitch(switches::kDebugPluginLoading); 245 246 #if defined(OS_POSIX) 247 // TODO(port): Sandbox this on Linux. Also, zygote this to work with 248 // Linux updating. 249 if (has_cmd_prefix) { 250 // launch the utility child process with some prefix (usually "xterm -e gdb 251 // --args"). 252 cmd_line->PrependWrapper(browser_command_line.GetSwitchValueNative( 253 switches::kUtilityCmdPrefix)); 254 } 255 256 cmd_line->AppendSwitchPath(switches::kUtilityProcessAllowedDir, exposed_dir_); 257 #endif 258 259 if (is_mdns_enabled_) 260 cmd_line->AppendSwitch(switches::kUtilityProcessEnableMDns); 261 262 bool use_zygote = false; 263 264 #if defined(OS_LINUX) 265 use_zygote = !no_sandbox_ && use_linux_zygote_; 266 #endif 267 268 process_->Launch( 269 #if defined(OS_WIN) 270 new UtilitySandboxedProcessLauncherDelegate(exposed_dir_), 271 #elif defined(OS_POSIX) 272 use_zygote, 273 env_, 274 #endif 275 cmd_line); 276 } 277 278 return true; 279 } 280 281 bool UtilityProcessHostImpl::OnMessageReceived(const IPC::Message& message) { 282 client_task_runner_->PostTask( 283 FROM_HERE, 284 base::Bind(base::IgnoreResult( 285 &UtilityProcessHostClient::OnMessageReceived), client_.get(), 286 message)); 287 return true; 288 } 289 290 void UtilityProcessHostImpl::OnProcessCrashed(int exit_code) { 291 client_task_runner_->PostTask( 292 FROM_HERE, 293 base::Bind(&UtilityProcessHostClient::OnProcessCrashed, client_.get(), 294 exit_code)); 295 } 296 297 } // namespace content 298