| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 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/child_process_launcher.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/scoped_ptr.h" | |
| 10 #include "base/thread.h" | |
| 11 #include "chrome/browser/chrome_thread.h" | |
| 12 #include "chrome/common/chrome_descriptors.h" | |
| 13 #include "chrome/common/chrome_switches.h" | |
| 14 #include "chrome/common/process_watcher.h" | |
| 15 #include "chrome/common/result_codes.h" | |
| 16 | |
| 17 #if defined(OS_WIN) | |
| 18 #include "chrome/common/sandbox_policy.h" | |
| 19 #elif defined(OS_LINUX) | |
| 20 #include "base/singleton.h" | |
| 21 #include "chrome/browser/crash_handler_host_linux.h" | |
| 22 #include "chrome/browser/zygote_host_linux.h" | |
| 23 #include "chrome/browser/renderer_host/render_sandbox_host_linux.h" | |
| 24 #endif | |
| 25 | |
| 26 #if defined(OS_MACOSX) | |
| 27 #include "chrome/browser/mach_broker_mac.h" | |
| 28 #endif | |
| 29 | |
| 30 #if defined(OS_POSIX) | |
| 31 #include "base/global_descriptors_posix.h" | |
| 32 #endif | |
| 33 | |
| 34 // TODO(eroman): Debugging helper to make strings show up in mini-dumps. | |
| 35 // Remove after done investigating 40447. | |
| 36 class StackString { | |
| 37 public: | |
| 38 explicit StackString(const std::wstring& str) { | |
| 39 length_ = str.size(); | |
| 40 memcpy(&buffer_[0], str.data(), | |
| 41 std::min(sizeof(wchar_t) * str.length(), | |
| 42 sizeof(buffer_))); | |
| 43 } | |
| 44 | |
| 45 std::wstring ToString() { | |
| 46 return std::wstring(buffer_, length_); | |
| 47 } | |
| 48 | |
| 49 ~StackString() { | |
| 50 // Hack to make sure compiler doesn't optimize us away. | |
| 51 if (ToString() != ToString()) | |
| 52 LOG(INFO) << ToString(); | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 wchar_t buffer_[128]; | |
| 57 size_t length_; | |
| 58 }; | |
| 59 | |
| 60 // Having the functionality of ChildProcessLauncher be in an internal | |
| 61 // ref counted object allows us to automatically terminate the process when the | |
| 62 // parent class destructs, while still holding on to state that we need. | |
| 63 class ChildProcessLauncher::Context | |
| 64 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { | |
| 65 public: | |
| 66 Context() | |
| 67 : starting_(true) | |
| 68 #if defined(OS_LINUX) | |
| 69 , zygote_(false) | |
| 70 #endif | |
| 71 { | |
| 72 } | |
| 73 | |
| 74 void Launch( | |
| 75 #if defined(OS_WIN) | |
| 76 const FilePath& exposed_dir, | |
| 77 #elif defined(OS_POSIX) | |
| 78 bool use_zygote, | |
| 79 const base::environment_vector& environ, | |
| 80 int ipcfd, | |
| 81 #endif | |
| 82 CommandLine* cmd_line, | |
| 83 Client* client) { | |
| 84 client_ = client; | |
| 85 | |
| 86 CHECK(ChromeThread::GetCurrentThreadIdentifier(&client_thread_id_)); | |
| 87 | |
| 88 ChromeThread::PostTask( | |
| 89 ChromeThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 90 NewRunnableMethod( | |
| 91 this, | |
| 92 &Context::LaunchInternal, | |
| 93 #if defined(OS_WIN) | |
| 94 exposed_dir, | |
| 95 #elif defined(POSIX) | |
| 96 use_zygote, | |
| 97 environ, | |
| 98 ipcfd, | |
| 99 #endif | |
| 100 cmd_line)); | |
| 101 } | |
| 102 | |
| 103 void ResetClient() { | |
| 104 // No need for locking as this function gets called on the same thread that | |
| 105 // client_ would be used. | |
| 106 CHECK(ChromeThread::CurrentlyOn(client_thread_id_)); | |
| 107 client_ = NULL; | |
| 108 } | |
| 109 | |
| 110 private: | |
| 111 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; | |
| 112 friend class ChildProcessLauncher; | |
| 113 | |
| 114 ~Context() { | |
| 115 Terminate(); | |
| 116 } | |
| 117 | |
| 118 void LaunchInternal( | |
| 119 #if defined(OS_WIN) | |
| 120 const FilePath& exposed_dir, | |
| 121 #elif defined(OS_POSIX) | |
| 122 bool use_zygote, | |
| 123 const base::environment_vector& env, | |
| 124 int ipcfd, | |
| 125 #endif | |
| 126 CommandLine* cmd_line) { | |
| 127 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); | |
| 128 base::ProcessHandle handle = base::kNullProcessHandle; | |
| 129 #if defined(OS_WIN) | |
| 130 // TODO(eroman): Remove after done investigating 40447. | |
| 131 StackString stack_command_line(cmd_line->command_line_string()); | |
| 132 // This line might crash, since it calls the string copy-constructor: | |
| 133 StackString stack_program(cmd_line->program()); | |
| 134 | |
| 135 handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir); | |
| 136 #elif defined(OS_POSIX) | |
| 137 | |
| 138 #if defined(OS_LINUX) | |
| 139 if (use_zygote) { | |
| 140 base::GlobalDescriptors::Mapping mapping; | |
| 141 mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd)); | |
| 142 const int crash_signal_fd = | |
| 143 Singleton<RendererCrashHandlerHostLinux>()->GetDeathSignalSocket(); | |
| 144 if (crash_signal_fd >= 0) { | |
| 145 mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal, | |
| 146 crash_signal_fd)); | |
| 147 } | |
| 148 handle = Singleton<ZygoteHost>()->ForkRenderer(cmd_line->argv(), mapping); | |
| 149 } else | |
| 150 // Fall through to the normal posix case below when we're not zygoting. | |
| 151 #endif | |
| 152 { | |
| 153 base::file_handle_mapping_vector fds_to_map; | |
| 154 fds_to_map.push_back(std::make_pair( | |
| 155 ipcfd, | |
| 156 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); | |
| 157 | |
| 158 #if defined(OS_LINUX) | |
| 159 // On Linux, we need to add some extra file descriptors for crash handling | |
| 160 // and the sandbox. | |
| 161 bool is_renderer = | |
| 162 cmd_line->GetSwitchValueASCII(switches::kProcessType) == | |
| 163 switches::kRendererProcess; | |
| 164 bool is_plugin = | |
| 165 cmd_line->GetSwitchValueASCII(switches::kProcessType) == | |
| 166 switches::kPluginProcess; | |
| 167 | |
| 168 if (is_renderer || is_plugin) { | |
| 169 int crash_signal_fd; | |
| 170 if (is_renderer) { | |
| 171 crash_signal_fd = Singleton<RendererCrashHandlerHostLinux>()-> | |
| 172 GetDeathSignalSocket(); | |
| 173 } else { | |
| 174 crash_signal_fd = Singleton<PluginCrashHandlerHostLinux>()-> | |
| 175 GetDeathSignalSocket(); | |
| 176 } | |
| 177 if (crash_signal_fd >= 0) { | |
| 178 fds_to_map.push_back(std::make_pair( | |
| 179 crash_signal_fd, | |
| 180 kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor)); | |
| 181 } | |
| 182 } | |
| 183 if (is_renderer) { | |
| 184 const int sandbox_fd = | |
| 185 Singleton<RenderSandboxHostLinux>()->GetRendererSocket(); | |
| 186 fds_to_map.push_back(std::make_pair( | |
| 187 sandbox_fd, | |
| 188 kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); | |
| 189 } | |
| 190 #endif // defined(OS_LINUX) | |
| 191 | |
| 192 // Actually launch the app. | |
| 193 bool launched; | |
| 194 #if defined(OS_MACOSX) | |
| 195 task_t child_task; | |
| 196 launched = base::LaunchAppAndGetTask( | |
| 197 cmd_line->argv(), env, fds_to_map, false, &child_task, &handle); | |
| 198 if (launched && child_task != MACH_PORT_NULL) { | |
| 199 MachBroker::instance()->RegisterPid( | |
| 200 handle, | |
| 201 MachBroker::MachInfo().SetTask(child_task)); | |
| 202 } | |
| 203 #else | |
| 204 launched = base::LaunchApp(cmd_line->argv(), env, fds_to_map, | |
| 205 /* wait= */false, &handle); | |
| 206 #endif | |
| 207 if (!launched) | |
| 208 handle = base::kNullProcessHandle; | |
| 209 } | |
| 210 #endif // else defined(OS_POSIX) | |
| 211 | |
| 212 ChromeThread::PostTask( | |
| 213 client_thread_id_, FROM_HERE, | |
| 214 NewRunnableMethod( | |
| 215 this, | |
| 216 &ChildProcessLauncher::Context::Notify, | |
| 217 #if defined(OS_LINUX) | |
| 218 use_zygote, | |
| 219 #endif | |
| 220 handle)); | |
| 221 } | |
| 222 | |
| 223 void Notify( | |
| 224 #if defined(OS_LINUX) | |
| 225 bool zygote, | |
| 226 #endif | |
| 227 base::ProcessHandle handle) { | |
| 228 starting_ = false; | |
| 229 process_.set_handle(handle); | |
| 230 #if defined(OS_LINUX) | |
| 231 zygote_ = zygote; | |
| 232 #endif | |
| 233 if (client_) { | |
| 234 client_->OnProcessLaunched(); | |
| 235 } else { | |
| 236 Terminate(); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 void Terminate() { | |
| 241 if (!process_.handle()) | |
| 242 return; | |
| 243 | |
| 244 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So | |
| 245 // don't this on the UI/IO threads. | |
| 246 ChromeThread::PostTask( | |
| 247 ChromeThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 248 NewRunnableFunction( | |
| 249 &ChildProcessLauncher::Context::TerminateInternal, | |
| 250 #if defined(OS_LINUX) | |
| 251 zygote_, | |
| 252 #endif | |
| 253 process_.handle())); | |
| 254 process_.set_handle(base::kNullProcessHandle); | |
| 255 } | |
| 256 | |
| 257 static void TerminateInternal( | |
| 258 #if defined(OS_LINUX) | |
| 259 bool zygote, | |
| 260 #endif | |
| 261 base::ProcessHandle handle) { | |
| 262 base::Process process(handle); | |
| 263 // Client has gone away, so just kill the process. Using exit code 0 | |
| 264 // means that UMA won't treat this as a crash. | |
| 265 process.Terminate(ResultCodes::NORMAL_EXIT); | |
| 266 // On POSIX, we must additionally reap the child. | |
| 267 #if defined(OS_POSIX) | |
| 268 #if defined(OS_LINUX) | |
| 269 if (zygote) { | |
| 270 // If the renderer was created via a zygote, we have to proxy the reaping | |
| 271 // through the zygote process. | |
| 272 Singleton<ZygoteHost>()->EnsureProcessTerminated(handle); | |
| 273 } else | |
| 274 #endif // OS_LINUX | |
| 275 { | |
| 276 ProcessWatcher::EnsureProcessTerminated(handle); | |
| 277 } | |
| 278 #endif // OS_POSIX | |
| 279 process.Close(); | |
| 280 } | |
| 281 | |
| 282 Client* client_; | |
| 283 ChromeThread::ID client_thread_id_; | |
| 284 base::Process process_; | |
| 285 bool starting_; | |
| 286 | |
| 287 #if defined(OS_LINUX) | |
| 288 bool zygote_; | |
| 289 #endif | |
| 290 }; | |
| 291 | |
| 292 | |
| 293 ChildProcessLauncher::ChildProcessLauncher( | |
| 294 #if defined(OS_WIN) | |
| 295 const FilePath& exposed_dir, | |
| 296 #elif defined(OS_POSIX) | |
| 297 bool use_zygote, | |
| 298 const base::environment_vector& environ, | |
| 299 int ipcfd, | |
| 300 #endif | |
| 301 CommandLine* cmd_line, | |
| 302 Client* client) { | |
| 303 context_ = new Context(); | |
| 304 context_->Launch( | |
| 305 #if defined(OS_WIN) | |
| 306 exposed_dir, | |
| 307 #elif defined(OS_POSIX) | |
| 308 use_zygote, | |
| 309 environ, | |
| 310 ipcfd, | |
| 311 #endif | |
| 312 cmd_line, | |
| 313 client); | |
| 314 } | |
| 315 | |
| 316 ChildProcessLauncher::~ChildProcessLauncher() { | |
| 317 context_->ResetClient(); | |
| 318 } | |
| 319 | |
| 320 bool ChildProcessLauncher::IsStarting() { | |
| 321 return context_->starting_; | |
| 322 } | |
| 323 | |
| 324 base::ProcessHandle ChildProcessLauncher::GetHandle() { | |
| 325 DCHECK(!context_->starting_); | |
| 326 return context_->process_.handle(); | |
| 327 } | |
| 328 | |
| 329 bool ChildProcessLauncher::DidProcessCrash() { | |
| 330 bool did_crash, child_exited; | |
| 331 base::ProcessHandle handle = context_->process_.handle(); | |
| 332 #if defined(OS_LINUX) | |
| 333 if (context_->zygote_) { | |
| 334 did_crash = Singleton<ZygoteHost>()->DidProcessCrash(handle, &child_exited); | |
| 335 } else | |
| 336 #endif | |
| 337 { | |
| 338 did_crash = base::DidProcessCrash(&child_exited, handle); | |
| 339 } | |
| 340 | |
| 341 // POSIX: If the process crashed, then the kernel closed the socket for it | |
| 342 // and so the child has already died by the time we get here. Since | |
| 343 // DidProcessCrash called waitpid with WNOHANG, it'll reap the process. | |
| 344 // However, if DidProcessCrash didn't reap the child, we'll need to in | |
| 345 // Terminate via ProcessWatcher. So we can't close the handle here. | |
| 346 if (child_exited) | |
| 347 context_->process_.Close(); | |
| 348 | |
| 349 return did_crash; | |
| 350 } | |
| 351 | |
| 352 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { | |
| 353 DCHECK(!context_->starting_); | |
| 354 context_->process_.SetProcessBackgrounded(background); | |
| 355 } | |
| OLD | NEW |