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/common/child_process_host_impl.h" 20 #include "content/common/utility_messages.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "content/public/browser/content_browser_client.h" 23 #include "content/public/browser/utility_process_host_client.h" 24 #include "content/public/common/content_switches.h" 25 #include "content/public/common/process_type.h" 26 #include "ipc/ipc_switches.h" 27 #include "ui/base/ui_base_switches.h" 28 29 #if defined(OS_WIN) 30 #include "content/public/common/sandboxed_process_launcher_delegate.h" 31 #endif 32 33 namespace content { 34 35 #if defined(OS_WIN) 36 // NOTE: changes to this class need to be reviewed by the security team. 37 class UtilitySandboxedProcessLauncherDelegate 38 : public SandboxedProcessLauncherDelegate { 39 public: 40 explicit UtilitySandboxedProcessLauncherDelegate( 41 const base::FilePath& exposed_dir) : exposed_dir_(exposed_dir) {} 42 virtual ~UtilitySandboxedProcessLauncherDelegate() {} 43 44 virtual void PreSandbox(bool* disable_default_policy, 45 base::FilePath* exposed_dir) OVERRIDE { 46 *exposed_dir = exposed_dir_; 47 } 48 49 private: 50 base::FilePath exposed_dir_; 51 }; 52 #endif 53 54 55 UtilityMainThreadFactoryFunction g_utility_main_thread_factory = NULL; 56 57 UtilityProcessHost* UtilityProcessHost::Create( 58 UtilityProcessHostClient* client, 59 base::SequencedTaskRunner* client_task_runner) { 60 return new UtilityProcessHostImpl(client, client_task_runner); 61 } 62 63 void UtilityProcessHost::RegisterUtilityMainThreadFactory( 64 UtilityMainThreadFactoryFunction create) { 65 g_utility_main_thread_factory = create; 66 } 67 68 UtilityProcessHostImpl::UtilityProcessHostImpl( 69 UtilityProcessHostClient* client, 70 base::SequencedTaskRunner* client_task_runner) 71 : client_(client), 72 client_task_runner_(client_task_runner), 73 is_batch_mode_(false), 74 is_mdns_enabled_(false), 75 no_sandbox_(false), 76 #if defined(OS_LINUX) 77 child_flags_(ChildProcessHost::CHILD_ALLOW_SELF), 78 #else 79 child_flags_(ChildProcessHost::CHILD_NORMAL), 80 #endif 81 started_(false) { 82 } 83 84 UtilityProcessHostImpl::~UtilityProcessHostImpl() { 85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 86 if (is_batch_mode_) 87 EndBatchMode(); 88 } 89 90 bool UtilityProcessHostImpl::Send(IPC::Message* message) { 91 if (!StartProcess()) 92 return false; 93 94 return process_->Send(message); 95 } 96 97 bool UtilityProcessHostImpl::StartBatchMode() { 98 CHECK(!is_batch_mode_); 99 is_batch_mode_ = StartProcess(); 100 Send(new UtilityMsg_BatchMode_Started()); 101 return is_batch_mode_; 102 } 103 104 void UtilityProcessHostImpl::EndBatchMode() { 105 CHECK(is_batch_mode_); 106 is_batch_mode_ = false; 107 Send(new UtilityMsg_BatchMode_Finished()); 108 } 109 110 void UtilityProcessHostImpl::SetExposedDir(const base::FilePath& dir) { 111 exposed_dir_ = dir; 112 } 113 114 void UtilityProcessHostImpl::EnableMDns() { 115 is_mdns_enabled_ = true; 116 } 117 118 void UtilityProcessHostImpl::DisableSandbox() { 119 no_sandbox_ = true; 120 } 121 122 const ChildProcessData& UtilityProcessHostImpl::GetData() { 123 return process_->GetData(); 124 } 125 126 #if defined(OS_POSIX) 127 128 void UtilityProcessHostImpl::SetEnv(const base::EnvironmentMap& env) { 129 env_ = env; 130 } 131 132 #endif // OS_POSIX 133 134 bool UtilityProcessHostImpl::StartProcess() { 135 if (started_) 136 return true; 137 started_ = true; 138 139 if (is_batch_mode_) 140 return true; 141 142 // Name must be set or metrics_service will crash in any test which 143 // launches a UtilityProcessHost. 144 process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_UTILITY, this)); 145 process_->SetName(ASCIIToUTF16("utility process")); 146 147 std::string channel_id = process_->GetHost()->CreateChannel(); 148 if (channel_id.empty()) 149 return false; 150 151 if (RenderProcessHost::run_renderer_in_process()) { 152 DCHECK(g_utility_main_thread_factory); 153 // See comment in RenderProcessHostImpl::Init() for the background on why we 154 // support single process mode this way. 155 in_process_thread_.reset(g_utility_main_thread_factory(channel_id)); 156 in_process_thread_->Start(); 157 } else { 158 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 159 int child_flags = child_flags_; 160 161 #if defined(OS_POSIX) 162 bool has_cmd_prefix = browser_command_line.HasSwitch( 163 switches::kUtilityCmdPrefix); 164 165 // When running under gdb, forking /proc/self/exe ends up forking the gdb 166 // executable instead of Chromium. It is almost safe to assume that no 167 // updates will happen while a developer is running with 168 // |switches::kUtilityCmdPrefix|. See ChildProcessHost::GetChildPath() for 169 // a similar case with Valgrind. 170 if (has_cmd_prefix) 171 child_flags = ChildProcessHost::CHILD_NORMAL; 172 #endif 173 174 base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags); 175 if (exe_path.empty()) { 176 NOTREACHED() << "Unable to get utility process binary name."; 177 return false; 178 } 179 180 CommandLine* cmd_line = new CommandLine(exe_path); 181 cmd_line->AppendSwitchASCII(switches::kProcessType, 182 switches::kUtilityProcess); 183 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); 184 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); 185 cmd_line->AppendSwitchASCII(switches::kLang, locale); 186 187 if (no_sandbox_ || browser_command_line.HasSwitch(switches::kNoSandbox)) 188 cmd_line->AppendSwitch(switches::kNoSandbox); 189 #if defined(OS_MACOSX) 190 if (browser_command_line.HasSwitch(switches::kEnableSandboxLogging)) 191 cmd_line->AppendSwitch(switches::kEnableSandboxLogging); 192 #endif 193 if (browser_command_line.HasSwitch(switches::kDebugPluginLoading)) 194 cmd_line->AppendSwitch(switches::kDebugPluginLoading); 195 196 #if defined(OS_POSIX) 197 if (has_cmd_prefix) { 198 // Launch the utility child process with some prefix 199 // (usually "xterm -e gdb --args"). 200 cmd_line->PrependWrapper(browser_command_line.GetSwitchValueNative( 201 switches::kUtilityCmdPrefix)); 202 } 203 204 if (!exposed_dir_.empty()) { 205 cmd_line->AppendSwitchPath(switches::kUtilityProcessAllowedDir, 206 exposed_dir_); 207 } 208 #endif 209 210 if (is_mdns_enabled_) 211 cmd_line->AppendSwitch(switches::kUtilityProcessEnableMDns); 212 213 bool use_zygote = false; 214 215 #if defined(OS_LINUX) 216 // The Linux sandbox does not support granting access to a single directory, 217 // so we need to bypass the zygote in that case. 218 use_zygote = !no_sandbox_ && exposed_dir_.empty(); 219 #endif 220 221 process_->Launch( 222 #if defined(OS_WIN) 223 new UtilitySandboxedProcessLauncherDelegate(exposed_dir_), 224 #elif defined(OS_POSIX) 225 use_zygote, 226 env_, 227 #endif 228 cmd_line); 229 } 230 231 return true; 232 } 233 234 bool UtilityProcessHostImpl::OnMessageReceived(const IPC::Message& message) { 235 client_task_runner_->PostTask( 236 FROM_HERE, 237 base::Bind(base::IgnoreResult( 238 &UtilityProcessHostClient::OnMessageReceived), client_.get(), 239 message)); 240 return true; 241 } 242 243 void UtilityProcessHostImpl::OnProcessCrashed(int exit_code) { 244 client_task_runner_->PostTask( 245 FROM_HERE, 246 base::Bind(&UtilityProcessHostClient::OnProcessCrashed, client_.get(), 247 exit_code)); 248 } 249 250 } // namespace content 251