Bumping manifests a=b2g-bump
[gecko.git] / ipc / glue / GeckoChildProcessHost.cpp
blobbc65b922b5ba42fe19b9808438bd5bd5800822bc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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"
18 #endif
20 #include "MainThreadUtils.h"
21 #include "prprf.h"
22 #include "prenv.h"
23 #include "nsXPCOMPrivate.h"
25 #include "nsExceptionHandler.h"
27 #include "nsDirectoryServiceDefs.h"
28 #include "nsIFile.h"
30 #include "mozilla/ClearOnShutdown.h"
31 #include "mozilla/ipc/BrowserProcessSubThread.h"
32 #include "mozilla/Omnijar.h"
33 #include <sys/stat.h>
35 #ifdef XP_WIN
36 #include "nsIWinTaskbar.h"
37 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
38 #endif
40 #include "nsTArray.h"
41 #include "nsClassHashtable.h"
42 #include "nsHashKeys.h"
43 #include "nsNativeCharsetUtils.h"
45 using mozilla::MonitorAutoLock;
46 using mozilla::ipc::GeckoChildProcessHost;
48 #ifdef ANDROID
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;
53 #endif
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
59 true
60 #else
61 false
62 #endif
65 static bool
66 ShouldHaveDirectoryService()
68 return GeckoProcessType_Default == XRE_GetProcessType();
71 template<>
72 struct RunnableMethodTraits<GeckoChildProcessHost>
74 static void RetainCallee(GeckoChildProcessHost* obj) { }
75 static void ReleaseCallee(GeckoChildProcessHost* obj) { }
78 /*static*/
79 base::ChildPrivileges
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),
93 mDelegate(nullptr),
94 mChildProcessHandle(0)
95 #if defined(MOZ_WIDGET_COCOA)
96 , mChildTask(MACH_PORT_NULL)
97 #endif
99 MOZ_COUNT_CTOR(GeckoChildProcessHost);
102 GeckoChildProcessHost::~GeckoChildProcessHost()
105 AssertIOThread();
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"
113 #endif
116 #if defined(MOZ_WIDGET_COCOA)
117 if (mChildTask != MACH_PORT_NULL)
118 mach_port_deallocate(mach_task_self(), mChildTask);
119 #endif
122 //static
123 void
124 GeckoChildProcessHost::GetPathToBinary(FilePath& exePath)
126 if (ShouldHaveDirectoryService()) {
127 MOZ_ASSERT(gGREBinPath);
128 #ifdef OS_WIN
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"));
139 nsCString tempCPath;
140 childProcPath->GetNativePath(tempCPath);
141 exePath = FilePath(tempCPath.get());
142 #else
143 nsCString path;
144 NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
145 exePath = FilePath(path.get());
146 #endif
149 if (exePath.empty()) {
150 #ifdef OS_WIN
151 exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
152 #else
153 exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
154 #endif
155 exePath = exePath.DirName();
158 exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
161 #ifdef MOZ_WIDGET_COCOA
162 class AutoCFTypeObject {
163 public:
164 explicit AutoCFTypeObject(CFTypeRef object)
166 mObject = object;
168 ~AutoCFTypeObject()
170 ::CFRelease(mObject);
172 private:
173 CFTypeRef mObject;
175 #endif
177 nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32_t *result)
179 *result = 0;
181 #ifdef MOZ_WIDGET_COCOA
182 CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
183 (const UInt8*)path,
184 strlen(path),
185 false);
186 if (!url) {
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, &currentArchInt)) {
202 continue;
204 switch (currentArchInt) {
205 case kCFBundleExecutableArchitectureI386:
206 *result |= base::PROCESS_ARCH_I386;
207 break;
208 case kCFBundleExecutableArchitectureX86_64:
209 *result |= base::PROCESS_ARCH_X86_64;
210 break;
211 case kCFBundleExecutableArchitecturePPC:
212 *result |= base::PROCESS_ARCH_PPC;
213 break;
214 default:
215 break;
219 return (*result ? NS_OK : NS_ERROR_FAILURE);
220 #else
221 return NS_ERROR_NOT_IMPLEMENTED;
222 #endif
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) {
233 FilePath exePath;
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;
243 #endif
245 return base::GetCurrentProcessArchitecture();
248 void
249 GeckoChildProcessHost::PrepareLaunch()
251 #ifdef MOZ_CRASHREPORTER
252 if (CrashReporter::GetEnabled()) {
253 CrashReporter::OOPInit();
255 #endif
257 #ifdef XP_WIN
258 if (mProcessType == GeckoProcessType_Plugin) {
259 InitWindowsGroupID();
261 #endif
264 #ifdef XP_WIN
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);
272 if (taskbarInfo) {
273 bool isSupported = false;
274 taskbarInfo->GetAvailable(&isSupported);
275 nsAutoString appId;
276 if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) {
277 mGroupId.Append(appId);
278 } else {
279 mGroupId.Assign('-');
283 #endif
285 bool
286 GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
288 PrepareLaunch();
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,
298 aExtraOpts, arch));
299 // NB: this uses a different mechanism than the chromium parent
300 // class.
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) {
310 break;
313 lock.Wait(timeoutTicks);
315 if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) {
316 current = PR_IntervalNow();
317 PRIntervalTime elapsed = current - waitStart;
318 if (elapsed > timeoutTicks) {
319 break;
321 timeoutTicks = timeoutTicks - elapsed;
322 waitStart = current;
326 return mProcessState == PROCESS_CONNECTED;
329 bool
330 GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
332 PrepareLaunch();
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) {
344 lock.Wait();
347 return true;
350 bool
351 GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
353 PrepareLaunch();
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) {
363 lock.Wait();
365 MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle);
367 return mProcessState < PROCESS_ERROR;
370 void
371 GeckoChildProcessHost::InitializeChannel()
373 CreateChannel();
375 MonitorAutoLock lock(mMonitor);
376 mProcessState = CHANNEL_INITIALIZED;
377 lock.Notify();
380 void
381 GeckoChildProcessHost::Join()
383 AssertIOThread();
385 if (!mChildProcessHandle) {
386 return;
389 // If this fails, there's nothing we can do.
390 base::KillProcess(mChildProcessHandle, 0, /*wait*/true);
391 SetAlreadyDead();
394 void
395 GeckoChildProcessHost::SetAlreadyDead()
397 mChildProcessHandle = 0;
400 int32_t GeckoChildProcessHost::mChildCounter = 0;
403 // Wrapper function for handling GECKO_SEPARATE_NSPR_LOGS
405 bool
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");
410 if (!origLogName) {
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);
441 return retval;
444 bool
445 GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
446 base::ProcessArchitecture aArch)
448 InitializeChannel();
449 return PerformAsyncLaunch(aExtraOpts, aArch);
452 void
453 #if defined(XP_WIN)
454 AddAppDirToCommandLine(CommandLine& aCmdLine)
455 #else
456 AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
457 #endif
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,
469 NS_GET_IID(nsIFile),
470 getter_AddRefs(appDir));
471 if (NS_SUCCEEDED(rv)) {
472 nsAutoCString path;
473 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appDir->GetNativePath(path)));
474 #if defined(XP_WIN)
475 aCmdLine.AppendLooseValue(UTF8ToWide("-appdir"));
476 aCmdLine.AppendLooseValue(UTF8ToWide(path.get()));
477 #else
478 aCmdLine.push_back("-appdir");
479 aCmdLine.push_back(path.get());
480 #endif
486 bool
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.
491 if (!GetChannel()) {
492 return false;
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
499 char pidstring[32];
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);
526 nsCString path;
527 NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
528 # if defined(OS_LINUX) || defined(OS_BSD)
529 # if defined(MOZ_WIDGET_ANDROID)
530 path += "/lib";
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();
539 } else {
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
560 # elif OS_MACOSX
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");
573 nsCString interpose;
574 if (prevInterpose) {
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();
581 # endif // OS_LINUX
583 #endif // OS_LINUX || OS_MACOSX
585 FilePath exePath;
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
593 #ifdef ANDROID
594 // Remap the Android property workspace to a well-known int,
595 // and update the environment to reflect the new value for the
596 // child process.
597 const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE");
598 if (apws) {
599 int fd = atoi(apws);
600 mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd));
602 char buf[32];
603 char *szptr = strchr(apws, ',');
605 snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr);
606 newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf;
608 #endif // ANDROID
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
634 nsAutoCString path;
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))
657 return false;
658 if (0 <= childCrashFd) {
659 mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
660 // "true" == crash reporting enabled
661 childArgv.push_back("true");
663 else {
664 // "false" == crash reporting disabled
665 childArgv.push_back("false");
667 # elif defined(MOZ_WIDGET_COCOA)
668 childArgv.push_back(CrashReporter::GetChildNotificationPipe());
669 # endif // OS_LINUX
670 #endif
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());
681 #endif
683 childArgv.push_back(childProcessType);
685 base::LaunchApp(childArgv, mFileMap,
686 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
687 newEnvVars, privs,
688 #endif
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;
706 return false;
709 task_t child_task = child_message.GetTranslatedPort(0);
710 if (child_task == MACH_PORT_NULL) {
711 CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
712 return false;
715 if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
716 CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
717 return false;
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.";
724 return false;
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;
731 return false;
733 #endif
735 //--------------------------------------------------
736 #elif defined(OS_WIN)
738 FilePath exePath;
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();
746 ++it) {
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
753 nsAutoString path;
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());
766 #if defined(XP_WIN)
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
777 break;
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;
783 break;
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;
789 break;
790 case GeckoProcessType_GMPlugin:
791 #ifdef MOZ_SANDBOX
792 if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) {
793 mSandboxBroker.SetSecurityLevelForGMPlugin();
794 cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
795 shouldSandboxCurrentProcess = true;
797 #endif
798 break;
799 case GeckoProcessType_Default:
800 default:
801 MOZ_CRASH("Bad process type in GeckoChildProcessHost");
802 break;
805 if (shouldSandboxCurrentProcess) {
806 for (auto it = mAllowedFilesRead.begin();
807 it != mAllowedFilesRead.end();
808 ++it) {
809 mSandboxBroker.AllowReadFile(it->c_str());
813 #endif // XP_WIN
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.
822 // Win app model id
823 cmdLine.AppendLooseValue(mGroupId.get());
825 // Process id
826 cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
828 #if defined(MOZ_CRASHREPORTER)
829 cmdLine.AppendLooseValue(
830 UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
831 #endif
833 // Process type
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(),
840 &process);
841 } else
842 #endif
844 base::LaunchApp(cmdLine, false, false, &process);
847 #else
848 # error Sorry
849 #endif
851 if (!process) {
852 MonitorAutoLock lock(mMonitor);
853 mProcessState = PROCESS_ERROR;
854 lock.Notify();
855 return false;
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.
860 SetHandle(process);
861 #if defined(MOZ_WIDGET_COCOA)
862 mChildTask = child_task;
863 #endif
865 OpenPrivilegedHandle(base::GetProcId(process));
867 MonitorAutoLock lock(mMonitor);
868 mProcessState = PROCESS_CREATED;
869 lock.Notify();
872 return true;
875 void
876 GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid)
878 if (mChildProcessHandle) {
879 MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle));
880 return;
882 if (!base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle)) {
883 NS_RUNTIMEABORT("can't open handle to child process");
887 void
888 GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid)
890 OpenPrivilegedHandle(peer_pid);
892 MonitorAutoLock lock(mMonitor);
893 mProcessState = PROCESS_CONNECTED;
894 lock.Notify();
898 void
899 GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg)
901 // We never process messages ourself, just save them up for the next
902 // listener.
903 mQueue.push(aMsg);
906 void
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;
916 lock.Notify();
918 // FIXME/bug 773925: save up this error for the next listener.
921 void
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());
926 swap(queue, mQueue);
927 // We expect the next listener to take over processing of our queue.
930 void
931 GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event)
933 if (mDelegate) {
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
961 // will happen.
962 SetAlreadyDead();
965 bool
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;
975 lock.Notify();
977 return true;
980 void
981 GeckoExistingProcessHost::InitializeChannel()
983 CreateChannel(mExistingFileDescriptor);
985 MonitorAutoLock lock(mMonitor);
986 mProcessState = CHANNEL_INITIALIZED;
987 lock.Notify();
990 #endif /* MOZ_NUWA_PROCESS */