blob: 61509657d7a37ccfa1cc2a82290164115075d139 [file] [log] [blame]
[email protected]3e55e212011-03-24 19:45:021// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]9610ef242009-11-18 02:41:262// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]df8e899b2011-02-22 22:58:225#include "content/browser/child_process_launcher.h"
[email protected]9610ef242009-11-18 02:41:266
[email protected]eb1bd832010-05-18 20:39:587#include <utility> // For std::pair.
8
[email protected]9610ef242009-11-18 02:41:269#include "base/command_line.h"
[email protected]9610ef242009-11-18 02:41:2610#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1511#include "base/memory/scoped_ptr.h"
[email protected]20305ec2011-01-21 04:55:5212#include "base/synchronization/lock.h"
[email protected]34b99632011-01-01 01:01:0613#include "base/threading/thread.h"
[email protected]df8e899b2011-02-22 22:58:2214#include "content/browser/browser_thread.h"
[email protected]b80f68432011-05-02 17:22:3015#include "content/browser/content_browser_client.h"
[email protected]acb94722011-03-18 01:33:3416#include "content/common/chrome_descriptors.h"
[email protected]25fe7fc52011-05-27 21:48:4117#include "content/common/content_switches.h"
[email protected]4dd57932011-03-17 06:06:1218#include "content/common/process_watcher.h"
19#include "content/common/result_codes.h"
[email protected]9610ef242009-11-18 02:41:2620
21#if defined(OS_WIN)
[email protected]864b1362010-08-19 03:49:3822#include "base/file_path.h"
[email protected]7c8635702011-06-09 22:35:3323#include "content/browser/handle_enumerator_win.h"
[email protected]cd5fa1a2011-05-28 18:21:4724#include "content/common/sandbox_policy.h"
[email protected]e63c4d72011-05-31 22:38:2925#elif defined(OS_MACOSX)
[email protected]ed0fbe62011-06-23 18:32:1126#include "content/browser/mach_broker_mac.h"
[email protected]e63c4d72011-05-31 22:38:2927#elif defined(OS_POSIX)
[email protected]3b63f8f42011-03-28 01:54:1528#include "base/memory/singleton.h"
[email protected]a01efd22011-03-01 00:38:3229#include "content/browser/zygote_host_linux.h"
30#include "content/browser/renderer_host/render_sandbox_host_linux.h"
[email protected]9610ef242009-11-18 02:41:2631#endif
32
[email protected]fb1277e82009-11-21 20:32:3033#if defined(OS_POSIX)
34#include "base/global_descriptors_posix.h"
35#endif
36
[email protected]9610ef242009-11-18 02:41:2637// Having the functionality of ChildProcessLauncher be in an internal
38// ref counted object allows us to automatically terminate the process when the
39// parent class destructs, while still holding on to state that we need.
40class ChildProcessLauncher::Context
41 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
42 public:
[email protected]fb1277e82009-11-21 20:32:3043 Context()
[email protected]18cbea32010-10-13 19:56:0044 : client_(NULL),
45 client_thread_id_(BrowserThread::UI),
[email protected]f3c1d3c2011-07-25 18:50:4846 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
47 exit_code_(content::RESULT_CODE_NORMAL_EXIT),
[email protected]358cb8e2011-05-25 02:12:4548 starting_(true),
49 terminate_child_on_shutdown_(true)
[email protected]e63c4d72011-05-31 22:38:2950#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]fb1277e82009-11-21 20:32:3051 , zygote_(false)
52#endif
53 {
54 }
[email protected]9610ef242009-11-18 02:41:2655
[email protected]fb1277e82009-11-21 20:32:3056 void Launch(
57#if defined(OS_WIN)
58 const FilePath& exposed_dir,
59#elif defined(OS_POSIX)
[email protected]7c4ea142010-01-26 05:15:4260 bool use_zygote,
[email protected]fb1277e82009-11-21 20:32:3061 const base::environment_vector& environ,
62 int ipcfd,
63#endif
64 CommandLine* cmd_line,
65 Client* client) {
[email protected]9610ef242009-11-18 02:41:2666 client_ = client;
67
[email protected]d04e7662010-10-10 22:24:4868 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
[email protected]9610ef242009-11-18 02:41:2669
[email protected]d04e7662010-10-10 22:24:4870 BrowserThread::PostTask(
71 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
[email protected]9610ef242009-11-18 02:41:2672 NewRunnableMethod(
[email protected]fb1277e82009-11-21 20:32:3073 this,
74 &Context::LaunchInternal,
75#if defined(OS_WIN)
76 exposed_dir,
[email protected]eb1bd832010-05-18 20:39:5877#elif defined(OS_POSIX)
[email protected]7c4ea142010-01-26 05:15:4278 use_zygote,
[email protected]3d2217d2009-11-23 21:26:4779 environ,
[email protected]fb1277e82009-11-21 20:32:3080 ipcfd,
81#endif
82 cmd_line));
[email protected]9610ef242009-11-18 02:41:2683 }
84
85 void ResetClient() {
86 // No need for locking as this function gets called on the same thread that
87 // client_ would be used.
[email protected]d04e7662010-10-10 22:24:4888 CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
[email protected]9610ef242009-11-18 02:41:2689 client_ = NULL;
90 }
91
[email protected]358cb8e2011-05-25 02:12:4592 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
93 terminate_child_on_shutdown_ = terminate_on_shutdown;
94 }
95
[email protected]9610ef242009-11-18 02:41:2696 private:
97 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
98 friend class ChildProcessLauncher;
99
100 ~Context() {
101 Terminate();
102 }
103
[email protected]fb1277e82009-11-21 20:32:30104 void LaunchInternal(
105#if defined(OS_WIN)
106 const FilePath& exposed_dir,
107#elif defined(OS_POSIX)
[email protected]7c4ea142010-01-26 05:15:42108 bool use_zygote,
[email protected]3d2217d2009-11-23 21:26:47109 const base::environment_vector& env,
[email protected]fb1277e82009-11-21 20:32:30110 int ipcfd,
111#endif
112 CommandLine* cmd_line) {
[email protected]9610ef242009-11-18 02:41:26113 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
114 base::ProcessHandle handle = base::kNullProcessHandle;
[email protected]9610ef242009-11-18 02:41:26115#if defined(OS_WIN)
[email protected]fb1277e82009-11-21 20:32:30116 handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir);
[email protected]9610ef242009-11-18 02:41:26117#elif defined(OS_POSIX)
118
[email protected]e63c4d72011-05-31 22:38:29119#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]54457f32011-04-15 22:05:29120 // On Linux, we need to add some extra file descriptors for crash handling.
121 std::string process_type =
122 cmd_line->GetSwitchValueASCII(switches::kProcessType);
[email protected]b80f68432011-05-02 17:22:30123 int crash_signal_fd =
124 content::GetContentClient()->browser()->GetCrashSignalFD(process_type);
[email protected]7c4ea142010-01-26 05:15:42125 if (use_zygote) {
[email protected]9610ef242009-11-18 02:41:26126 base::GlobalDescriptors::Mapping mapping;
127 mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd));
[email protected]9610ef242009-11-18 02:41:26128 if (crash_signal_fd >= 0) {
129 mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal,
130 crash_signal_fd));
131 }
[email protected]ce2ae442011-07-18 16:13:41132 handle = ZygoteHost::GetInstance()->ForkRequest(cmd_line->argv(),
133 mapping,
134 process_type);
[email protected]7c4ea142010-01-26 05:15:42135 } else
136 // Fall through to the normal posix case below when we're not zygoting.
[email protected]fb1277e82009-11-21 20:32:30137#endif
138 {
[email protected]9610ef242009-11-18 02:41:26139 base::file_handle_mapping_vector fds_to_map;
[email protected]fb1277e82009-11-21 20:32:30140 fds_to_map.push_back(std::make_pair(
141 ipcfd,
142 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
[email protected]9610ef242009-11-18 02:41:26143
[email protected]e63c4d72011-05-31 22:38:29144#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]54457f32011-04-15 22:05:29145 if (crash_signal_fd >= 0) {
146 fds_to_map.push_back(std::make_pair(
147 crash_signal_fd,
148 kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor));
[email protected]9610ef242009-11-18 02:41:26149 }
[email protected]b80f68432011-05-02 17:22:30150 if (process_type == switches::kRendererProcess) {
[email protected]fb1277e82009-11-21 20:32:30151 const int sandbox_fd =
[email protected]d3c6c0d72010-12-09 08:15:04152 RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
[email protected]fb1277e82009-11-21 20:32:30153 fds_to_map.push_back(std::make_pair(
154 sandbox_fd,
155 kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
156 }
[email protected]e63c4d72011-05-31 22:38:29157#endif // defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]9610ef242009-11-18 02:41:26158
[email protected]b88a7492010-09-17 12:28:32159 bool launched = false;
[email protected]c0028792010-01-12 00:39:15160#if defined(OS_MACOSX)
[email protected]b88a7492010-09-17 12:28:32161 // It is possible for the child process to die immediately after
162 // launching. To prevent leaking MachBroker map entries in this case,
[email protected]1e41c382011-07-12 18:09:46163 // lock around all of LaunchProcess(). If the child dies, the death
[email protected]b88a7492010-09-17 12:28:32164 // notification will be processed by the MachBroker after the call to
165 // AddPlaceholderForPid(), enabling proper cleanup.
166 { // begin scope for AutoLock
[email protected]dc8caba2010-12-13 16:52:35167 MachBroker* broker = MachBroker::GetInstance();
[email protected]20305ec2011-01-21 04:55:52168 base::AutoLock lock(broker->GetLock());
[email protected]b88a7492010-09-17 12:28:32169
170 // This call to |PrepareForFork()| will start the MachBroker listener
171 // thread, if it is not already running. Therefore the browser process
[email protected]1e41c382011-07-12 18:09:46172 // will be listening for Mach IPC before LaunchProcess() is called.
[email protected]b88a7492010-09-17 12:28:32173 broker->PrepareForFork();
174#endif
[email protected]1e41c382011-07-12 18:09:46175
[email protected]b88a7492010-09-17 12:28:32176 // Actually launch the app.
[email protected]1e41c382011-07-12 18:09:46177 base::LaunchOptions options;
178 options.environ = &env;
179 options.fds_to_remap = &fds_to_map;
[email protected]e5992182011-07-15 16:47:02180 launched = base::LaunchProcess(*cmd_line, options, &handle);
[email protected]1e41c382011-07-12 18:09:46181
[email protected]b88a7492010-09-17 12:28:32182#if defined(OS_MACOSX)
183 if (launched)
184 broker->AddPlaceholderForPid(handle);
185 } // end scope for AutoLock
[email protected]c0028792010-01-12 00:39:15186#endif
187 if (!launched)
[email protected]9610ef242009-11-18 02:41:26188 handle = base::kNullProcessHandle;
189 }
[email protected]c0028792010-01-12 00:39:15190#endif // else defined(OS_POSIX)
[email protected]9610ef242009-11-18 02:41:26191
[email protected]d04e7662010-10-10 22:24:48192 BrowserThread::PostTask(
[email protected]9610ef242009-11-18 02:41:26193 client_thread_id_, FROM_HERE,
194 NewRunnableMethod(
[email protected]fb1277e82009-11-21 20:32:30195 this,
[email protected]cd69619b2010-05-05 02:41:38196 &ChildProcessLauncher::Context::Notify,
[email protected]e63c4d72011-05-31 22:38:29197#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]7c4ea142010-01-26 05:15:42198 use_zygote,
[email protected]fb1277e82009-11-21 20:32:30199#endif
200 handle));
[email protected]9610ef242009-11-18 02:41:26201 }
202
[email protected]cd69619b2010-05-05 02:41:38203 void Notify(
[email protected]e63c4d72011-05-31 22:38:29204#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]fb1277e82009-11-21 20:32:30205 bool zygote,
206#endif
207 base::ProcessHandle handle) {
[email protected]9610ef242009-11-18 02:41:26208 starting_ = false;
209 process_.set_handle(handle);
[email protected]e63c4d72011-05-31 22:38:29210#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]9610ef242009-11-18 02:41:26211 zygote_ = zygote;
[email protected]fb1277e82009-11-21 20:32:30212#endif
[email protected]9610ef242009-11-18 02:41:26213 if (client_) {
214 client_->OnProcessLaunched();
215 } else {
216 Terminate();
217 }
218 }
219
220 void Terminate() {
221 if (!process_.handle())
222 return;
223
[email protected]358cb8e2011-05-25 02:12:45224 if (!terminate_child_on_shutdown_)
225 return;
226
[email protected]7c8635702011-06-09 22:35:33227#if defined(OS_WIN)
228 const CommandLine& browser_command_line =
229 *CommandLine::ForCurrentProcess();
230 if (browser_command_line.HasSwitch(switches::kAuditHandles) ||
231 browser_command_line.HasSwitch(switches::kAuditAllHandles)) {
232 scoped_refptr<content::HandleEnumerator> handle_enum(
233 new content::HandleEnumerator(process_.handle(),
234 browser_command_line.HasSwitch(switches::kAuditAllHandles)));
235 handle_enum->RunHandleEnumeration();
236 process_.set_handle(base::kNullProcessHandle);
237 return;
238 }
239#endif
240
[email protected]9610ef242009-11-18 02:41:26241 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
242 // don't this on the UI/IO threads.
[email protected]d04e7662010-10-10 22:24:48243 BrowserThread::PostTask(
244 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
[email protected]9610ef242009-11-18 02:41:26245 NewRunnableFunction(
246 &ChildProcessLauncher::Context::TerminateInternal,
[email protected]e63c4d72011-05-31 22:38:29247#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]fb1277e82009-11-21 20:32:30248 zygote_,
249#endif
250 process_.handle()));
[email protected]9610ef242009-11-18 02:41:26251 process_.set_handle(base::kNullProcessHandle);
252 }
253
[email protected]3e55e212011-03-24 19:45:02254 void SetProcessBackgrounded(bool background) {
255 DCHECK(!starting_);
256 process_.SetProcessBackgrounded(background);
257 }
258
[email protected]fb1277e82009-11-21 20:32:30259 static void TerminateInternal(
[email protected]e63c4d72011-05-31 22:38:29260#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]fb1277e82009-11-21 20:32:30261 bool zygote,
262#endif
263 base::ProcessHandle handle) {
[email protected]9610ef242009-11-18 02:41:26264 base::Process process(handle);
265 // Client has gone away, so just kill the process. Using exit code 0
266 // means that UMA won't treat this as a crash.
[email protected]1fcfb202011-07-19 19:53:14267 process.Terminate(content::RESULT_CODE_NORMAL_EXIT);
[email protected]9610ef242009-11-18 02:41:26268 // On POSIX, we must additionally reap the child.
269#if defined(OS_POSIX)
[email protected]e63c4d72011-05-31 22:38:29270#if !defined(OS_MACOSX)
[email protected]fb1277e82009-11-21 20:32:30271 if (zygote) {
[email protected]9610ef242009-11-18 02:41:26272 // If the renderer was created via a zygote, we have to proxy the reaping
273 // through the zygote process.
[email protected]d3c6c0d72010-12-09 08:15:04274 ZygoteHost::GetInstance()->EnsureProcessTerminated(handle);
[email protected]fb1277e82009-11-21 20:32:30275 } else
[email protected]e63c4d72011-05-31 22:38:29276#endif // !OS_MACOSX
[email protected]fb1277e82009-11-21 20:32:30277 {
[email protected]9610ef242009-11-18 02:41:26278 ProcessWatcher::EnsureProcessTerminated(handle);
279 }
[email protected]e36e86f2009-12-15 11:55:08280#endif // OS_POSIX
[email protected]9610ef242009-11-18 02:41:26281 process.Close();
282 }
283
[email protected]9610ef242009-11-18 02:41:26284 Client* client_;
[email protected]d04e7662010-10-10 22:24:48285 BrowserThread::ID client_thread_id_;
[email protected]9610ef242009-11-18 02:41:26286 base::Process process_;
[email protected]f3c1d3c2011-07-25 18:50:48287 base::TerminationStatus termination_status_;
288 int exit_code_;
[email protected]9610ef242009-11-18 02:41:26289 bool starting_;
[email protected]358cb8e2011-05-25 02:12:45290 // Controls whether the child process should be terminated on browser
291 // shutdown. Default behavior is to terminate the child.
292 bool terminate_child_on_shutdown_;
[email protected]fb1277e82009-11-21 20:32:30293
[email protected]e63c4d72011-05-31 22:38:29294#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]9610ef242009-11-18 02:41:26295 bool zygote_;
[email protected]fb1277e82009-11-21 20:32:30296#endif
[email protected]9610ef242009-11-18 02:41:26297};
298
299
[email protected]fb1277e82009-11-21 20:32:30300ChildProcessLauncher::ChildProcessLauncher(
301#if defined(OS_WIN)
302 const FilePath& exposed_dir,
303#elif defined(OS_POSIX)
[email protected]7c4ea142010-01-26 05:15:42304 bool use_zygote,
[email protected]fb1277e82009-11-21 20:32:30305 const base::environment_vector& environ,
306 int ipcfd,
[email protected]9610ef242009-11-18 02:41:26307#endif
[email protected]fb1277e82009-11-21 20:32:30308 CommandLine* cmd_line,
309 Client* client) {
310 context_ = new Context();
311 context_->Launch(
312#if defined(OS_WIN)
313 exposed_dir,
314#elif defined(OS_POSIX)
[email protected]7c4ea142010-01-26 05:15:42315 use_zygote,
[email protected]fb1277e82009-11-21 20:32:30316 environ,
317 ipcfd,
318#endif
319 cmd_line,
320 client);
[email protected]9610ef242009-11-18 02:41:26321}
322
323ChildProcessLauncher::~ChildProcessLauncher() {
324 context_->ResetClient();
325}
326
327bool ChildProcessLauncher::IsStarting() {
328 return context_->starting_;
329}
330
331base::ProcessHandle ChildProcessLauncher::GetHandle() {
332 DCHECK(!context_->starting_);
333 return context_->process_.handle();
334}
335
[email protected]443b80e2010-12-14 00:42:23336base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
337 int* exit_code) {
[email protected]cd69619b2010-05-05 02:41:38338 base::ProcessHandle handle = context_->process_.handle();
[email protected]f3c1d3c2011-07-25 18:50:48339 if (handle == base::kNullProcessHandle) {
340 // Process is already gone, so return the cached termination status.
341 if (exit_code)
342 *exit_code = context_->exit_code_;
343 return context_->termination_status_;
344 }
[email protected]e63c4d72011-05-31 22:38:29345#if defined(OS_POSIX) && !defined(OS_MACOSX)
[email protected]cd69619b2010-05-05 02:41:38346 if (context_->zygote_) {
[email protected]f3c1d3c2011-07-25 18:50:48347 context_->termination_status_ = ZygoteHost::GetInstance()->
348 GetTerminationStatus(handle, &context_->exit_code_);
[email protected]cd69619b2010-05-05 02:41:38349 } else
350#endif
351 {
[email protected]f3c1d3c2011-07-25 18:50:48352 context_->termination_status_ =
353 base::GetTerminationStatus(handle, &context_->exit_code_);
[email protected]cd69619b2010-05-05 02:41:38354 }
355
[email protected]f3c1d3c2011-07-25 18:50:48356 if (exit_code)
357 *exit_code = context_->exit_code_;
358
[email protected]443b80e2010-12-14 00:42:23359 // POSIX: If the process crashed, then the kernel closed the socket
360 // for it and so the child has already died by the time we get
361 // here. Since GetTerminationStatus called waitpid with WNOHANG,
362 // it'll reap the process. However, if GetTerminationStatus didn't
363 // reap the child (because it was still running), we'll need to
[email protected]cd69619b2010-05-05 02:41:38364 // Terminate via ProcessWatcher. So we can't close the handle here.
[email protected]f3c1d3c2011-07-25 18:50:48365 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
[email protected]cd69619b2010-05-05 02:41:38366 context_->process_.Close();
367
[email protected]f3c1d3c2011-07-25 18:50:48368 return context_->termination_status_;
[email protected]9610ef242009-11-18 02:41:26369}
370
371void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
[email protected]3e55e212011-03-24 19:45:02372 BrowserThread::PostTask(
373 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
374 NewRunnableMethod(
375 context_.get(),
376 &ChildProcessLauncher::Context::SetProcessBackgrounded,
377 background));
[email protected]9610ef242009-11-18 02:41:26378}
[email protected]358cb8e2011-05-25 02:12:45379
380void ChildProcessLauncher::SetTerminateChildOnShutdown(
381 bool terminate_on_shutdown) {
382 if (context_)
383 context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
384}