1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "GeckoChildProcessHost.h"
9 #include "base/command_line.h"
10 #include "base/path_service.h"
11 #include "base/string_util.h"
12 #include "chrome/common/chrome_switches.h"
13 #include "chrome/common/process_watcher.h"
14 #ifdef MOZ_WIDGET_COCOA
15 #include "chrome/common/mach_ipc_mac.h"
16 #include "base/rand_util.h"
17 #include "nsILocalFileMac.h"
20 #include "MainThreadUtils.h"
23 #include "nsXPCOMPrivate.h"
25 #include "nsExceptionHandler.h"
27 #include "nsDirectoryServiceDefs.h"
30 #include "mozilla/ClearOnShutdown.h"
31 #include "mozilla/ipc/BrowserProcessSubThread.h"
32 #include "mozilla/Omnijar.h"
36 #include "nsIWinTaskbar.h"
37 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
41 #include "nsClassHashtable.h"
42 #include "nsHashKeys.h"
43 #include "nsNativeCharsetUtils.h"
45 using mozilla::MonitorAutoLock
;
46 using mozilla::ipc::GeckoChildProcessHost
;
49 // Like its predecessor in nsExceptionHandler.cpp, this is
50 // the magic number of a file descriptor remapping we must
51 // preserve for the child process.
52 static const int kMagicAndroidSystemPropFd
= 5;
55 static const bool kLowRightsSubprocesses
=
56 // We currently only attempt to drop privileges on gonk, because we
57 // have no plugins or extensions to worry about breaking.
58 #ifdef MOZ_WIDGET_GONK
66 ShouldHaveDirectoryService()
68 return GeckoProcessType_Default
== XRE_GetProcessType();
72 struct RunnableMethodTraits
<GeckoChildProcessHost
>
74 static void RetainCallee(GeckoChildProcessHost
* obj
) { }
75 static void ReleaseCallee(GeckoChildProcessHost
* obj
) { }
80 GeckoChildProcessHost::DefaultChildPrivileges()
82 return (kLowRightsSubprocesses
?
83 base::PRIVILEGES_UNPRIVILEGED
: base::PRIVILEGES_INHERIT
);
86 GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType
,
87 ChildPrivileges aPrivileges
)
88 : ChildProcessHost(RENDER_PROCESS
), // FIXME/cjones: we should own this enum
89 mProcessType(aProcessType
),
90 mPrivileges(aPrivileges
),
91 mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
92 mProcessState(CREATING_CHANNEL
),
94 mChildProcessHandle(0)
95 #if defined(MOZ_WIDGET_COCOA)
96 , mChildTask(MACH_PORT_NULL
)
99 MOZ_COUNT_CTOR(GeckoChildProcessHost
);
102 GeckoChildProcessHost::~GeckoChildProcessHost()
107 MOZ_COUNT_DTOR(GeckoChildProcessHost
);
109 if (mChildProcessHandle
> 0)
110 ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
111 #if defined(NS_BUILD_REFCNT_LOGGING)
112 , false // don't "force"
116 #if defined(MOZ_WIDGET_COCOA)
117 if (mChildTask
!= MACH_PORT_NULL
)
118 mach_port_deallocate(mach_task_self(), mChildTask
);
124 GeckoChildProcessHost::GetPathToBinary(FilePath
& exePath
)
126 if (ShouldHaveDirectoryService()) {
127 MOZ_ASSERT(gGREBinPath
);
129 exePath
= FilePath(char16ptr_t(gGREBinPath
));
130 #elif MOZ_WIDGET_COCOA
131 nsCOMPtr
<nsIFile
> childProcPath
;
132 NS_NewLocalFile(nsDependentString(gGREBinPath
), false,
133 getter_AddRefs(childProcPath
));
134 // We need to use an App Bundle on OS X so that we can hide
135 // the dock icon. See Bug 557225.
136 childProcPath
->AppendNative(NS_LITERAL_CSTRING("plugin-container.app"));
137 childProcPath
->AppendNative(NS_LITERAL_CSTRING("Contents"));
138 childProcPath
->AppendNative(NS_LITERAL_CSTRING("MacOS"));
140 childProcPath
->GetNativePath(tempCPath
);
141 exePath
= FilePath(tempCPath
.get());
144 NS_CopyUnicodeToNative(nsDependentString(gGREBinPath
), path
);
145 exePath
= FilePath(path
.get());
149 if (exePath
.empty()) {
151 exePath
= FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
153 exePath
= FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
155 exePath
= exePath
.DirName();
158 exePath
= exePath
.AppendASCII(MOZ_CHILD_PROCESS_NAME
);
161 #ifdef MOZ_WIDGET_COCOA
162 class AutoCFTypeObject
{
164 explicit AutoCFTypeObject(CFTypeRef object
)
170 ::CFRelease(mObject
);
177 nsresult
GeckoChildProcessHost::GetArchitecturesForBinary(const char *path
, uint32_t *result
)
181 #ifdef MOZ_WIDGET_COCOA
182 CFURLRef url
= ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
187 return NS_ERROR_FAILURE
;
189 AutoCFTypeObject
autoPluginContainerURL(url
);
191 CFArrayRef pluginContainerArchs
= ::CFBundleCopyExecutableArchitecturesForURL(url
);
192 if (!pluginContainerArchs
) {
193 return NS_ERROR_FAILURE
;
195 AutoCFTypeObject
autoPluginContainerArchs(pluginContainerArchs
);
197 CFIndex pluginArchCount
= ::CFArrayGetCount(pluginContainerArchs
);
198 for (CFIndex i
= 0; i
< pluginArchCount
; i
++) {
199 CFNumberRef currentArch
= static_cast<CFNumberRef
>(::CFArrayGetValueAtIndex(pluginContainerArchs
, i
));
200 int currentArchInt
= 0;
201 if (!::CFNumberGetValue(currentArch
, kCFNumberIntType
, ¤tArchInt
)) {
204 switch (currentArchInt
) {
205 case kCFBundleExecutableArchitectureI386
:
206 *result
|= base::PROCESS_ARCH_I386
;
208 case kCFBundleExecutableArchitectureX86_64
:
209 *result
|= base::PROCESS_ARCH_X86_64
;
211 case kCFBundleExecutableArchitecturePPC
:
212 *result
|= base::PROCESS_ARCH_PPC
;
219 return (*result
? NS_OK
: NS_ERROR_FAILURE
);
221 return NS_ERROR_NOT_IMPLEMENTED
;
225 uint32_t GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type
)
227 #ifdef MOZ_WIDGET_COCOA
228 if (type
== GeckoProcessType_Plugin
) {
230 // Cache this, it shouldn't ever change.
231 static uint32_t pluginContainerArchs
= 0;
232 if (pluginContainerArchs
== 0) {
234 GetPathToBinary(exePath
);
235 nsresult rv
= GetArchitecturesForBinary(exePath
.value().c_str(), &pluginContainerArchs
);
236 NS_ASSERTION(NS_SUCCEEDED(rv
) && pluginContainerArchs
!= 0, "Getting architecture of plugin container failed!");
237 if (NS_FAILED(rv
) || pluginContainerArchs
== 0) {
238 pluginContainerArchs
= base::GetCurrentProcessArchitecture();
241 return pluginContainerArchs
;
245 return base::GetCurrentProcessArchitecture();
249 GeckoChildProcessHost::PrepareLaunch()
251 #ifdef MOZ_CRASHREPORTER
252 if (CrashReporter::GetEnabled()) {
253 CrashReporter::OOPInit();
258 if (mProcessType
== GeckoProcessType_Plugin
) {
259 InitWindowsGroupID();
265 void GeckoChildProcessHost::InitWindowsGroupID()
267 // On Win7+, pass the application user model to the child, so it can
268 // register with it. This insures windows created by the container
269 // properly group with the parent app on the Win7 taskbar.
270 nsCOMPtr
<nsIWinTaskbar
> taskbarInfo
=
271 do_GetService(NS_TASKBAR_CONTRACTID
);
273 bool isSupported
= false;
274 taskbarInfo
->GetAvailable(&isSupported
);
276 if (isSupported
&& NS_SUCCEEDED(taskbarInfo
->GetDefaultGroupId(appId
))) {
277 mGroupId
.Append(appId
);
279 mGroupId
.Assign('-');
286 GeckoChildProcessHost::SyncLaunch(std::vector
<std::string
> aExtraOpts
, int aTimeoutMs
, base::ProcessArchitecture arch
)
290 PRIntervalTime timeoutTicks
= (aTimeoutMs
> 0) ?
291 PR_MillisecondsToInterval(aTimeoutMs
) : PR_INTERVAL_NO_TIMEOUT
;
292 MessageLoop
* ioLoop
= XRE_GetIOMessageLoop();
293 NS_ASSERTION(MessageLoop::current() != ioLoop
, "sync launch from the IO thread NYI");
295 ioLoop
->PostTask(FROM_HERE
,
296 NewRunnableMethod(this,
297 &GeckoChildProcessHost::RunPerformAsyncLaunch
,
299 // NB: this uses a different mechanism than the chromium parent
301 MonitorAutoLock
lock(mMonitor
);
302 PRIntervalTime waitStart
= PR_IntervalNow();
303 PRIntervalTime current
;
305 // We'll receive several notifications, we need to exit when we
306 // have either successfully launched or have timed out.
307 while (mProcessState
!= PROCESS_CONNECTED
) {
308 // If there was an error then return it, don't wait out the timeout.
309 if (mProcessState
== PROCESS_ERROR
) {
313 lock
.Wait(timeoutTicks
);
315 if (timeoutTicks
!= PR_INTERVAL_NO_TIMEOUT
) {
316 current
= PR_IntervalNow();
317 PRIntervalTime elapsed
= current
- waitStart
;
318 if (elapsed
> timeoutTicks
) {
321 timeoutTicks
= timeoutTicks
- elapsed
;
326 return mProcessState
== PROCESS_CONNECTED
;
330 GeckoChildProcessHost::AsyncLaunch(std::vector
<std::string
> aExtraOpts
)
334 MessageLoop
* ioLoop
= XRE_GetIOMessageLoop();
335 ioLoop
->PostTask(FROM_HERE
,
336 NewRunnableMethod(this,
337 &GeckoChildProcessHost::RunPerformAsyncLaunch
,
338 aExtraOpts
, base::GetCurrentProcessArchitecture()));
340 // This may look like the sync launch wait, but we only delay as
341 // long as it takes to create the channel.
342 MonitorAutoLock
lock(mMonitor
);
343 while (mProcessState
< CHANNEL_INITIALIZED
) {
351 GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts
)
355 MessageLoop
* ioLoop
= XRE_GetIOMessageLoop();
356 ioLoop
->PostTask(FROM_HERE
,
357 NewRunnableMethod(this,
358 &GeckoChildProcessHost::RunPerformAsyncLaunch
,
359 aExtraOpts
, base::GetCurrentProcessArchitecture()));
361 MonitorAutoLock
lock(mMonitor
);
362 while (mProcessState
< PROCESS_CREATED
) {
365 MOZ_ASSERT(mProcessState
== PROCESS_ERROR
|| mChildProcessHandle
);
367 return mProcessState
< PROCESS_ERROR
;
371 GeckoChildProcessHost::InitializeChannel()
375 MonitorAutoLock
lock(mMonitor
);
376 mProcessState
= CHANNEL_INITIALIZED
;
381 GeckoChildProcessHost::Join()
385 if (!mChildProcessHandle
) {
389 // If this fails, there's nothing we can do.
390 base::KillProcess(mChildProcessHandle
, 0, /*wait*/true);
395 GeckoChildProcessHost::SetAlreadyDead()
397 mChildProcessHandle
= 0;
400 int32_t GeckoChildProcessHost::mChildCounter
= 0;
403 // Wrapper function for handling GECKO_SEPARATE_NSPR_LOGS
406 GeckoChildProcessHost::PerformAsyncLaunch(std::vector
<std::string
> aExtraOpts
, base::ProcessArchitecture arch
)
408 // If NSPR log files are not requested, we're done.
409 const char* origLogName
= PR_GetEnv("NSPR_LOG_FILE");
411 return PerformAsyncLaunchInternal(aExtraOpts
, arch
);
414 // We currently have no portable way to launch child with environment
415 // different than parent. So temporarily change NSPR_LOG_FILE so child
416 // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
417 // startup, so it's 'safe' to play with the parent's environment this way.)
418 nsAutoCString
setChildLogName("NSPR_LOG_FILE=");
419 setChildLogName
.Append(origLogName
);
421 // remember original value so we can restore it.
422 // - buffer needs to be permanently allocated for PR_SetEnv()
423 // - Note: this code is not called re-entrantly, nor are restoreOrigLogName
424 // or mChildCounter touched by any other thread, so this is safe.
425 static char* restoreOrigLogName
= 0;
426 if (!restoreOrigLogName
)
427 restoreOrigLogName
= strdup(setChildLogName
.get());
429 // Append child-specific postfix to name
430 setChildLogName
.AppendLiteral(".child-");
431 setChildLogName
.AppendInt(++mChildCounter
);
433 // Passing temporary to PR_SetEnv is ok here because env gets copied
434 // by exec, etc., to permanent storage in child when process launched.
435 PR_SetEnv(setChildLogName
.get());
436 bool retval
= PerformAsyncLaunchInternal(aExtraOpts
, arch
);
438 // Revert to original value
439 PR_SetEnv(restoreOrigLogName
);
445 GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector
<std::string
> aExtraOpts
,
446 base::ProcessArchitecture aArch
)
449 return PerformAsyncLaunch(aExtraOpts
, aArch
);
454 AddAppDirToCommandLine(CommandLine
& aCmdLine
)
456 AddAppDirToCommandLine(std::vector
<std::string
>& aCmdLine
)
459 // Content processes need access to application resources, so pass
460 // the full application directory path to the child process.
461 if (ShouldHaveDirectoryService()) {
462 nsCOMPtr
<nsIProperties
> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
));
463 NS_ASSERTION(directoryService
, "Expected XPCOM to be available");
464 if (directoryService
) {
465 nsCOMPtr
<nsIFile
> appDir
;
466 // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the
467 // current process dir.
468 nsresult rv
= directoryService
->Get(NS_XPCOM_CURRENT_PROCESS_DIR
,
470 getter_AddRefs(appDir
));
471 if (NS_SUCCEEDED(rv
)) {
473 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appDir
->GetNativePath(path
)));
475 aCmdLine
.AppendLooseValue(UTF8ToWide("-appdir"));
476 aCmdLine
.AppendLooseValue(UTF8ToWide(path
.get()));
478 aCmdLine
.push_back("-appdir");
479 aCmdLine
.push_back(path
.get());
487 GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector
<std::string
>& aExtraOpts
, base::ProcessArchitecture arch
)
489 // We rely on the fact that InitializeChannel() has already been processed
490 // on the IO thread before this point is reached.
495 base::ProcessHandle process
= 0;
497 // send the child the PID so that it can open a ProcessHandle back to us.
498 // probably don't want to do this in the long run
500 PR_snprintf(pidstring
, sizeof(pidstring
) - 1,
501 "%ld", base::Process::Current().pid());
503 const char* const childProcessType
=
504 XRE_ChildProcessTypeToString(mProcessType
);
506 //--------------------------------------------------
507 #if defined(OS_POSIX)
508 // For POSIX, we have to be extremely anal about *not* using
509 // std::wstring in code compiled with Mozilla's -fshort-wchar
510 // configuration, because chromium is compiled with -fno-short-wchar
511 // and passing wstrings from one config to the other is unsafe. So
512 // we split the logic here.
514 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
515 base::environment_map newEnvVars
;
516 ChildPrivileges privs
= mPrivileges
;
517 if (privs
== base::PRIVILEGES_DEFAULT
) {
518 privs
= DefaultChildPrivileges();
520 // XPCOM may not be initialized in some subprocesses. We don't want
521 // to initialize XPCOM just for the directory service, especially
522 // since LD_LIBRARY_PATH is already set correctly in subprocesses
523 // (meaning that we don't need to set that up in the environment).
524 if (ShouldHaveDirectoryService()) {
525 MOZ_ASSERT(gGREBinPath
);
527 NS_CopyUnicodeToNative(nsDependentString(gGREBinPath
), path
);
528 # if defined(OS_LINUX) || defined(OS_BSD)
529 # if defined(MOZ_WIDGET_ANDROID)
531 # endif // MOZ_WIDGET_ANDROID
532 const char *ld_library_path
= PR_GetEnv("LD_LIBRARY_PATH");
533 nsCString new_ld_lib_path
;
534 if (ld_library_path
&& *ld_library_path
) {
535 new_ld_lib_path
.Assign(path
.get());
536 new_ld_lib_path
.Append(':');
537 new_ld_lib_path
.Append(ld_library_path
);
538 newEnvVars
["LD_LIBRARY_PATH"] = new_ld_lib_path
.get();
540 newEnvVars
["LD_LIBRARY_PATH"] = path
.get();
543 # if (MOZ_WIDGET_GTK == 3)
544 if (mProcessType
== GeckoProcessType_Plugin
) {
545 const char *ld_preload
= PR_GetEnv("LD_PRELOAD");
546 nsCString new_ld_preload
;
548 new_ld_preload
.Assign(path
.get());
549 new_ld_preload
.AppendLiteral("/" DLL_PREFIX
"mozgtk2" DLL_SUFFIX
);
551 if (ld_preload
&& *ld_preload
) {
552 new_ld_preload
.AppendLiteral(":");
553 new_ld_preload
.Append(ld_preload
);
555 newEnvVars
["LD_PRELOAD"] = new_ld_preload
.get();
557 # endif // MOZ_WIDGET_GTK
561 newEnvVars
["DYLD_LIBRARY_PATH"] = path
.get();
562 // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
563 // process, and has no effect on other subprocesses (the hooks in
564 // libplugin_child_interpose.dylib become noops). But currently it
565 // gets set when launching any kind of subprocess.
567 // Trigger "dyld interposing" for the dylib that contains
568 // plugin_child_interpose.mm. This allows us to hook OS calls in the
569 // plugin process (ones that don't work correctly in a background
570 // process). Don't break any other "dyld interposing" that has already
571 // been set up by whatever may have launched the browser.
572 const char* prevInterpose
= PR_GetEnv("DYLD_INSERT_LIBRARIES");
575 interpose
.Assign(prevInterpose
);
576 interpose
.Append(':');
578 interpose
.Append(path
.get());
579 interpose
.AppendLiteral("/libplugin_child_interpose.dylib");
580 newEnvVars
["DYLD_INSERT_LIBRARIES"] = interpose
.get();
583 #endif // OS_LINUX || OS_MACOSX
586 GetPathToBinary(exePath
);
588 #ifdef MOZ_WIDGET_ANDROID
589 // The java wrapper unpacks this for us but can't make it executable
590 chmod(exePath
.value().c_str(), 0700);
591 #endif // MOZ_WIDGET_ANDROID
594 // Remap the Android property workspace to a well-known int,
595 // and update the environment to reflect the new value for the
597 const char *apws
= getenv("ANDROID_PROPERTY_WORKSPACE");
600 mFileMap
.push_back(std::pair
<int, int>(fd
, kMagicAndroidSystemPropFd
));
603 char *szptr
= strchr(apws
, ',');
605 snprintf(buf
, sizeof(buf
), "%d%s", kMagicAndroidSystemPropFd
, szptr
);
606 newEnvVars
["ANDROID_PROPERTY_WORKSPACE"] = buf
;
610 #ifdef MOZ_WIDGET_GONK
611 if (const char *ldPreloadPath
= getenv("LD_PRELOAD")) {
612 newEnvVars
["LD_PRELOAD"] = ldPreloadPath
;
614 #endif // MOZ_WIDGET_GONK
616 // remap the IPC socket fd to a well-known int, as the OS does for
617 // STDOUT_FILENO, for example
618 int srcChannelFd
, dstChannelFd
;
619 channel().GetClientFileDescriptorMapping(&srcChannelFd
, &dstChannelFd
);
620 mFileMap
.push_back(std::pair
<int,int>(srcChannelFd
, dstChannelFd
));
622 // no need for kProcessChannelID, the child process inherits the
623 // other end of the socketpair() from us
625 std::vector
<std::string
> childArgv
;
627 childArgv
.push_back(exePath
.value());
629 childArgv
.insert(childArgv
.end(), aExtraOpts
.begin(), aExtraOpts
.end());
631 if (Omnijar::IsInitialized()) {
632 // Make sure that child processes can find the omnijar
633 // See XRE_InitCommandLine in nsAppRunner.cpp
635 nsCOMPtr
<nsIFile
> file
= Omnijar::GetPath(Omnijar::GRE
);
636 if (file
&& NS_SUCCEEDED(file
->GetNativePath(path
))) {
637 childArgv
.push_back("-greomni");
638 childArgv
.push_back(path
.get());
640 file
= Omnijar::GetPath(Omnijar::APP
);
641 if (file
&& NS_SUCCEEDED(file
->GetNativePath(path
))) {
642 childArgv
.push_back("-appomni");
643 childArgv
.push_back(path
.get());
647 // Add the application directory path (-appdir path)
648 AddAppDirToCommandLine(childArgv
);
650 childArgv
.push_back(pidstring
);
652 #if defined(MOZ_CRASHREPORTER)
653 # if defined(OS_LINUX) || defined(OS_BSD)
654 int childCrashFd
, childCrashRemapFd
;
655 if (!CrashReporter::CreateNotificationPipeForChild(
656 &childCrashFd
, &childCrashRemapFd
))
658 if (0 <= childCrashFd
) {
659 mFileMap
.push_back(std::pair
<int,int>(childCrashFd
, childCrashRemapFd
));
660 // "true" == crash reporting enabled
661 childArgv
.push_back("true");
664 // "false" == crash reporting disabled
665 childArgv
.push_back("false");
667 # elif defined(MOZ_WIDGET_COCOA)
668 childArgv
.push_back(CrashReporter::GetChildNotificationPipe());
672 #ifdef MOZ_WIDGET_COCOA
673 // Add a mach port to the command line so the child can communicate its
674 // 'task_t' back to the parent.
676 // Put a random number into the channel name, so that a compromised renderer
677 // can't pretend being the child that's forked off.
678 std::string mach_connection_name
= StringPrintf("org.mozilla.machname.%d",
679 base::RandInt(0, std::numeric_limits
<int>::max()));
680 childArgv
.push_back(mach_connection_name
.c_str());
683 childArgv
.push_back(childProcessType
);
685 base::LaunchApp(childArgv
, mFileMap
,
686 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
689 false, &process
, arch
);
691 // We're in the parent and the child was launched. Close the child FD in the
692 // parent as soon as possible, which will allow the parent to detect when the
693 // child closes its FD (either due to normal exit or due to crash).
694 GetChannel()->CloseClientFileDescriptor();
696 #ifdef MOZ_WIDGET_COCOA
697 // Wait for the child process to send us its 'task_t' data.
698 const int kTimeoutMs
= 10000;
700 MachReceiveMessage child_message
;
701 ReceivePort
parent_recv_port(mach_connection_name
.c_str());
702 kern_return_t err
= parent_recv_port
.WaitForMessage(&child_message
, kTimeoutMs
);
703 if (err
!= KERN_SUCCESS
) {
704 std::string errString
= StringPrintf("0x%x %s", err
, mach_error_string(err
));
705 CHROMIUM_LOG(ERROR
) << "parent WaitForMessage() failed: " << errString
;
709 task_t child_task
= child_message
.GetTranslatedPort(0);
710 if (child_task
== MACH_PORT_NULL
) {
711 CHROMIUM_LOG(ERROR
) << "parent GetTranslatedPort(0) failed.";
715 if (child_message
.GetTranslatedPort(1) == MACH_PORT_NULL
) {
716 CHROMIUM_LOG(ERROR
) << "parent GetTranslatedPort(1) failed.";
719 MachPortSender
parent_sender(child_message
.GetTranslatedPort(1));
721 MachSendMessage
parent_message(/* id= */0);
722 if (!parent_message
.AddDescriptor(MachMsgPortDescriptor(bootstrap_port
))) {
723 CHROMIUM_LOG(ERROR
) << "parent AddDescriptor(" << bootstrap_port
<< ") failed.";
727 err
= parent_sender
.SendMessage(parent_message
, kTimeoutMs
);
728 if (err
!= KERN_SUCCESS
) {
729 std::string errString
= StringPrintf("0x%x %s", err
, mach_error_string(err
));
730 CHROMIUM_LOG(ERROR
) << "parent SendMessage() failed: " << errString
;
735 //--------------------------------------------------
736 #elif defined(OS_WIN)
739 GetPathToBinary(exePath
);
741 CommandLine
cmdLine(exePath
.ToWStringHack());
742 cmdLine
.AppendSwitchWithValue(switches::kProcessChannelID
, channel_id());
744 for (std::vector
<std::string
>::iterator it
= aExtraOpts
.begin();
745 it
!= aExtraOpts
.end();
747 cmdLine
.AppendLooseValue(UTF8ToWide(*it
));
750 if (Omnijar::IsInitialized()) {
751 // Make sure the child process can find the omnijar
752 // See XRE_InitCommandLine in nsAppRunner.cpp
754 nsCOMPtr
<nsIFile
> file
= Omnijar::GetPath(Omnijar::GRE
);
755 if (file
&& NS_SUCCEEDED(file
->GetPath(path
))) {
756 cmdLine
.AppendLooseValue(UTF8ToWide("-greomni"));
757 cmdLine
.AppendLooseValue(path
.get());
759 file
= Omnijar::GetPath(Omnijar::APP
);
760 if (file
&& NS_SUCCEEDED(file
->GetPath(path
))) {
761 cmdLine
.AppendLooseValue(UTF8ToWide("-appomni"));
762 cmdLine
.AppendLooseValue(path
.get());
767 bool shouldSandboxCurrentProcess
= false;
768 switch (mProcessType
) {
769 case GeckoProcessType_Content
:
770 #if defined(MOZ_CONTENT_SANDBOX)
771 if (!PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
772 mSandboxBroker
.SetSecurityLevelForContentProcess();
773 cmdLine
.AppendLooseValue(UTF8ToWide("-sandbox"));
774 shouldSandboxCurrentProcess
= true;
776 #endif // MOZ_CONTENT_SANDBOX
778 case GeckoProcessType_Plugin
:
779 // XXX: We don't sandbox this process type yet
780 // mSandboxBroker.SetSecurityLevelForPluginProcess();
781 // cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
782 // shouldSandboxCurrentProcess = true;
784 case GeckoProcessType_IPDLUnitTest
:
785 // XXX: We don't sandbox this process type yet
786 // mSandboxBroker.SetSecurityLevelForIPDLUnitTestProcess();
787 // cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
788 // shouldSandboxCurrentProcess = true;
790 case GeckoProcessType_GMPlugin
:
792 if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) {
793 mSandboxBroker
.SetSecurityLevelForGMPlugin();
794 cmdLine
.AppendLooseValue(UTF8ToWide("-sandbox"));
795 shouldSandboxCurrentProcess
= true;
799 case GeckoProcessType_Default
:
801 MOZ_CRASH("Bad process type in GeckoChildProcessHost");
805 if (shouldSandboxCurrentProcess
) {
806 for (auto it
= mAllowedFilesRead
.begin();
807 it
!= mAllowedFilesRead
.end();
809 mSandboxBroker
.AllowReadFile(it
->c_str());
815 // Add the application directory path (-appdir path)
816 AddAppDirToCommandLine(cmdLine
);
818 // XXX Command line params past this point are expected to be at
819 // the end of the command line string, and in a specific order.
820 // See XRE_InitChildProcess in nsEmbedFunction.
823 cmdLine
.AppendLooseValue(mGroupId
.get());
826 cmdLine
.AppendLooseValue(UTF8ToWide(pidstring
));
828 #if defined(MOZ_CRASHREPORTER)
829 cmdLine
.AppendLooseValue(
830 UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
834 cmdLine
.AppendLooseValue(UTF8ToWide(childProcessType
));
836 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
837 if (shouldSandboxCurrentProcess
) {
838 mSandboxBroker
.LaunchApp(cmdLine
.program().c_str(),
839 cmdLine
.command_line_string().c_str(),
844 base::LaunchApp(cmdLine
, false, false, &process
);
852 MonitorAutoLock
lock(mMonitor
);
853 mProcessState
= PROCESS_ERROR
;
857 // NB: on OS X, we block much longer than we need to in order to
858 // reach this call, waiting for the child process's task_t. The
859 // best way to fix that is to refactor this file, hard.
861 #if defined(MOZ_WIDGET_COCOA)
862 mChildTask
= child_task
;
865 OpenPrivilegedHandle(base::GetProcId(process
));
867 MonitorAutoLock
lock(mMonitor
);
868 mProcessState
= PROCESS_CREATED
;
876 GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid
)
878 if (mChildProcessHandle
) {
879 MOZ_ASSERT(aPid
== base::GetProcId(mChildProcessHandle
));
882 if (!base::OpenPrivilegedProcessHandle(aPid
, &mChildProcessHandle
)) {
883 NS_RUNTIMEABORT("can't open handle to child process");
888 GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid
)
890 OpenPrivilegedHandle(peer_pid
);
892 MonitorAutoLock
lock(mMonitor
);
893 mProcessState
= PROCESS_CONNECTED
;
899 GeckoChildProcessHost::OnMessageReceived(const IPC::Message
& aMsg
)
901 // We never process messages ourself, just save them up for the next
907 GeckoChildProcessHost::OnChannelError()
909 // Update the process state to an error state if we have a channel
910 // error before we're connected. This fixes certain failures,
911 // but does not address the full range of possible issues described
912 // in the FIXME comment below.
913 MonitorAutoLock
lock(mMonitor
);
914 if (mProcessState
< PROCESS_CONNECTED
) {
915 mProcessState
= PROCESS_ERROR
;
918 // FIXME/bug 773925: save up this error for the next listener.
922 GeckoChildProcessHost::GetQueuedMessages(std::queue
<IPC::Message
>& queue
)
924 // If this is called off the IO thread, bad things will happen.
925 DCHECK(MessageLoopForIO::current());
927 // We expect the next listener to take over processing of our queue.
931 GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent
*event
)
934 mDelegate
->OnWaitableEventSignaled(event
);
936 ChildProcessHost::OnWaitableEventSignaled(event
);
939 #ifdef MOZ_NUWA_PROCESS
941 using mozilla::ipc::GeckoExistingProcessHost
;
942 using mozilla::ipc::FileDescriptor
;
944 GeckoExistingProcessHost::
945 GeckoExistingProcessHost(GeckoProcessType aProcessType
,
946 base::ProcessHandle aProcess
,
947 const FileDescriptor
& aFileDescriptor
,
948 ChildPrivileges aPrivileges
)
949 : GeckoChildProcessHost(aProcessType
, aPrivileges
)
950 , mExistingProcessHandle(aProcess
)
951 , mExistingFileDescriptor(aFileDescriptor
)
953 NS_ASSERTION(aFileDescriptor
.IsValid(),
954 "Expected file descriptor to be valid");
957 GeckoExistingProcessHost::~GeckoExistingProcessHost()
959 // Bug 943174: If we don't do this, ~GeckoChildProcessHost will try
960 // to wait on a process that isn't a direct child, and bad things
966 GeckoExistingProcessHost::PerformAsyncLaunch(StringVector aExtraOpts
,
967 base::ProcessArchitecture aArch
)
969 SetHandle(mExistingProcessHandle
);
971 OpenPrivilegedHandle(base::GetProcId(mExistingProcessHandle
));
973 MonitorAutoLock
lock(mMonitor
);
974 mProcessState
= PROCESS_CREATED
;
981 GeckoExistingProcessHost::InitializeChannel()
983 CreateChannel(mExistingFileDescriptor
);
985 MonitorAutoLock
lock(mMonitor
);
986 mProcessState
= CHANNEL_INITIALIZED
;
990 #endif /* MOZ_NUWA_PROCESS */