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.
5 #include "content/browser/child_process_launcher.h"
7 #include <utility> // For std::pair.
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/process_util.h"
15 #include "base/synchronization/lock.h"
16 #include "base/threading/thread.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/content_browser_client.h"
19 #include "content/public/common/content_descriptors.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/common/result_codes.h"
24 #include "base/file_path.h"
25 #include "content/common/sandbox_policy.h"
26 #elif defined(OS_MACOSX)
27 #include "content/browser/mach_broker_mac.h"
28 #elif defined(OS_ANDROID)
29 #include "base/android/jni_android.h"
30 #include "content/browser/android/sandboxed_process_launcher.h"
31 #elif defined(OS_POSIX)
32 #include "base/memory/singleton.h"
33 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
34 #include "content/browser/zygote_host/zygote_host_impl_linux.h"
38 #include "base/global_descriptors_posix.h"
41 using content::BrowserThread
;
43 // Having the functionality of ChildProcessLauncher be in an internal
44 // ref counted object allows us to automatically terminate the process when the
45 // parent class destructs, while still holding on to state that we need.
46 class ChildProcessLauncher::Context
47 : public base::RefCountedThreadSafe
<ChildProcessLauncher::Context
> {
51 client_thread_id_(BrowserThread::UI
),
52 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION
),
53 exit_code_(content::RESULT_CODE_NORMAL_EXIT
),
55 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
60 terminate_child_on_shutdown_
= !CommandLine::ForCurrentProcess()->
61 HasSwitch(switches::kChildCleanExit
);
63 terminate_child_on_shutdown_
= true;
69 const FilePath
& exposed_dir
,
70 #elif defined(OS_ANDROID)
72 #elif defined(OS_POSIX)
74 const base::EnvironmentVector
& environ
,
77 CommandLine
* cmd_line
,
81 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_
));
83 #if defined(OS_ANDROID)
84 // We need to close the client end of the IPC channel to reliably detect
85 // child termination. We will close this fd after we create the child
86 // process which is asynchronous on Android.
89 BrowserThread::PostTask(
90 BrowserThread::PROCESS_LAUNCHER
, FROM_HERE
,
92 &Context::LaunchInternal
,
93 make_scoped_refptr(this),
97 #elif defined(OS_ANDROID)
99 #elif defined(OS_POSIX)
107 #if defined(OS_ANDROID)
108 static void OnSandboxedProcessStarted(
109 // |this_object| is NOT thread safe. Only use it to post a task back.
110 scoped_refptr
<Context
> this_object
,
111 BrowserThread::ID client_thread_id
,
112 base::ProcessHandle handle
) {
113 if (BrowserThread::CurrentlyOn(client_thread_id
)) {
114 // This is always invoked on the UI thread which is commonly the
115 // |client_thread_id| so we can shortcut one PostTask.
116 this_object
->Notify(handle
);
118 BrowserThread::PostTask(
119 client_thread_id
, FROM_HERE
,
121 &ChildProcessLauncher::Context::Notify
,
129 // No need for locking as this function gets called on the same thread that
130 // client_ would be used.
131 CHECK(BrowserThread::CurrentlyOn(client_thread_id_
));
135 void set_terminate_child_on_shutdown(bool terminate_on_shutdown
) {
136 terminate_child_on_shutdown_
= terminate_on_shutdown
;
140 friend class base::RefCountedThreadSafe
<ChildProcessLauncher::Context
>;
141 friend class ChildProcessLauncher
;
147 static void LaunchInternal(
148 // |this_object| is NOT thread safe. Only use it to post a task back.
149 scoped_refptr
<Context
> this_object
,
150 BrowserThread::ID client_thread_id
,
152 const FilePath
& exposed_dir
,
153 #elif defined(OS_ANDROID)
155 #elif defined(OS_POSIX)
157 const base::EnvironmentVector
& env
,
160 CommandLine
* cmd_line
) {
161 scoped_ptr
<CommandLine
> cmd_line_deleter(cmd_line
);
164 base::ProcessHandle handle
= sandbox::StartProcessWithAccess(
165 cmd_line
, exposed_dir
);
166 #elif defined(OS_ANDROID)
167 std::string process_type
=
168 cmd_line
->GetSwitchValueASCII(switches::kProcessType
);
169 std::vector
<content::FileDescriptorInfo
> files_to_register
;
170 files_to_register
.push_back(
171 content::FileDescriptorInfo(kPrimaryIPCChannel
,
172 base::FileDescriptor(ipcfd
, false)));
174 content::GetContentClient()->browser()->
175 GetAdditionalMappedFilesForChildProcess(*cmd_line
, &files_to_register
);
177 content::StartSandboxedProcess(cmd_line
->argv(), files_to_register
,
178 base::Bind(&ChildProcessLauncher::Context::OnSandboxedProcessStarted
,
179 this_object
, client_thread_id
));
181 #elif defined(OS_POSIX)
182 base::ProcessHandle handle
= base::kNullProcessHandle
;
183 // We need to close the client end of the IPC channel to reliably detect
184 // child termination.
185 file_util::ScopedFD
ipcfd_closer(&ipcfd
);
187 std::string process_type
=
188 cmd_line
->GetSwitchValueASCII(switches::kProcessType
);
189 std::vector
<content::FileDescriptorInfo
> files_to_register
;
190 files_to_register
.push_back(
191 content::FileDescriptorInfo(kPrimaryIPCChannel
,
192 base::FileDescriptor(ipcfd
, false)));
194 #if !defined(OS_MACOSX)
195 content::GetContentClient()->browser()->
196 GetAdditionalMappedFilesForChildProcess(*cmd_line
, &files_to_register
);
198 handle
= ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line
->argv(),
202 // Fall through to the normal posix case below when we're not zygoting.
203 #endif // !defined(OS_MACOSX)
205 // Convert FD mapping to FileHandleMappingVector
206 base::FileHandleMappingVector fds_to_map
;
207 for (std::vector
<content::FileDescriptorInfo
>::const_iterator
208 i
= files_to_register
.begin(); i
!= files_to_register
.end(); ++i
) {
209 const content::FileDescriptorInfo
& fd_info
= *i
;
210 fds_to_map
.push_back(std::make_pair(
212 fd_info
.id
+ base::GlobalDescriptors::kBaseDescriptor
));
215 #if !defined(OS_MACOSX)
216 if (process_type
== switches::kRendererProcess
) {
217 const int sandbox_fd
=
218 RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
219 fds_to_map
.push_back(std::make_pair(
221 kSandboxIPCChannel
+ base::GlobalDescriptors::kBaseDescriptor
));
223 #endif // defined(OS_MACOSX)
225 // Actually launch the app.
226 base::LaunchOptions options
;
227 options
.environ
= &env
;
228 options
.fds_to_remap
= &fds_to_map
;
230 #if defined(OS_MACOSX)
231 // Use synchronization to make sure that the MachBroker is ready to
232 // receive a check-in from the new process before the new process
233 // actually tries to check in.
234 base::LaunchSynchronizationHandle synchronization_handle
;
235 options
.synchronize
= &synchronization_handle
;
236 #endif // defined(OS_MACOSX)
238 bool launched
= base::LaunchProcess(*cmd_line
, options
, &handle
);
240 #if defined(OS_MACOSX)
242 MachBroker
* broker
= MachBroker::GetInstance();
244 base::AutoLock
lock(broker
->GetLock());
246 // Make sure the MachBroker is running, and inform it to expect a
247 // check-in from the new process.
248 broker
->EnsureRunning();
249 broker
->AddPlaceholderForPid(handle
);
252 // Now that the MachBroker is ready, the child may continue.
253 base::LaunchSynchronize(synchronization_handle
);
255 #endif // defined(OS_MACOSX)
258 handle
= base::kNullProcessHandle
;
260 #endif // else defined(OS_POSIX)
261 #if !defined(OS_ANDROID)
262 BrowserThread::PostTask(
263 client_thread_id
, FROM_HERE
,
267 #if defined(OS_POSIX) && !defined(OS_MACOSX)
271 #endif // !defined(OS_ANDROID)
275 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
278 base::ProcessHandle handle
) {
279 #if defined(OS_ANDROID)
280 // Finally close the ipcfd
281 file_util::ScopedFD
ipcfd_closer(&ipcfd_
);
284 process_
.set_handle(handle
);
286 LOG(ERROR
) << "Failed to launch child process";
288 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
292 client_
->OnProcessLaunched();
299 if (!process_
.handle())
302 if (!terminate_child_on_shutdown_
)
305 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
306 // don't this on the UI/IO threads.
307 BrowserThread::PostTask(
308 BrowserThread::PROCESS_LAUNCHER
, FROM_HERE
,
310 &Context::TerminateInternal
,
311 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
315 process_
.set_handle(base::kNullProcessHandle
);
318 static void SetProcessBackgrounded(base::ProcessHandle handle
,
320 base::Process
process(handle
);
321 process
.SetProcessBackgrounded(background
);
324 static void TerminateInternal(
325 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
328 base::ProcessHandle handle
) {
329 #if defined(OS_ANDROID)
330 LOG(INFO
) << "ChromeProcess: Stopping process with handle " << handle
;
331 content::StopSandboxedProcess(handle
);
333 base::Process
process(handle
);
334 // Client has gone away, so just kill the process. Using exit code 0
335 // means that UMA won't treat this as a crash.
336 process
.Terminate(content::RESULT_CODE_NORMAL_EXIT
);
337 // On POSIX, we must additionally reap the child.
338 #if defined(OS_POSIX)
339 #if !defined(OS_MACOSX)
341 // If the renderer was created via a zygote, we have to proxy the reaping
342 // through the zygote process.
343 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle
);
347 base::EnsureProcessTerminated(handle
);
351 #endif // defined(OS_ANDROID)
355 BrowserThread::ID client_thread_id_
;
356 base::Process process_
;
357 base::TerminationStatus termination_status_
;
360 // Controls whether the child process should be terminated on browser
361 // shutdown. Default behavior is to terminate the child.
362 bool terminate_child_on_shutdown_
;
363 #if defined(OS_ANDROID)
364 // The fd to close after creating the process.
366 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
372 ChildProcessLauncher::ChildProcessLauncher(
374 const FilePath
& exposed_dir
,
375 #elif defined(OS_POSIX)
377 const base::EnvironmentVector
& environ
,
380 CommandLine
* cmd_line
,
382 context_
= new Context();
386 #elif defined(OS_ANDROID)
388 #elif defined(OS_POSIX)
397 ChildProcessLauncher::~ChildProcessLauncher() {
398 context_
->ResetClient();
401 bool ChildProcessLauncher::IsStarting() {
402 return context_
->starting_
;
405 base::ProcessHandle
ChildProcessLauncher::GetHandle() {
406 DCHECK(!context_
->starting_
);
407 return context_
->process_
.handle();
410 base::TerminationStatus
ChildProcessLauncher::GetChildTerminationStatus(
412 base::ProcessHandle handle
= context_
->process_
.handle();
413 if (handle
== base::kNullProcessHandle
) {
414 // Process is already gone, so return the cached termination status.
416 *exit_code
= context_
->exit_code_
;
417 return context_
->termination_status_
;
419 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
420 if (context_
->zygote_
) {
421 context_
->termination_status_
= ZygoteHostImpl::GetInstance()->
422 GetTerminationStatus(handle
, &context_
->exit_code_
);
426 context_
->termination_status_
=
427 base::GetTerminationStatus(handle
, &context_
->exit_code_
);
431 *exit_code
= context_
->exit_code_
;
433 // POSIX: If the process crashed, then the kernel closed the socket
434 // for it and so the child has already died by the time we get
435 // here. Since GetTerminationStatus called waitpid with WNOHANG,
436 // it'll reap the process. However, if GetTerminationStatus didn't
437 // reap the child (because it was still running), we'll need to
438 // Terminate via ProcessWatcher. So we can't close the handle here.
439 if (context_
->termination_status_
!= base::TERMINATION_STATUS_STILL_RUNNING
)
440 context_
->process_
.Close();
442 return context_
->termination_status_
;
445 void ChildProcessLauncher::SetProcessBackgrounded(bool background
) {
446 BrowserThread::PostTask(
447 BrowserThread::PROCESS_LAUNCHER
, FROM_HERE
,
449 &ChildProcessLauncher::Context::SetProcessBackgrounded
,
450 GetHandle(), background
));
453 void ChildProcessLauncher::SetTerminateChildOnShutdown(
454 bool terminate_on_shutdown
) {
456 context_
->set_terminate_child_on_shutdown(terminate_on_shutdown
);