Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
10 #include "sandboxBroker.h"
11 #endif
13 #include "base/command_line.h"
14 #include "base/path_service.h"
15 #include "base/string_util.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/process_watcher.h"
18 #ifdef MOZ_WIDGET_COCOA
19 #include "chrome/common/mach_ipc_mac.h"
20 #include "base/rand_util.h"
21 #include "nsILocalFileMac.h"
22 #endif
24 #include "MainThreadUtils.h"
25 #include "prprf.h"
26 #include "prenv.h"
28 #include "nsExceptionHandler.h"
30 #include "nsDirectoryServiceDefs.h"
31 #include "nsIFile.h"
33 #include "mozilla/ClearOnShutdown.h"
34 #include "mozilla/ipc/BrowserProcessSubThread.h"
35 #include "mozilla/Omnijar.h"
36 #include <sys/stat.h>
38 #ifdef XP_WIN
39 #include "nsIWinTaskbar.h"
40 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
41 #endif
43 #include "nsTArray.h"
44 #include "nsClassHashtable.h"
45 #include "nsHashKeys.h"
47 using mozilla::MonitorAutoLock;
48 using mozilla::ipc::GeckoChildProcessHost;
50 #ifdef ANDROID
51 // Like its predecessor in nsExceptionHandler.cpp, this is
52 // the magic number of a file descriptor remapping we must
53 // preserve for the child process.
54 static const int kMagicAndroidSystemPropFd = 5;
55 #endif
57 static const bool kLowRightsSubprocesses =
58 // We currently only attempt to drop privileges on gonk, because we
59 // have no plugins or extensions to worry about breaking.
60 #ifdef MOZ_WIDGET_GONK
61 true
62 #else
63 false
64 #endif
65 ;
67 mozilla::StaticRefPtr<nsIFile> GeckoChildProcessHost::sGreDir;
68 mozilla::DebugOnly<bool> GeckoChildProcessHost::sGreDirCached;
70 static bool
71 ShouldHaveDirectoryService()
72 {
73 return GeckoProcessType_Default == XRE_GetProcessType();
74 }
76 template<>
77 struct RunnableMethodTraits<GeckoChildProcessHost>
78 {
79 static void RetainCallee(GeckoChildProcessHost* obj) { }
80 static void ReleaseCallee(GeckoChildProcessHost* obj) { }
81 };
83 /*static*/
84 base::ChildPrivileges
85 GeckoChildProcessHost::DefaultChildPrivileges()
86 {
87 return (kLowRightsSubprocesses ?
88 base::PRIVILEGES_UNPRIVILEGED : base::PRIVILEGES_INHERIT);
89 }
91 GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
92 ChildPrivileges aPrivileges)
93 : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum
94 mProcessType(aProcessType),
95 mSandboxEnabled(true),
96 mPrivileges(aPrivileges),
97 mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
98 mProcessState(CREATING_CHANNEL),
99 mDelegate(nullptr),
100 mChildProcessHandle(0)
101 #if defined(MOZ_WIDGET_COCOA)
102 , mChildTask(MACH_PORT_NULL)
103 #endif
104 {
105 MOZ_COUNT_CTOR(GeckoChildProcessHost);
106 }
108 GeckoChildProcessHost::~GeckoChildProcessHost()
110 {
111 AssertIOThread();
113 MOZ_COUNT_DTOR(GeckoChildProcessHost);
115 if (mChildProcessHandle > 0)
116 ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
117 #if defined(NS_BUILD_REFCNT_LOGGING)
118 , false // don't "force"
119 #endif
120 );
122 #if defined(MOZ_WIDGET_COCOA)
123 if (mChildTask != MACH_PORT_NULL)
124 mach_port_deallocate(mach_task_self(), mChildTask);
125 #endif
126 }
128 //static
129 void
130 GeckoChildProcessHost::GetPathToBinary(FilePath& exePath)
131 {
132 if (ShouldHaveDirectoryService()) {
133 MOZ_ASSERT(sGreDirCached);
134 if (sGreDir) {
135 #ifdef OS_WIN
136 nsString path;
137 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetPath(path)));
138 #else
139 nsCString path;
140 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetNativePath(path)));
141 #endif
142 exePath = FilePath(path.get());
143 #ifdef MOZ_WIDGET_COCOA
144 // We need to use an App Bundle on OS X so that we can hide
145 // the dock icon. See Bug 557225.
146 exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
147 #endif
148 }
149 }
151 if (exePath.empty()) {
152 #ifdef OS_WIN
153 exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
154 #else
155 exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
156 #endif
157 exePath = exePath.DirName();
158 }
160 exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
161 }
163 #ifdef MOZ_WIDGET_COCOA
164 class AutoCFTypeObject {
165 public:
166 AutoCFTypeObject(CFTypeRef object)
167 {
168 mObject = object;
169 }
170 ~AutoCFTypeObject()
171 {
172 ::CFRelease(mObject);
173 }
174 private:
175 CFTypeRef mObject;
176 };
177 #endif
179 nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32_t *result)
180 {
181 *result = 0;
183 #ifdef MOZ_WIDGET_COCOA
184 CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
185 (const UInt8*)path,
186 strlen(path),
187 false);
188 if (!url) {
189 return NS_ERROR_FAILURE;
190 }
191 AutoCFTypeObject autoPluginContainerURL(url);
193 CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url);
194 if (!pluginContainerArchs) {
195 return NS_ERROR_FAILURE;
196 }
197 AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs);
199 CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs);
200 for (CFIndex i = 0; i < pluginArchCount; i++) {
201 CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i));
202 int currentArchInt = 0;
203 if (!::CFNumberGetValue(currentArch, kCFNumberIntType, ¤tArchInt)) {
204 continue;
205 }
206 switch (currentArchInt) {
207 case kCFBundleExecutableArchitectureI386:
208 *result |= base::PROCESS_ARCH_I386;
209 break;
210 case kCFBundleExecutableArchitectureX86_64:
211 *result |= base::PROCESS_ARCH_X86_64;
212 break;
213 case kCFBundleExecutableArchitecturePPC:
214 *result |= base::PROCESS_ARCH_PPC;
215 break;
216 default:
217 break;
218 }
219 }
221 return (*result ? NS_OK : NS_ERROR_FAILURE);
222 #else
223 return NS_ERROR_NOT_IMPLEMENTED;
224 #endif
225 }
227 uint32_t GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type)
228 {
229 #ifdef MOZ_WIDGET_COCOA
230 if (type == GeckoProcessType_Plugin) {
232 // Cache this, it shouldn't ever change.
233 static uint32_t pluginContainerArchs = 0;
234 if (pluginContainerArchs == 0) {
235 CacheGreDir();
236 FilePath exePath;
237 GetPathToBinary(exePath);
238 nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs);
239 NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!");
240 if (NS_FAILED(rv) || pluginContainerArchs == 0) {
241 pluginContainerArchs = base::GetCurrentProcessArchitecture();
242 }
243 }
244 return pluginContainerArchs;
245 }
246 #endif
248 return base::GetCurrentProcessArchitecture();
249 }
251 void
252 GeckoChildProcessHost::PrepareLaunch()
253 {
254 #ifdef MOZ_CRASHREPORTER
255 if (CrashReporter::GetEnabled()) {
256 CrashReporter::OOPInit();
257 }
258 #endif
260 #ifdef XP_WIN
261 InitWindowsGroupID();
262 #endif
263 CacheGreDir();
264 }
266 //static
267 void
268 GeckoChildProcessHost::CacheGreDir()
269 {
270 // PerformAysncLaunchInternal/GetPathToBinary may be called on the IO thread,
271 // and they want to use the directory service, which needs to happen on the
272 // main thread (in the event that its implemented in JS). So we grab
273 // NS_GRE_DIR here and stash it.
275 #ifdef MOZ_WIDGET_GONK
276 // Apparently, this ASSERT should be present on all platforms. Currently,
277 // this assert causes mochitest failures on the Mac platform if its left in.
279 // B2G overrides the directory service in JS, so this needs to be called
280 // on the main thread.
281 MOZ_ASSERT(NS_IsMainThread());
282 #endif
284 if (ShouldHaveDirectoryService()) {
285 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
286 MOZ_ASSERT(directoryService, "Expected XPCOM to be available");
287 if (directoryService) {
288 // getter_AddRefs doesn't work with StaticRefPtr, so we need to store the
289 // result in an nsCOMPtr and copy it over.
290 nsCOMPtr<nsIFile> greDir;
291 nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
292 if (NS_SUCCEEDED(rv)) {
293 sGreDir = greDir;
294 mozilla::ClearOnShutdown(&sGreDir);
295 }
296 }
297 }
298 sGreDirCached = true;
299 }
301 #ifdef XP_WIN
302 void GeckoChildProcessHost::InitWindowsGroupID()
303 {
304 // On Win7+, pass the application user model to the child, so it can
305 // register with it. This insures windows created by the container
306 // properly group with the parent app on the Win7 taskbar.
307 nsCOMPtr<nsIWinTaskbar> taskbarInfo =
308 do_GetService(NS_TASKBAR_CONTRACTID);
309 if (taskbarInfo) {
310 bool isSupported = false;
311 taskbarInfo->GetAvailable(&isSupported);
312 nsAutoString appId;
313 if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) {
314 mGroupId.Append(appId);
315 } else {
316 mGroupId.AssignLiteral("-");
317 }
318 }
319 }
320 #endif
322 bool
323 GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
324 {
325 PrepareLaunch();
327 PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ?
328 PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
329 MessageLoop* ioLoop = XRE_GetIOMessageLoop();
330 NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI");
332 ioLoop->PostTask(FROM_HERE,
333 NewRunnableMethod(this,
334 &GeckoChildProcessHost::RunPerformAsyncLaunch,
335 aExtraOpts, arch));
336 // NB: this uses a different mechanism than the chromium parent
337 // class.
338 MonitorAutoLock lock(mMonitor);
339 PRIntervalTime waitStart = PR_IntervalNow();
340 PRIntervalTime current;
342 // We'll receive several notifications, we need to exit when we
343 // have either successfully launched or have timed out.
344 while (mProcessState != PROCESS_CONNECTED) {
345 // If there was an error then return it, don't wait out the timeout.
346 if (mProcessState == PROCESS_ERROR) {
347 break;
348 }
350 lock.Wait(timeoutTicks);
352 if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) {
353 current = PR_IntervalNow();
354 PRIntervalTime elapsed = current - waitStart;
355 if (elapsed > timeoutTicks) {
356 break;
357 }
358 timeoutTicks = timeoutTicks - elapsed;
359 waitStart = current;
360 }
361 }
363 return mProcessState == PROCESS_CONNECTED;
364 }
366 bool
367 GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
368 {
369 PrepareLaunch();
371 MessageLoop* ioLoop = XRE_GetIOMessageLoop();
372 ioLoop->PostTask(FROM_HERE,
373 NewRunnableMethod(this,
374 &GeckoChildProcessHost::RunPerformAsyncLaunch,
375 aExtraOpts, base::GetCurrentProcessArchitecture()));
377 // This may look like the sync launch wait, but we only delay as
378 // long as it takes to create the channel.
379 MonitorAutoLock lock(mMonitor);
380 while (mProcessState < CHANNEL_INITIALIZED) {
381 lock.Wait();
382 }
384 return true;
385 }
387 bool
388 GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
389 {
390 PrepareLaunch();
392 MessageLoop* ioLoop = XRE_GetIOMessageLoop();
393 ioLoop->PostTask(FROM_HERE,
394 NewRunnableMethod(this,
395 &GeckoChildProcessHost::RunPerformAsyncLaunch,
396 aExtraOpts, base::GetCurrentProcessArchitecture()));
398 MonitorAutoLock lock(mMonitor);
399 while (mProcessState < PROCESS_CREATED) {
400 lock.Wait();
401 }
402 MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle);
404 return mProcessState < PROCESS_ERROR;
405 }
407 void
408 GeckoChildProcessHost::InitializeChannel()
409 {
410 CreateChannel();
412 MonitorAutoLock lock(mMonitor);
413 mProcessState = CHANNEL_INITIALIZED;
414 lock.Notify();
415 }
417 void
418 GeckoChildProcessHost::Join()
419 {
420 AssertIOThread();
422 if (!mChildProcessHandle) {
423 return;
424 }
426 // If this fails, there's nothing we can do.
427 base::KillProcess(mChildProcessHandle, 0, /*wait*/true);
428 SetAlreadyDead();
429 }
431 void
432 GeckoChildProcessHost::SetAlreadyDead()
433 {
434 mChildProcessHandle = 0;
435 }
437 int32_t GeckoChildProcessHost::mChildCounter = 0;
439 //
440 // Wrapper function for handling GECKO_SEPARATE_NSPR_LOGS
441 //
442 bool
443 GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch)
444 {
445 // If NSPR log files are not requested, we're done.
446 const char* origLogName = PR_GetEnv("NSPR_LOG_FILE");
447 if (!origLogName) {
448 return PerformAsyncLaunchInternal(aExtraOpts, arch);
449 }
451 // We currently have no portable way to launch child with environment
452 // different than parent. So temporarily change NSPR_LOG_FILE so child
453 // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
454 // startup, so it's 'safe' to play with the parent's environment this way.)
455 nsAutoCString setChildLogName("NSPR_LOG_FILE=");
456 setChildLogName.Append(origLogName);
458 // remember original value so we can restore it.
459 // - buffer needs to be permanently allocated for PR_SetEnv()
460 // - Note: this code is not called re-entrantly, nor are restoreOrigLogName
461 // or mChildCounter touched by any other thread, so this is safe.
462 static char* restoreOrigLogName = 0;
463 if (!restoreOrigLogName)
464 restoreOrigLogName = strdup(setChildLogName.get());
466 // Append child-specific postfix to name
467 setChildLogName.AppendLiteral(".child-");
468 setChildLogName.AppendInt(++mChildCounter);
470 // Passing temporary to PR_SetEnv is ok here because env gets copied
471 // by exec, etc., to permanent storage in child when process launched.
472 PR_SetEnv(setChildLogName.get());
473 bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
475 // Revert to original value
476 PR_SetEnv(restoreOrigLogName);
478 return retval;
479 }
481 bool
482 GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
483 base::ProcessArchitecture aArch)
484 {
485 InitializeChannel();
486 return PerformAsyncLaunch(aExtraOpts, aArch);
487 }
489 void
490 #if defined(XP_WIN)
491 AddAppDirToCommandLine(CommandLine& aCmdLine)
492 #else
493 AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
494 #endif
495 {
496 // Content processes need access to application resources, so pass
497 // the full application directory path to the child process.
498 if (ShouldHaveDirectoryService()) {
499 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
500 NS_ASSERTION(directoryService, "Expected XPCOM to be available");
501 if (directoryService) {
502 nsCOMPtr<nsIFile> appDir;
503 // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the
504 // current process dir.
505 nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
506 NS_GET_IID(nsIFile),
507 getter_AddRefs(appDir));
508 if (NS_SUCCEEDED(rv)) {
509 nsAutoCString path;
510 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appDir->GetNativePath(path)));
511 #if defined(XP_WIN)
512 aCmdLine.AppendLooseValue(UTF8ToWide("-appdir"));
513 aCmdLine.AppendLooseValue(UTF8ToWide(path.get()));
514 #else
515 aCmdLine.push_back("-appdir");
516 aCmdLine.push_back(path.get());
517 #endif
518 }
519 }
520 }
521 }
523 bool
524 GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
525 {
526 // We rely on the fact that InitializeChannel() has already been processed
527 // on the IO thread before this point is reached.
528 if (!GetChannel()) {
529 return false;
530 }
532 base::ProcessHandle process = 0;
534 // send the child the PID so that it can open a ProcessHandle back to us.
535 // probably don't want to do this in the long run
536 char pidstring[32];
537 PR_snprintf(pidstring, sizeof(pidstring) - 1,
538 "%ld", base::Process::Current().pid());
540 const char* const childProcessType =
541 XRE_ChildProcessTypeToString(mProcessType);
543 //--------------------------------------------------
544 #if defined(OS_POSIX)
545 // For POSIX, we have to be extremely anal about *not* using
546 // std::wstring in code compiled with Mozilla's -fshort-wchar
547 // configuration, because chromium is compiled with -fno-short-wchar
548 // and passing wstrings from one config to the other is unsafe. So
549 // we split the logic here.
551 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
552 base::environment_map newEnvVars;
553 ChildPrivileges privs = mPrivileges;
554 if (privs == base::PRIVILEGES_DEFAULT) {
555 privs = DefaultChildPrivileges();
556 }
557 // XPCOM may not be initialized in some subprocesses. We don't want
558 // to initialize XPCOM just for the directory service, especially
559 // since LD_LIBRARY_PATH is already set correctly in subprocesses
560 // (meaning that we don't need to set that up in the environment).
561 if (ShouldHaveDirectoryService()) {
562 MOZ_ASSERT(sGreDirCached);
563 if (sGreDir) {
564 nsCString path;
565 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetNativePath(path)));
566 # if defined(OS_LINUX) || defined(OS_BSD)
567 # if defined(MOZ_WIDGET_ANDROID)
568 path += "/lib";
569 # endif // MOZ_WIDGET_ANDROID
570 const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
571 nsCString new_ld_lib_path;
572 if (ld_library_path && *ld_library_path) {
573 new_ld_lib_path.Assign(path.get());
574 new_ld_lib_path.AppendLiteral(":");
575 new_ld_lib_path.Append(ld_library_path);
576 newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
577 } else {
578 newEnvVars["LD_LIBRARY_PATH"] = path.get();
579 }
580 # elif OS_MACOSX
581 newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
582 // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
583 // process, and has no effect on other subprocesses (the hooks in
584 // libplugin_child_interpose.dylib become noops). But currently it
585 // gets set when launching any kind of subprocess.
586 //
587 // Trigger "dyld interposing" for the dylib that contains
588 // plugin_child_interpose.mm. This allows us to hook OS calls in the
589 // plugin process (ones that don't work correctly in a background
590 // process). Don't break any other "dyld interposing" that has already
591 // been set up by whatever may have launched the browser.
592 const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
593 nsCString interpose;
594 if (prevInterpose) {
595 interpose.Assign(prevInterpose);
596 interpose.AppendLiteral(":");
597 }
598 interpose.Append(path.get());
599 interpose.AppendLiteral("/libplugin_child_interpose.dylib");
600 newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
601 # endif // OS_LINUX
602 }
603 }
604 #endif // OS_LINUX || OS_MACOSX
606 FilePath exePath;
607 GetPathToBinary(exePath);
609 #ifdef MOZ_WIDGET_ANDROID
610 // The java wrapper unpacks this for us but can't make it executable
611 chmod(exePath.value().c_str(), 0700);
612 #endif // MOZ_WIDGET_ANDROID
614 #ifdef ANDROID
615 // Remap the Android property workspace to a well-known int,
616 // and update the environment to reflect the new value for the
617 // child process.
618 const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE");
619 if (apws) {
620 int fd = atoi(apws);
621 mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd));
623 char buf[32];
624 char *szptr = strchr(apws, ',');
626 snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr);
627 newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf;
628 }
629 #endif // ANDROID
631 #ifdef MOZ_WIDGET_GONK
632 if (const char *ldPreloadPath = getenv("LD_PRELOAD")) {
633 newEnvVars["LD_PRELOAD"] = ldPreloadPath;
634 }
635 #endif // MOZ_WIDGET_GONK
637 // remap the IPC socket fd to a well-known int, as the OS does for
638 // STDOUT_FILENO, for example
639 int srcChannelFd, dstChannelFd;
640 channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
641 mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
643 // no need for kProcessChannelID, the child process inherits the
644 // other end of the socketpair() from us
646 std::vector<std::string> childArgv;
648 childArgv.push_back(exePath.value());
650 childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
652 if (Omnijar::IsInitialized()) {
653 // Make sure that child processes can find the omnijar
654 // See XRE_InitCommandLine in nsAppRunner.cpp
655 nsAutoCString path;
656 nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
657 if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
658 childArgv.push_back("-greomni");
659 childArgv.push_back(path.get());
660 }
661 file = Omnijar::GetPath(Omnijar::APP);
662 if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
663 childArgv.push_back("-appomni");
664 childArgv.push_back(path.get());
665 }
666 }
668 // Add the application directory path (-appdir path)
669 AddAppDirToCommandLine(childArgv);
671 childArgv.push_back(pidstring);
673 #if defined(MOZ_CRASHREPORTER)
674 # if defined(OS_LINUX) || defined(OS_BSD)
675 int childCrashFd, childCrashRemapFd;
676 if (!CrashReporter::CreateNotificationPipeForChild(
677 &childCrashFd, &childCrashRemapFd))
678 return false;
679 if (0 <= childCrashFd) {
680 mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
681 // "true" == crash reporting enabled
682 childArgv.push_back("true");
683 }
684 else {
685 // "false" == crash reporting disabled
686 childArgv.push_back("false");
687 }
688 # elif defined(MOZ_WIDGET_COCOA)
689 childArgv.push_back(CrashReporter::GetChildNotificationPipe());
690 # endif // OS_LINUX
691 #endif
693 #ifdef MOZ_WIDGET_COCOA
694 // Add a mach port to the command line so the child can communicate its
695 // 'task_t' back to the parent.
696 //
697 // Put a random number into the channel name, so that a compromised renderer
698 // can't pretend being the child that's forked off.
699 std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
700 base::RandInt(0, std::numeric_limits<int>::max()));
701 childArgv.push_back(mach_connection_name.c_str());
702 #endif
704 childArgv.push_back(childProcessType);
706 base::LaunchApp(childArgv, mFileMap,
707 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
708 newEnvVars, privs,
709 #endif
710 false, &process, arch);
712 // We're in the parent and the child was launched. Close the child FD in the
713 // parent as soon as possible, which will allow the parent to detect when the
714 // child closes its FD (either due to normal exit or due to crash).
715 GetChannel()->CloseClientFileDescriptor();
717 #ifdef MOZ_WIDGET_COCOA
718 // Wait for the child process to send us its 'task_t' data.
719 const int kTimeoutMs = 10000;
721 MachReceiveMessage child_message;
722 ReceivePort parent_recv_port(mach_connection_name.c_str());
723 kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
724 if (err != KERN_SUCCESS) {
725 std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
726 CHROMIUM_LOG(ERROR) << "parent WaitForMessage() failed: " << errString;
727 return false;
728 }
730 task_t child_task = child_message.GetTranslatedPort(0);
731 if (child_task == MACH_PORT_NULL) {
732 CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
733 return false;
734 }
736 if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
737 CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
738 return false;
739 }
740 MachPortSender parent_sender(child_message.GetTranslatedPort(1));
742 MachSendMessage parent_message(/* id= */0);
743 if (!parent_message.AddDescriptor(bootstrap_port)) {
744 CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
745 return false;
746 }
748 err = parent_sender.SendMessage(parent_message, kTimeoutMs);
749 if (err != KERN_SUCCESS) {
750 std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
751 CHROMIUM_LOG(ERROR) << "parent SendMessage() failed: " << errString;
752 return false;
753 }
754 #endif
756 //--------------------------------------------------
757 #elif defined(OS_WIN)
759 FilePath exePath;
760 GetPathToBinary(exePath);
762 CommandLine cmdLine(exePath.ToWStringHack());
763 cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
765 for (std::vector<std::string>::iterator it = aExtraOpts.begin();
766 it != aExtraOpts.end();
767 ++it) {
768 cmdLine.AppendLooseValue(UTF8ToWide(*it));
769 }
771 if (Omnijar::IsInitialized()) {
772 // Make sure the child process can find the omnijar
773 // See XRE_InitCommandLine in nsAppRunner.cpp
774 nsAutoString path;
775 nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
776 if (file && NS_SUCCEEDED(file->GetPath(path))) {
777 cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
778 cmdLine.AppendLooseValue(path.get());
779 }
780 file = Omnijar::GetPath(Omnijar::APP);
781 if (file && NS_SUCCEEDED(file->GetPath(path))) {
782 cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
783 cmdLine.AppendLooseValue(path.get());
784 }
785 }
787 #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
788 if (mSandboxEnabled) {
789 // Tell the process that it should lower its rights after initialization.
790 cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
791 }
792 #endif
794 // Add the application directory path (-appdir path)
795 AddAppDirToCommandLine(cmdLine);
797 // XXX Command line params past this point are expected to be at
798 // the end of the command line string, and in a specific order.
799 // See XRE_InitChildProcess in nsEmbedFunction.
801 // Win app model id
802 cmdLine.AppendLooseValue(mGroupId.get());
804 // Process id
805 cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
807 #if defined(MOZ_CRASHREPORTER)
808 cmdLine.AppendLooseValue(
809 UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
810 #endif
812 // Process type
813 cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
815 #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
816 if (mSandboxEnabled) {
818 mozilla::SandboxBroker sandboxBroker;
819 sandboxBroker.LaunchApp(cmdLine.program().c_str(),
820 cmdLine.command_line_string().c_str(),
821 &process);
822 } else
823 #endif
824 {
825 base::LaunchApp(cmdLine, false, false, &process);
826 }
828 #else
829 # error Sorry
830 #endif
832 if (!process) {
833 MonitorAutoLock lock(mMonitor);
834 mProcessState = PROCESS_ERROR;
835 lock.Notify();
836 return false;
837 }
838 // NB: on OS X, we block much longer than we need to in order to
839 // reach this call, waiting for the child process's task_t. The
840 // best way to fix that is to refactor this file, hard.
841 SetHandle(process);
842 #if defined(MOZ_WIDGET_COCOA)
843 mChildTask = child_task;
844 #endif
846 OpenPrivilegedHandle(base::GetProcId(process));
847 {
848 MonitorAutoLock lock(mMonitor);
849 mProcessState = PROCESS_CREATED;
850 lock.Notify();
851 }
853 return true;
854 }
856 void
857 GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid)
858 {
859 if (mChildProcessHandle) {
860 MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle));
861 return;
862 }
863 if (!base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle)) {
864 NS_RUNTIMEABORT("can't open handle to child process");
865 }
866 }
868 void
869 GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid)
870 {
871 OpenPrivilegedHandle(peer_pid);
872 {
873 MonitorAutoLock lock(mMonitor);
874 mProcessState = PROCESS_CONNECTED;
875 lock.Notify();
876 }
877 }
879 void
880 GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg)
881 {
882 // We never process messages ourself, just save them up for the next
883 // listener.
884 mQueue.push(aMsg);
885 }
887 void
888 GeckoChildProcessHost::OnChannelError()
889 {
890 // Update the process state to an error state if we have a channel
891 // error before we're connected. This fixes certain failures,
892 // but does not address the full range of possible issues described
893 // in the FIXME comment below.
894 MonitorAutoLock lock(mMonitor);
895 if (mProcessState < PROCESS_CONNECTED) {
896 mProcessState = PROCESS_ERROR;
897 lock.Notify();
898 }
899 // FIXME/bug 773925: save up this error for the next listener.
900 }
902 void
903 GeckoChildProcessHost::GetQueuedMessages(std::queue<IPC::Message>& queue)
904 {
905 // If this is called off the IO thread, bad things will happen.
906 DCHECK(MessageLoopForIO::current());
907 swap(queue, mQueue);
908 // We expect the next listener to take over processing of our queue.
909 }
911 void
912 GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event)
913 {
914 if (mDelegate) {
915 mDelegate->OnWaitableEventSignaled(event);
916 }
917 ChildProcessHost::OnWaitableEventSignaled(event);
918 }
920 #ifdef MOZ_NUWA_PROCESS
922 using mozilla::ipc::GeckoExistingProcessHost;
923 using mozilla::ipc::FileDescriptor;
925 GeckoExistingProcessHost::
926 GeckoExistingProcessHost(GeckoProcessType aProcessType,
927 base::ProcessHandle aProcess,
928 const FileDescriptor& aFileDescriptor,
929 ChildPrivileges aPrivileges)
930 : GeckoChildProcessHost(aProcessType, aPrivileges)
931 , mExistingProcessHandle(aProcess)
932 , mExistingFileDescriptor(aFileDescriptor)
933 {
934 NS_ASSERTION(aFileDescriptor.IsValid(),
935 "Expected file descriptor to be valid");
936 }
938 GeckoExistingProcessHost::~GeckoExistingProcessHost()
939 {
940 // Bug 943174: If we don't do this, ~GeckoChildProcessHost will try
941 // to wait on a process that isn't a direct child, and bad things
942 // will happen.
943 SetAlreadyDead();
944 }
946 bool
947 GeckoExistingProcessHost::PerformAsyncLaunch(StringVector aExtraOpts,
948 base::ProcessArchitecture aArch)
949 {
950 SetHandle(mExistingProcessHandle);
952 OpenPrivilegedHandle(base::GetProcId(mExistingProcessHandle));
954 MonitorAutoLock lock(mMonitor);
955 mProcessState = PROCESS_CREATED;
956 lock.Notify();
958 return true;
959 }
961 void
962 GeckoExistingProcessHost::InitializeChannel()
963 {
964 CreateChannel(mExistingFileDescriptor);
966 MonitorAutoLock lock(mMonitor);
967 mProcessState = CHANNEL_INITIALIZED;
968 lock.Notify();
969 }
971 #endif /* MOZ_NUWA_PROCESS */