1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/glue/GeckoChildProcessHost.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,971 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "GeckoChildProcessHost.h" 1.11 + 1.12 +#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) 1.13 +#include "sandboxBroker.h" 1.14 +#endif 1.15 + 1.16 +#include "base/command_line.h" 1.17 +#include "base/path_service.h" 1.18 +#include "base/string_util.h" 1.19 +#include "chrome/common/chrome_switches.h" 1.20 +#include "chrome/common/process_watcher.h" 1.21 +#ifdef MOZ_WIDGET_COCOA 1.22 +#include "chrome/common/mach_ipc_mac.h" 1.23 +#include "base/rand_util.h" 1.24 +#include "nsILocalFileMac.h" 1.25 +#endif 1.26 + 1.27 +#include "MainThreadUtils.h" 1.28 +#include "prprf.h" 1.29 +#include "prenv.h" 1.30 + 1.31 +#include "nsExceptionHandler.h" 1.32 + 1.33 +#include "nsDirectoryServiceDefs.h" 1.34 +#include "nsIFile.h" 1.35 + 1.36 +#include "mozilla/ClearOnShutdown.h" 1.37 +#include "mozilla/ipc/BrowserProcessSubThread.h" 1.38 +#include "mozilla/Omnijar.h" 1.39 +#include <sys/stat.h> 1.40 + 1.41 +#ifdef XP_WIN 1.42 +#include "nsIWinTaskbar.h" 1.43 +#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" 1.44 +#endif 1.45 + 1.46 +#include "nsTArray.h" 1.47 +#include "nsClassHashtable.h" 1.48 +#include "nsHashKeys.h" 1.49 + 1.50 +using mozilla::MonitorAutoLock; 1.51 +using mozilla::ipc::GeckoChildProcessHost; 1.52 + 1.53 +#ifdef ANDROID 1.54 +// Like its predecessor in nsExceptionHandler.cpp, this is 1.55 +// the magic number of a file descriptor remapping we must 1.56 +// preserve for the child process. 1.57 +static const int kMagicAndroidSystemPropFd = 5; 1.58 +#endif 1.59 + 1.60 +static const bool kLowRightsSubprocesses = 1.61 + // We currently only attempt to drop privileges on gonk, because we 1.62 + // have no plugins or extensions to worry about breaking. 1.63 +#ifdef MOZ_WIDGET_GONK 1.64 + true 1.65 +#else 1.66 + false 1.67 +#endif 1.68 + ; 1.69 + 1.70 +mozilla::StaticRefPtr<nsIFile> GeckoChildProcessHost::sGreDir; 1.71 +mozilla::DebugOnly<bool> GeckoChildProcessHost::sGreDirCached; 1.72 + 1.73 +static bool 1.74 +ShouldHaveDirectoryService() 1.75 +{ 1.76 + return GeckoProcessType_Default == XRE_GetProcessType(); 1.77 +} 1.78 + 1.79 +template<> 1.80 +struct RunnableMethodTraits<GeckoChildProcessHost> 1.81 +{ 1.82 + static void RetainCallee(GeckoChildProcessHost* obj) { } 1.83 + static void ReleaseCallee(GeckoChildProcessHost* obj) { } 1.84 +}; 1.85 + 1.86 +/*static*/ 1.87 +base::ChildPrivileges 1.88 +GeckoChildProcessHost::DefaultChildPrivileges() 1.89 +{ 1.90 + return (kLowRightsSubprocesses ? 1.91 + base::PRIVILEGES_UNPRIVILEGED : base::PRIVILEGES_INHERIT); 1.92 +} 1.93 + 1.94 +GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType, 1.95 + ChildPrivileges aPrivileges) 1.96 + : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum 1.97 + mProcessType(aProcessType), 1.98 + mSandboxEnabled(true), 1.99 + mPrivileges(aPrivileges), 1.100 + mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"), 1.101 + mProcessState(CREATING_CHANNEL), 1.102 + mDelegate(nullptr), 1.103 + mChildProcessHandle(0) 1.104 +#if defined(MOZ_WIDGET_COCOA) 1.105 + , mChildTask(MACH_PORT_NULL) 1.106 +#endif 1.107 +{ 1.108 + MOZ_COUNT_CTOR(GeckoChildProcessHost); 1.109 +} 1.110 + 1.111 +GeckoChildProcessHost::~GeckoChildProcessHost() 1.112 + 1.113 +{ 1.114 + AssertIOThread(); 1.115 + 1.116 + MOZ_COUNT_DTOR(GeckoChildProcessHost); 1.117 + 1.118 + if (mChildProcessHandle > 0) 1.119 + ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle 1.120 +#if defined(NS_BUILD_REFCNT_LOGGING) 1.121 + , false // don't "force" 1.122 +#endif 1.123 + ); 1.124 + 1.125 +#if defined(MOZ_WIDGET_COCOA) 1.126 + if (mChildTask != MACH_PORT_NULL) 1.127 + mach_port_deallocate(mach_task_self(), mChildTask); 1.128 +#endif 1.129 +} 1.130 + 1.131 +//static 1.132 +void 1.133 +GeckoChildProcessHost::GetPathToBinary(FilePath& exePath) 1.134 +{ 1.135 + if (ShouldHaveDirectoryService()) { 1.136 + MOZ_ASSERT(sGreDirCached); 1.137 + if (sGreDir) { 1.138 +#ifdef OS_WIN 1.139 + nsString path; 1.140 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetPath(path))); 1.141 +#else 1.142 + nsCString path; 1.143 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetNativePath(path))); 1.144 +#endif 1.145 + exePath = FilePath(path.get()); 1.146 +#ifdef MOZ_WIDGET_COCOA 1.147 + // We need to use an App Bundle on OS X so that we can hide 1.148 + // the dock icon. See Bug 557225. 1.149 + exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE); 1.150 +#endif 1.151 + } 1.152 + } 1.153 + 1.154 + if (exePath.empty()) { 1.155 +#ifdef OS_WIN 1.156 + exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program()); 1.157 +#else 1.158 + exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]); 1.159 +#endif 1.160 + exePath = exePath.DirName(); 1.161 + } 1.162 + 1.163 + exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME); 1.164 +} 1.165 + 1.166 +#ifdef MOZ_WIDGET_COCOA 1.167 +class AutoCFTypeObject { 1.168 +public: 1.169 + AutoCFTypeObject(CFTypeRef object) 1.170 + { 1.171 + mObject = object; 1.172 + } 1.173 + ~AutoCFTypeObject() 1.174 + { 1.175 + ::CFRelease(mObject); 1.176 + } 1.177 +private: 1.178 + CFTypeRef mObject; 1.179 +}; 1.180 +#endif 1.181 + 1.182 +nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32_t *result) 1.183 +{ 1.184 + *result = 0; 1.185 + 1.186 +#ifdef MOZ_WIDGET_COCOA 1.187 + CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, 1.188 + (const UInt8*)path, 1.189 + strlen(path), 1.190 + false); 1.191 + if (!url) { 1.192 + return NS_ERROR_FAILURE; 1.193 + } 1.194 + AutoCFTypeObject autoPluginContainerURL(url); 1.195 + 1.196 + CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url); 1.197 + if (!pluginContainerArchs) { 1.198 + return NS_ERROR_FAILURE; 1.199 + } 1.200 + AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs); 1.201 + 1.202 + CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs); 1.203 + for (CFIndex i = 0; i < pluginArchCount; i++) { 1.204 + CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i)); 1.205 + int currentArchInt = 0; 1.206 + if (!::CFNumberGetValue(currentArch, kCFNumberIntType, ¤tArchInt)) { 1.207 + continue; 1.208 + } 1.209 + switch (currentArchInt) { 1.210 + case kCFBundleExecutableArchitectureI386: 1.211 + *result |= base::PROCESS_ARCH_I386; 1.212 + break; 1.213 + case kCFBundleExecutableArchitectureX86_64: 1.214 + *result |= base::PROCESS_ARCH_X86_64; 1.215 + break; 1.216 + case kCFBundleExecutableArchitecturePPC: 1.217 + *result |= base::PROCESS_ARCH_PPC; 1.218 + break; 1.219 + default: 1.220 + break; 1.221 + } 1.222 + } 1.223 + 1.224 + return (*result ? NS_OK : NS_ERROR_FAILURE); 1.225 +#else 1.226 + return NS_ERROR_NOT_IMPLEMENTED; 1.227 +#endif 1.228 +} 1.229 + 1.230 +uint32_t GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type) 1.231 +{ 1.232 +#ifdef MOZ_WIDGET_COCOA 1.233 + if (type == GeckoProcessType_Plugin) { 1.234 + 1.235 + // Cache this, it shouldn't ever change. 1.236 + static uint32_t pluginContainerArchs = 0; 1.237 + if (pluginContainerArchs == 0) { 1.238 + CacheGreDir(); 1.239 + FilePath exePath; 1.240 + GetPathToBinary(exePath); 1.241 + nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs); 1.242 + NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!"); 1.243 + if (NS_FAILED(rv) || pluginContainerArchs == 0) { 1.244 + pluginContainerArchs = base::GetCurrentProcessArchitecture(); 1.245 + } 1.246 + } 1.247 + return pluginContainerArchs; 1.248 + } 1.249 +#endif 1.250 + 1.251 + return base::GetCurrentProcessArchitecture(); 1.252 +} 1.253 + 1.254 +void 1.255 +GeckoChildProcessHost::PrepareLaunch() 1.256 +{ 1.257 +#ifdef MOZ_CRASHREPORTER 1.258 + if (CrashReporter::GetEnabled()) { 1.259 + CrashReporter::OOPInit(); 1.260 + } 1.261 +#endif 1.262 + 1.263 +#ifdef XP_WIN 1.264 + InitWindowsGroupID(); 1.265 +#endif 1.266 + CacheGreDir(); 1.267 +} 1.268 + 1.269 +//static 1.270 +void 1.271 +GeckoChildProcessHost::CacheGreDir() 1.272 +{ 1.273 + // PerformAysncLaunchInternal/GetPathToBinary may be called on the IO thread, 1.274 + // and they want to use the directory service, which needs to happen on the 1.275 + // main thread (in the event that its implemented in JS). So we grab 1.276 + // NS_GRE_DIR here and stash it. 1.277 + 1.278 +#ifdef MOZ_WIDGET_GONK 1.279 + // Apparently, this ASSERT should be present on all platforms. Currently, 1.280 + // this assert causes mochitest failures on the Mac platform if its left in. 1.281 + 1.282 + // B2G overrides the directory service in JS, so this needs to be called 1.283 + // on the main thread. 1.284 + MOZ_ASSERT(NS_IsMainThread()); 1.285 +#endif 1.286 + 1.287 + if (ShouldHaveDirectoryService()) { 1.288 + nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); 1.289 + MOZ_ASSERT(directoryService, "Expected XPCOM to be available"); 1.290 + if (directoryService) { 1.291 + // getter_AddRefs doesn't work with StaticRefPtr, so we need to store the 1.292 + // result in an nsCOMPtr and copy it over. 1.293 + nsCOMPtr<nsIFile> greDir; 1.294 + nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); 1.295 + if (NS_SUCCEEDED(rv)) { 1.296 + sGreDir = greDir; 1.297 + mozilla::ClearOnShutdown(&sGreDir); 1.298 + } 1.299 + } 1.300 + } 1.301 + sGreDirCached = true; 1.302 +} 1.303 + 1.304 +#ifdef XP_WIN 1.305 +void GeckoChildProcessHost::InitWindowsGroupID() 1.306 +{ 1.307 + // On Win7+, pass the application user model to the child, so it can 1.308 + // register with it. This insures windows created by the container 1.309 + // properly group with the parent app on the Win7 taskbar. 1.310 + nsCOMPtr<nsIWinTaskbar> taskbarInfo = 1.311 + do_GetService(NS_TASKBAR_CONTRACTID); 1.312 + if (taskbarInfo) { 1.313 + bool isSupported = false; 1.314 + taskbarInfo->GetAvailable(&isSupported); 1.315 + nsAutoString appId; 1.316 + if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) { 1.317 + mGroupId.Append(appId); 1.318 + } else { 1.319 + mGroupId.AssignLiteral("-"); 1.320 + } 1.321 + } 1.322 +} 1.323 +#endif 1.324 + 1.325 +bool 1.326 +GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch) 1.327 +{ 1.328 + PrepareLaunch(); 1.329 + 1.330 + PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? 1.331 + PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT; 1.332 + MessageLoop* ioLoop = XRE_GetIOMessageLoop(); 1.333 + NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI"); 1.334 + 1.335 + ioLoop->PostTask(FROM_HERE, 1.336 + NewRunnableMethod(this, 1.337 + &GeckoChildProcessHost::RunPerformAsyncLaunch, 1.338 + aExtraOpts, arch)); 1.339 + // NB: this uses a different mechanism than the chromium parent 1.340 + // class. 1.341 + MonitorAutoLock lock(mMonitor); 1.342 + PRIntervalTime waitStart = PR_IntervalNow(); 1.343 + PRIntervalTime current; 1.344 + 1.345 + // We'll receive several notifications, we need to exit when we 1.346 + // have either successfully launched or have timed out. 1.347 + while (mProcessState != PROCESS_CONNECTED) { 1.348 + // If there was an error then return it, don't wait out the timeout. 1.349 + if (mProcessState == PROCESS_ERROR) { 1.350 + break; 1.351 + } 1.352 + 1.353 + lock.Wait(timeoutTicks); 1.354 + 1.355 + if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) { 1.356 + current = PR_IntervalNow(); 1.357 + PRIntervalTime elapsed = current - waitStart; 1.358 + if (elapsed > timeoutTicks) { 1.359 + break; 1.360 + } 1.361 + timeoutTicks = timeoutTicks - elapsed; 1.362 + waitStart = current; 1.363 + } 1.364 + } 1.365 + 1.366 + return mProcessState == PROCESS_CONNECTED; 1.367 +} 1.368 + 1.369 +bool 1.370 +GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts) 1.371 +{ 1.372 + PrepareLaunch(); 1.373 + 1.374 + MessageLoop* ioLoop = XRE_GetIOMessageLoop(); 1.375 + ioLoop->PostTask(FROM_HERE, 1.376 + NewRunnableMethod(this, 1.377 + &GeckoChildProcessHost::RunPerformAsyncLaunch, 1.378 + aExtraOpts, base::GetCurrentProcessArchitecture())); 1.379 + 1.380 + // This may look like the sync launch wait, but we only delay as 1.381 + // long as it takes to create the channel. 1.382 + MonitorAutoLock lock(mMonitor); 1.383 + while (mProcessState < CHANNEL_INITIALIZED) { 1.384 + lock.Wait(); 1.385 + } 1.386 + 1.387 + return true; 1.388 +} 1.389 + 1.390 +bool 1.391 +GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts) 1.392 +{ 1.393 + PrepareLaunch(); 1.394 + 1.395 + MessageLoop* ioLoop = XRE_GetIOMessageLoop(); 1.396 + ioLoop->PostTask(FROM_HERE, 1.397 + NewRunnableMethod(this, 1.398 + &GeckoChildProcessHost::RunPerformAsyncLaunch, 1.399 + aExtraOpts, base::GetCurrentProcessArchitecture())); 1.400 + 1.401 + MonitorAutoLock lock(mMonitor); 1.402 + while (mProcessState < PROCESS_CREATED) { 1.403 + lock.Wait(); 1.404 + } 1.405 + MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle); 1.406 + 1.407 + return mProcessState < PROCESS_ERROR; 1.408 +} 1.409 + 1.410 +void 1.411 +GeckoChildProcessHost::InitializeChannel() 1.412 +{ 1.413 + CreateChannel(); 1.414 + 1.415 + MonitorAutoLock lock(mMonitor); 1.416 + mProcessState = CHANNEL_INITIALIZED; 1.417 + lock.Notify(); 1.418 +} 1.419 + 1.420 +void 1.421 +GeckoChildProcessHost::Join() 1.422 +{ 1.423 + AssertIOThread(); 1.424 + 1.425 + if (!mChildProcessHandle) { 1.426 + return; 1.427 + } 1.428 + 1.429 + // If this fails, there's nothing we can do. 1.430 + base::KillProcess(mChildProcessHandle, 0, /*wait*/true); 1.431 + SetAlreadyDead(); 1.432 +} 1.433 + 1.434 +void 1.435 +GeckoChildProcessHost::SetAlreadyDead() 1.436 +{ 1.437 + mChildProcessHandle = 0; 1.438 +} 1.439 + 1.440 +int32_t GeckoChildProcessHost::mChildCounter = 0; 1.441 + 1.442 +// 1.443 +// Wrapper function for handling GECKO_SEPARATE_NSPR_LOGS 1.444 +// 1.445 +bool 1.446 +GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch) 1.447 +{ 1.448 + // If NSPR log files are not requested, we're done. 1.449 + const char* origLogName = PR_GetEnv("NSPR_LOG_FILE"); 1.450 + if (!origLogName) { 1.451 + return PerformAsyncLaunchInternal(aExtraOpts, arch); 1.452 + } 1.453 + 1.454 + // We currently have no portable way to launch child with environment 1.455 + // different than parent. So temporarily change NSPR_LOG_FILE so child 1.456 + // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at 1.457 + // startup, so it's 'safe' to play with the parent's environment this way.) 1.458 + nsAutoCString setChildLogName("NSPR_LOG_FILE="); 1.459 + setChildLogName.Append(origLogName); 1.460 + 1.461 + // remember original value so we can restore it. 1.462 + // - buffer needs to be permanently allocated for PR_SetEnv() 1.463 + // - Note: this code is not called re-entrantly, nor are restoreOrigLogName 1.464 + // or mChildCounter touched by any other thread, so this is safe. 1.465 + static char* restoreOrigLogName = 0; 1.466 + if (!restoreOrigLogName) 1.467 + restoreOrigLogName = strdup(setChildLogName.get()); 1.468 + 1.469 + // Append child-specific postfix to name 1.470 + setChildLogName.AppendLiteral(".child-"); 1.471 + setChildLogName.AppendInt(++mChildCounter); 1.472 + 1.473 + // Passing temporary to PR_SetEnv is ok here because env gets copied 1.474 + // by exec, etc., to permanent storage in child when process launched. 1.475 + PR_SetEnv(setChildLogName.get()); 1.476 + bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch); 1.477 + 1.478 + // Revert to original value 1.479 + PR_SetEnv(restoreOrigLogName); 1.480 + 1.481 + return retval; 1.482 +} 1.483 + 1.484 +bool 1.485 +GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts, 1.486 + base::ProcessArchitecture aArch) 1.487 +{ 1.488 + InitializeChannel(); 1.489 + return PerformAsyncLaunch(aExtraOpts, aArch); 1.490 +} 1.491 + 1.492 +void 1.493 +#if defined(XP_WIN) 1.494 +AddAppDirToCommandLine(CommandLine& aCmdLine) 1.495 +#else 1.496 +AddAppDirToCommandLine(std::vector<std::string>& aCmdLine) 1.497 +#endif 1.498 +{ 1.499 + // Content processes need access to application resources, so pass 1.500 + // the full application directory path to the child process. 1.501 + if (ShouldHaveDirectoryService()) { 1.502 + nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); 1.503 + NS_ASSERTION(directoryService, "Expected XPCOM to be available"); 1.504 + if (directoryService) { 1.505 + nsCOMPtr<nsIFile> appDir; 1.506 + // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the 1.507 + // current process dir. 1.508 + nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, 1.509 + NS_GET_IID(nsIFile), 1.510 + getter_AddRefs(appDir)); 1.511 + if (NS_SUCCEEDED(rv)) { 1.512 + nsAutoCString path; 1.513 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appDir->GetNativePath(path))); 1.514 +#if defined(XP_WIN) 1.515 + aCmdLine.AppendLooseValue(UTF8ToWide("-appdir")); 1.516 + aCmdLine.AppendLooseValue(UTF8ToWide(path.get())); 1.517 +#else 1.518 + aCmdLine.push_back("-appdir"); 1.519 + aCmdLine.push_back(path.get()); 1.520 +#endif 1.521 + } 1.522 + } 1.523 + } 1.524 +} 1.525 + 1.526 +bool 1.527 +GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch) 1.528 +{ 1.529 + // We rely on the fact that InitializeChannel() has already been processed 1.530 + // on the IO thread before this point is reached. 1.531 + if (!GetChannel()) { 1.532 + return false; 1.533 + } 1.534 + 1.535 + base::ProcessHandle process = 0; 1.536 + 1.537 + // send the child the PID so that it can open a ProcessHandle back to us. 1.538 + // probably don't want to do this in the long run 1.539 + char pidstring[32]; 1.540 + PR_snprintf(pidstring, sizeof(pidstring) - 1, 1.541 + "%ld", base::Process::Current().pid()); 1.542 + 1.543 + const char* const childProcessType = 1.544 + XRE_ChildProcessTypeToString(mProcessType); 1.545 + 1.546 +//-------------------------------------------------- 1.547 +#if defined(OS_POSIX) 1.548 + // For POSIX, we have to be extremely anal about *not* using 1.549 + // std::wstring in code compiled with Mozilla's -fshort-wchar 1.550 + // configuration, because chromium is compiled with -fno-short-wchar 1.551 + // and passing wstrings from one config to the other is unsafe. So 1.552 + // we split the logic here. 1.553 + 1.554 +#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) 1.555 + base::environment_map newEnvVars; 1.556 + ChildPrivileges privs = mPrivileges; 1.557 + if (privs == base::PRIVILEGES_DEFAULT) { 1.558 + privs = DefaultChildPrivileges(); 1.559 + } 1.560 + // XPCOM may not be initialized in some subprocesses. We don't want 1.561 + // to initialize XPCOM just for the directory service, especially 1.562 + // since LD_LIBRARY_PATH is already set correctly in subprocesses 1.563 + // (meaning that we don't need to set that up in the environment). 1.564 + if (ShouldHaveDirectoryService()) { 1.565 + MOZ_ASSERT(sGreDirCached); 1.566 + if (sGreDir) { 1.567 + nsCString path; 1.568 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetNativePath(path))); 1.569 +# if defined(OS_LINUX) || defined(OS_BSD) 1.570 +# if defined(MOZ_WIDGET_ANDROID) 1.571 + path += "/lib"; 1.572 +# endif // MOZ_WIDGET_ANDROID 1.573 + const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH"); 1.574 + nsCString new_ld_lib_path; 1.575 + if (ld_library_path && *ld_library_path) { 1.576 + new_ld_lib_path.Assign(path.get()); 1.577 + new_ld_lib_path.AppendLiteral(":"); 1.578 + new_ld_lib_path.Append(ld_library_path); 1.579 + newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get(); 1.580 + } else { 1.581 + newEnvVars["LD_LIBRARY_PATH"] = path.get(); 1.582 + } 1.583 +# elif OS_MACOSX 1.584 + newEnvVars["DYLD_LIBRARY_PATH"] = path.get(); 1.585 + // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin 1.586 + // process, and has no effect on other subprocesses (the hooks in 1.587 + // libplugin_child_interpose.dylib become noops). But currently it 1.588 + // gets set when launching any kind of subprocess. 1.589 + // 1.590 + // Trigger "dyld interposing" for the dylib that contains 1.591 + // plugin_child_interpose.mm. This allows us to hook OS calls in the 1.592 + // plugin process (ones that don't work correctly in a background 1.593 + // process). Don't break any other "dyld interposing" that has already 1.594 + // been set up by whatever may have launched the browser. 1.595 + const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES"); 1.596 + nsCString interpose; 1.597 + if (prevInterpose) { 1.598 + interpose.Assign(prevInterpose); 1.599 + interpose.AppendLiteral(":"); 1.600 + } 1.601 + interpose.Append(path.get()); 1.602 + interpose.AppendLiteral("/libplugin_child_interpose.dylib"); 1.603 + newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get(); 1.604 +# endif // OS_LINUX 1.605 + } 1.606 + } 1.607 +#endif // OS_LINUX || OS_MACOSX 1.608 + 1.609 + FilePath exePath; 1.610 + GetPathToBinary(exePath); 1.611 + 1.612 +#ifdef MOZ_WIDGET_ANDROID 1.613 + // The java wrapper unpacks this for us but can't make it executable 1.614 + chmod(exePath.value().c_str(), 0700); 1.615 +#endif // MOZ_WIDGET_ANDROID 1.616 + 1.617 +#ifdef ANDROID 1.618 + // Remap the Android property workspace to a well-known int, 1.619 + // and update the environment to reflect the new value for the 1.620 + // child process. 1.621 + const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE"); 1.622 + if (apws) { 1.623 + int fd = atoi(apws); 1.624 + mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd)); 1.625 + 1.626 + char buf[32]; 1.627 + char *szptr = strchr(apws, ','); 1.628 + 1.629 + snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr); 1.630 + newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf; 1.631 + } 1.632 +#endif // ANDROID 1.633 + 1.634 +#ifdef MOZ_WIDGET_GONK 1.635 + if (const char *ldPreloadPath = getenv("LD_PRELOAD")) { 1.636 + newEnvVars["LD_PRELOAD"] = ldPreloadPath; 1.637 + } 1.638 +#endif // MOZ_WIDGET_GONK 1.639 + 1.640 + // remap the IPC socket fd to a well-known int, as the OS does for 1.641 + // STDOUT_FILENO, for example 1.642 + int srcChannelFd, dstChannelFd; 1.643 + channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd); 1.644 + mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd)); 1.645 + 1.646 + // no need for kProcessChannelID, the child process inherits the 1.647 + // other end of the socketpair() from us 1.648 + 1.649 + std::vector<std::string> childArgv; 1.650 + 1.651 + childArgv.push_back(exePath.value()); 1.652 + 1.653 + childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end()); 1.654 + 1.655 + if (Omnijar::IsInitialized()) { 1.656 + // Make sure that child processes can find the omnijar 1.657 + // See XRE_InitCommandLine in nsAppRunner.cpp 1.658 + nsAutoCString path; 1.659 + nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE); 1.660 + if (file && NS_SUCCEEDED(file->GetNativePath(path))) { 1.661 + childArgv.push_back("-greomni"); 1.662 + childArgv.push_back(path.get()); 1.663 + } 1.664 + file = Omnijar::GetPath(Omnijar::APP); 1.665 + if (file && NS_SUCCEEDED(file->GetNativePath(path))) { 1.666 + childArgv.push_back("-appomni"); 1.667 + childArgv.push_back(path.get()); 1.668 + } 1.669 + } 1.670 + 1.671 + // Add the application directory path (-appdir path) 1.672 + AddAppDirToCommandLine(childArgv); 1.673 + 1.674 + childArgv.push_back(pidstring); 1.675 + 1.676 +#if defined(MOZ_CRASHREPORTER) 1.677 +# if defined(OS_LINUX) || defined(OS_BSD) 1.678 + int childCrashFd, childCrashRemapFd; 1.679 + if (!CrashReporter::CreateNotificationPipeForChild( 1.680 + &childCrashFd, &childCrashRemapFd)) 1.681 + return false; 1.682 + if (0 <= childCrashFd) { 1.683 + mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd)); 1.684 + // "true" == crash reporting enabled 1.685 + childArgv.push_back("true"); 1.686 + } 1.687 + else { 1.688 + // "false" == crash reporting disabled 1.689 + childArgv.push_back("false"); 1.690 + } 1.691 +# elif defined(MOZ_WIDGET_COCOA) 1.692 + childArgv.push_back(CrashReporter::GetChildNotificationPipe()); 1.693 +# endif // OS_LINUX 1.694 +#endif 1.695 + 1.696 +#ifdef MOZ_WIDGET_COCOA 1.697 + // Add a mach port to the command line so the child can communicate its 1.698 + // 'task_t' back to the parent. 1.699 + // 1.700 + // Put a random number into the channel name, so that a compromised renderer 1.701 + // can't pretend being the child that's forked off. 1.702 + std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d", 1.703 + base::RandInt(0, std::numeric_limits<int>::max())); 1.704 + childArgv.push_back(mach_connection_name.c_str()); 1.705 +#endif 1.706 + 1.707 + childArgv.push_back(childProcessType); 1.708 + 1.709 + base::LaunchApp(childArgv, mFileMap, 1.710 +#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) 1.711 + newEnvVars, privs, 1.712 +#endif 1.713 + false, &process, arch); 1.714 + 1.715 + // We're in the parent and the child was launched. Close the child FD in the 1.716 + // parent as soon as possible, which will allow the parent to detect when the 1.717 + // child closes its FD (either due to normal exit or due to crash). 1.718 + GetChannel()->CloseClientFileDescriptor(); 1.719 + 1.720 +#ifdef MOZ_WIDGET_COCOA 1.721 + // Wait for the child process to send us its 'task_t' data. 1.722 + const int kTimeoutMs = 10000; 1.723 + 1.724 + MachReceiveMessage child_message; 1.725 + ReceivePort parent_recv_port(mach_connection_name.c_str()); 1.726 + kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs); 1.727 + if (err != KERN_SUCCESS) { 1.728 + std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err)); 1.729 + CHROMIUM_LOG(ERROR) << "parent WaitForMessage() failed: " << errString; 1.730 + return false; 1.731 + } 1.732 + 1.733 + task_t child_task = child_message.GetTranslatedPort(0); 1.734 + if (child_task == MACH_PORT_NULL) { 1.735 + CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(0) failed."; 1.736 + return false; 1.737 + } 1.738 + 1.739 + if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) { 1.740 + CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(1) failed."; 1.741 + return false; 1.742 + } 1.743 + MachPortSender parent_sender(child_message.GetTranslatedPort(1)); 1.744 + 1.745 + MachSendMessage parent_message(/* id= */0); 1.746 + if (!parent_message.AddDescriptor(bootstrap_port)) { 1.747 + CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed."; 1.748 + return false; 1.749 + } 1.750 + 1.751 + err = parent_sender.SendMessage(parent_message, kTimeoutMs); 1.752 + if (err != KERN_SUCCESS) { 1.753 + std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err)); 1.754 + CHROMIUM_LOG(ERROR) << "parent SendMessage() failed: " << errString; 1.755 + return false; 1.756 + } 1.757 +#endif 1.758 + 1.759 +//-------------------------------------------------- 1.760 +#elif defined(OS_WIN) 1.761 + 1.762 + FilePath exePath; 1.763 + GetPathToBinary(exePath); 1.764 + 1.765 + CommandLine cmdLine(exePath.ToWStringHack()); 1.766 + cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id()); 1.767 + 1.768 + for (std::vector<std::string>::iterator it = aExtraOpts.begin(); 1.769 + it != aExtraOpts.end(); 1.770 + ++it) { 1.771 + cmdLine.AppendLooseValue(UTF8ToWide(*it)); 1.772 + } 1.773 + 1.774 + if (Omnijar::IsInitialized()) { 1.775 + // Make sure the child process can find the omnijar 1.776 + // See XRE_InitCommandLine in nsAppRunner.cpp 1.777 + nsAutoString path; 1.778 + nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE); 1.779 + if (file && NS_SUCCEEDED(file->GetPath(path))) { 1.780 + cmdLine.AppendLooseValue(UTF8ToWide("-greomni")); 1.781 + cmdLine.AppendLooseValue(path.get()); 1.782 + } 1.783 + file = Omnijar::GetPath(Omnijar::APP); 1.784 + if (file && NS_SUCCEEDED(file->GetPath(path))) { 1.785 + cmdLine.AppendLooseValue(UTF8ToWide("-appomni")); 1.786 + cmdLine.AppendLooseValue(path.get()); 1.787 + } 1.788 + } 1.789 + 1.790 +#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) 1.791 + if (mSandboxEnabled) { 1.792 + // Tell the process that it should lower its rights after initialization. 1.793 + cmdLine.AppendLooseValue(UTF8ToWide("-sandbox")); 1.794 + } 1.795 +#endif 1.796 + 1.797 + // Add the application directory path (-appdir path) 1.798 + AddAppDirToCommandLine(cmdLine); 1.799 + 1.800 + // XXX Command line params past this point are expected to be at 1.801 + // the end of the command line string, and in a specific order. 1.802 + // See XRE_InitChildProcess in nsEmbedFunction. 1.803 + 1.804 + // Win app model id 1.805 + cmdLine.AppendLooseValue(mGroupId.get()); 1.806 + 1.807 + // Process id 1.808 + cmdLine.AppendLooseValue(UTF8ToWide(pidstring)); 1.809 + 1.810 +#if defined(MOZ_CRASHREPORTER) 1.811 + cmdLine.AppendLooseValue( 1.812 + UTF8ToWide(CrashReporter::GetChildNotificationPipe())); 1.813 +#endif 1.814 + 1.815 + // Process type 1.816 + cmdLine.AppendLooseValue(UTF8ToWide(childProcessType)); 1.817 + 1.818 +#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) 1.819 + if (mSandboxEnabled) { 1.820 + 1.821 + mozilla::SandboxBroker sandboxBroker; 1.822 + sandboxBroker.LaunchApp(cmdLine.program().c_str(), 1.823 + cmdLine.command_line_string().c_str(), 1.824 + &process); 1.825 + } else 1.826 +#endif 1.827 + { 1.828 + base::LaunchApp(cmdLine, false, false, &process); 1.829 + } 1.830 + 1.831 +#else 1.832 +# error Sorry 1.833 +#endif 1.834 + 1.835 + if (!process) { 1.836 + MonitorAutoLock lock(mMonitor); 1.837 + mProcessState = PROCESS_ERROR; 1.838 + lock.Notify(); 1.839 + return false; 1.840 + } 1.841 + // NB: on OS X, we block much longer than we need to in order to 1.842 + // reach this call, waiting for the child process's task_t. The 1.843 + // best way to fix that is to refactor this file, hard. 1.844 + SetHandle(process); 1.845 +#if defined(MOZ_WIDGET_COCOA) 1.846 + mChildTask = child_task; 1.847 +#endif 1.848 + 1.849 + OpenPrivilegedHandle(base::GetProcId(process)); 1.850 + { 1.851 + MonitorAutoLock lock(mMonitor); 1.852 + mProcessState = PROCESS_CREATED; 1.853 + lock.Notify(); 1.854 + } 1.855 + 1.856 + return true; 1.857 +} 1.858 + 1.859 +void 1.860 +GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid) 1.861 +{ 1.862 + if (mChildProcessHandle) { 1.863 + MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle)); 1.864 + return; 1.865 + } 1.866 + if (!base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle)) { 1.867 + NS_RUNTIMEABORT("can't open handle to child process"); 1.868 + } 1.869 +} 1.870 + 1.871 +void 1.872 +GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid) 1.873 +{ 1.874 + OpenPrivilegedHandle(peer_pid); 1.875 + { 1.876 + MonitorAutoLock lock(mMonitor); 1.877 + mProcessState = PROCESS_CONNECTED; 1.878 + lock.Notify(); 1.879 + } 1.880 +} 1.881 + 1.882 +void 1.883 +GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg) 1.884 +{ 1.885 + // We never process messages ourself, just save them up for the next 1.886 + // listener. 1.887 + mQueue.push(aMsg); 1.888 +} 1.889 + 1.890 +void 1.891 +GeckoChildProcessHost::OnChannelError() 1.892 +{ 1.893 + // Update the process state to an error state if we have a channel 1.894 + // error before we're connected. This fixes certain failures, 1.895 + // but does not address the full range of possible issues described 1.896 + // in the FIXME comment below. 1.897 + MonitorAutoLock lock(mMonitor); 1.898 + if (mProcessState < PROCESS_CONNECTED) { 1.899 + mProcessState = PROCESS_ERROR; 1.900 + lock.Notify(); 1.901 + } 1.902 + // FIXME/bug 773925: save up this error for the next listener. 1.903 +} 1.904 + 1.905 +void 1.906 +GeckoChildProcessHost::GetQueuedMessages(std::queue<IPC::Message>& queue) 1.907 +{ 1.908 + // If this is called off the IO thread, bad things will happen. 1.909 + DCHECK(MessageLoopForIO::current()); 1.910 + swap(queue, mQueue); 1.911 + // We expect the next listener to take over processing of our queue. 1.912 +} 1.913 + 1.914 +void 1.915 +GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event) 1.916 +{ 1.917 + if (mDelegate) { 1.918 + mDelegate->OnWaitableEventSignaled(event); 1.919 + } 1.920 + ChildProcessHost::OnWaitableEventSignaled(event); 1.921 +} 1.922 + 1.923 +#ifdef MOZ_NUWA_PROCESS 1.924 + 1.925 +using mozilla::ipc::GeckoExistingProcessHost; 1.926 +using mozilla::ipc::FileDescriptor; 1.927 + 1.928 +GeckoExistingProcessHost:: 1.929 +GeckoExistingProcessHost(GeckoProcessType aProcessType, 1.930 + base::ProcessHandle aProcess, 1.931 + const FileDescriptor& aFileDescriptor, 1.932 + ChildPrivileges aPrivileges) 1.933 + : GeckoChildProcessHost(aProcessType, aPrivileges) 1.934 + , mExistingProcessHandle(aProcess) 1.935 + , mExistingFileDescriptor(aFileDescriptor) 1.936 +{ 1.937 + NS_ASSERTION(aFileDescriptor.IsValid(), 1.938 + "Expected file descriptor to be valid"); 1.939 +} 1.940 + 1.941 +GeckoExistingProcessHost::~GeckoExistingProcessHost() 1.942 +{ 1.943 + // Bug 943174: If we don't do this, ~GeckoChildProcessHost will try 1.944 + // to wait on a process that isn't a direct child, and bad things 1.945 + // will happen. 1.946 + SetAlreadyDead(); 1.947 +} 1.948 + 1.949 +bool 1.950 +GeckoExistingProcessHost::PerformAsyncLaunch(StringVector aExtraOpts, 1.951 + base::ProcessArchitecture aArch) 1.952 +{ 1.953 + SetHandle(mExistingProcessHandle); 1.954 + 1.955 + OpenPrivilegedHandle(base::GetProcId(mExistingProcessHandle)); 1.956 + 1.957 + MonitorAutoLock lock(mMonitor); 1.958 + mProcessState = PROCESS_CREATED; 1.959 + lock.Notify(); 1.960 + 1.961 + return true; 1.962 +} 1.963 + 1.964 +void 1.965 +GeckoExistingProcessHost::InitializeChannel() 1.966 +{ 1.967 + CreateChannel(mExistingFileDescriptor); 1.968 + 1.969 + MonitorAutoLock lock(mMonitor); 1.970 + mProcessState = CHANNEL_INITIALIZED; 1.971 + lock.Notify(); 1.972 +} 1.973 + 1.974 +#endif /* MOZ_NUWA_PROCESS */