1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla IPC.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Ben Turner <bent.mozilla@gmail.com>.
20 : * Portions created by the Initial Developer are Copyright (C) 2009
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "GeckoChildProcessHost.h"
40 :
41 : #include "base/command_line.h"
42 : #include "base/path_service.h"
43 : #include "base/string_util.h"
44 : #include "chrome/common/chrome_switches.h"
45 : #include "chrome/common/process_watcher.h"
46 : #ifdef MOZ_WIDGET_COCOA
47 : #include "chrome/common/mach_ipc_mac.h"
48 : #include "base/rand_util.h"
49 : #include "nsILocalFileMac.h"
50 : #endif
51 :
52 : #include "prprf.h"
53 : #include "prenv.h"
54 :
55 : #if defined(OS_LINUX)
56 : # define XP_LINUX 1
57 : #endif
58 : #include "nsExceptionHandler.h"
59 :
60 : #include "nsDirectoryServiceDefs.h"
61 : #include "nsIFile.h"
62 : #include "nsILocalFile.h"
63 :
64 : #include "mozilla/ipc/BrowserProcessSubThread.h"
65 : #include "mozilla/Omnijar.h"
66 : #include <sys/stat.h>
67 :
68 : #ifdef XP_WIN
69 : #include "nsIWinTaskbar.h"
70 : #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
71 : #endif
72 :
73 : #ifdef MOZ_WIDGET_ANDROID
74 : #include "APKOpen.h"
75 : #endif
76 :
77 : using mozilla::MonitorAutoLock;
78 : using mozilla::ipc::GeckoChildProcessHost;
79 :
80 : #ifdef MOZ_WIDGET_ANDROID
81 : // Like its predecessor in nsExceptionHandler.cpp, this is
82 : // the magic number of a file descriptor remapping we must
83 : // preserve for the child process.
84 : static const int kMagicAndroidSystemPropFd = 5;
85 : #endif
86 :
87 : static bool
88 0 : ShouldHaveDirectoryService()
89 : {
90 0 : return GeckoProcessType_Default == XRE_GetProcessType();
91 : }
92 :
93 : template<>
94 : struct RunnableMethodTraits<GeckoChildProcessHost>
95 0 : {
96 0 : static void RetainCallee(GeckoChildProcessHost* obj) { }
97 0 : static void ReleaseCallee(GeckoChildProcessHost* obj) { }
98 : };
99 :
100 0 : GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
101 : base::WaitableEventWatcher::Delegate* aDelegate)
102 : : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum
103 : mProcessType(aProcessType),
104 : mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
105 : mLaunched(false),
106 : mChannelInitialized(false),
107 : mDelegate(aDelegate),
108 0 : mChildProcessHandle(0)
109 : #if defined(MOZ_WIDGET_COCOA)
110 : , mChildTask(MACH_PORT_NULL)
111 : #endif
112 : {
113 0 : MOZ_COUNT_CTOR(GeckoChildProcessHost);
114 :
115 0 : MessageLoop* ioLoop = XRE_GetIOMessageLoop();
116 : ioLoop->PostTask(FROM_HERE,
117 : NewRunnableMethod(this,
118 0 : &GeckoChildProcessHost::InitializeChannel));
119 0 : }
120 :
121 0 : GeckoChildProcessHost::~GeckoChildProcessHost()
122 :
123 : {
124 0 : AssertIOThread();
125 :
126 0 : MOZ_COUNT_DTOR(GeckoChildProcessHost);
127 :
128 0 : if (mChildProcessHandle > 0)
129 : ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
130 : #if defined(NS_BUILD_REFCNT_LOGGING)
131 : , false // don't "force"
132 : #endif
133 0 : );
134 :
135 : #if defined(MOZ_WIDGET_COCOA)
136 : if (mChildTask != MACH_PORT_NULL)
137 : mach_port_deallocate(mach_task_self(), mChildTask);
138 : #endif
139 0 : }
140 :
141 0 : void GetPathToBinary(FilePath& exePath)
142 : {
143 : #if defined(OS_WIN)
144 : exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
145 : exePath = exePath.DirName();
146 : exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
147 : #elif defined(OS_POSIX)
148 0 : if (ShouldHaveDirectoryService()) {
149 0 : nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
150 0 : NS_ASSERTION(directoryService, "Expected XPCOM to be available");
151 0 : if (directoryService) {
152 0 : nsCOMPtr<nsIFile> greDir;
153 0 : nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
154 0 : if (NS_SUCCEEDED(rv)) {
155 0 : nsCString path;
156 0 : greDir->GetNativePath(path);
157 0 : exePath = FilePath(path.get());
158 : #ifdef MOZ_WIDGET_COCOA
159 : // We need to use an App Bundle on OS X so that we can hide
160 : // the dock icon. See Bug 557225.
161 : exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
162 : #endif
163 : }
164 : }
165 : }
166 :
167 0 : if (exePath.empty()) {
168 0 : exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
169 0 : exePath = exePath.DirName();
170 : }
171 :
172 0 : exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
173 : #endif
174 0 : }
175 :
176 : #ifdef MOZ_WIDGET_COCOA
177 : class AutoCFTypeObject {
178 : public:
179 : AutoCFTypeObject(CFTypeRef object)
180 : {
181 : mObject = object;
182 : }
183 : ~AutoCFTypeObject()
184 : {
185 : ::CFRelease(mObject);
186 : }
187 : private:
188 : CFTypeRef mObject;
189 : };
190 : #endif
191 :
192 0 : nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32 *result)
193 : {
194 0 : *result = 0;
195 :
196 : #ifdef MOZ_WIDGET_COCOA
197 : CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
198 : (const UInt8*)path,
199 : strlen(path),
200 : false);
201 : if (!url) {
202 : return NS_ERROR_FAILURE;
203 : }
204 : AutoCFTypeObject autoPluginContainerURL(url);
205 :
206 : CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url);
207 : if (!pluginContainerArchs) {
208 : return NS_ERROR_FAILURE;
209 : }
210 : AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs);
211 :
212 : CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs);
213 : for (CFIndex i = 0; i < pluginArchCount; i++) {
214 : CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i));
215 : int currentArchInt = 0;
216 : if (!::CFNumberGetValue(currentArch, kCFNumberIntType, ¤tArchInt)) {
217 : continue;
218 : }
219 : switch (currentArchInt) {
220 : case kCFBundleExecutableArchitectureI386:
221 : *result |= base::PROCESS_ARCH_I386;
222 : break;
223 : case kCFBundleExecutableArchitectureX86_64:
224 : *result |= base::PROCESS_ARCH_X86_64;
225 : break;
226 : case kCFBundleExecutableArchitecturePPC:
227 : *result |= base::PROCESS_ARCH_PPC;
228 : break;
229 : default:
230 : break;
231 : }
232 : }
233 :
234 : return (*result ? NS_OK : NS_ERROR_FAILURE);
235 : #else
236 0 : return NS_ERROR_NOT_IMPLEMENTED;
237 : #endif
238 : }
239 :
240 0 : uint32 GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type)
241 : {
242 : #ifdef MOZ_WIDGET_COCOA
243 : if (type == GeckoProcessType_Plugin) {
244 : // Cache this, it shouldn't ever change.
245 : static uint32 pluginContainerArchs = 0;
246 : if (pluginContainerArchs == 0) {
247 : FilePath exePath;
248 : GetPathToBinary(exePath);
249 : nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs);
250 : NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!");
251 : if (NS_FAILED(rv) || pluginContainerArchs == 0) {
252 : pluginContainerArchs = base::GetCurrentProcessArchitecture();
253 : }
254 : }
255 : return pluginContainerArchs;
256 : }
257 : #endif
258 :
259 0 : return base::GetCurrentProcessArchitecture();
260 : }
261 :
262 : #ifdef XP_WIN
263 : void GeckoChildProcessHost::InitWindowsGroupID()
264 : {
265 : // On Win7+, pass the application user model to the child, so it can
266 : // register with it. This insures windows created by the container
267 : // properly group with the parent app on the Win7 taskbar.
268 : nsCOMPtr<nsIWinTaskbar> taskbarInfo =
269 : do_GetService(NS_TASKBAR_CONTRACTID);
270 : if (taskbarInfo) {
271 : bool isSupported = false;
272 : taskbarInfo->GetAvailable(&isSupported);
273 : nsAutoString appId;
274 : if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) {
275 : mGroupId.Append(appId);
276 : } else {
277 : mGroupId.AssignLiteral("-");
278 : }
279 : }
280 : }
281 : #endif
282 :
283 : bool
284 0 : GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
285 : {
286 : #ifdef XP_WIN
287 : InitWindowsGroupID();
288 : #endif
289 :
290 : PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ?
291 0 : PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
292 0 : MessageLoop* ioLoop = XRE_GetIOMessageLoop();
293 0 : NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI");
294 :
295 : ioLoop->PostTask(FROM_HERE,
296 : NewRunnableMethod(this,
297 : &GeckoChildProcessHost::PerformAsyncLaunch,
298 0 : aExtraOpts, arch));
299 : // NB: this uses a different mechanism than the chromium parent
300 : // class.
301 0 : MonitorAutoLock lock(mMonitor);
302 0 : PRIntervalTime waitStart = PR_IntervalNow();
303 : PRIntervalTime current;
304 :
305 : // We'll receive several notifications, we need to exit when we
306 : // have either successfully launched or have timed out.
307 0 : while (!mLaunched) {
308 0 : lock.Wait(timeoutTicks);
309 :
310 0 : if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) {
311 0 : current = PR_IntervalNow();
312 0 : PRIntervalTime elapsed = current - waitStart;
313 0 : if (elapsed > timeoutTicks) {
314 0 : break;
315 : }
316 0 : timeoutTicks = timeoutTicks - elapsed;
317 0 : waitStart = current;
318 : }
319 : }
320 :
321 0 : return mLaunched;
322 : }
323 :
324 : bool
325 0 : GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
326 : {
327 : #ifdef XP_WIN
328 : InitWindowsGroupID();
329 : #endif
330 :
331 0 : MessageLoop* ioLoop = XRE_GetIOMessageLoop();
332 : ioLoop->PostTask(FROM_HERE,
333 : NewRunnableMethod(this,
334 : &GeckoChildProcessHost::PerformAsyncLaunch,
335 0 : aExtraOpts, base::GetCurrentProcessArchitecture()));
336 :
337 : // This may look like the sync launch wait, but we only delay as
338 : // long as it takes to create the channel.
339 0 : MonitorAutoLock lock(mMonitor);
340 0 : while (!mChannelInitialized) {
341 0 : lock.Wait();
342 : }
343 :
344 0 : return true;
345 : }
346 :
347 : void
348 0 : GeckoChildProcessHost::InitializeChannel()
349 : {
350 0 : CreateChannel();
351 :
352 0 : MonitorAutoLock lock(mMonitor);
353 0 : mChannelInitialized = true;
354 0 : lock.Notify();
355 0 : }
356 :
357 : PRInt32 GeckoChildProcessHost::mChildCounter = 0;
358 :
359 : //
360 : // Wrapper function for handling GECKO_SEPARATE_NSPR_LOGS
361 : //
362 : bool
363 0 : GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch)
364 : {
365 : // If separate NSPR log files are not requested, we're done.
366 0 : const char* origLogName = PR_GetEnv("NSPR_LOG_FILE");
367 0 : const char* separateLogs = PR_GetEnv("GECKO_SEPARATE_NSPR_LOGS");
368 0 : if (!origLogName || !separateLogs || !*separateLogs ||
369 : *separateLogs == '0' || *separateLogs == 'N' || *separateLogs == 'n') {
370 0 : return PerformAsyncLaunchInternal(aExtraOpts, arch);
371 : }
372 :
373 : // We currently have no portable way to launch child with environment
374 : // different than parent. So temporarily change NSPR_LOG_FILE so child
375 : // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
376 : // startup, so it's 'safe' to play with the parent's environment this way.)
377 0 : nsCAutoString setChildLogName("NSPR_LOG_FILE=");
378 0 : setChildLogName.Append(origLogName);
379 :
380 : // remember original value so we can restore it.
381 : // - buffer needs to be permanently allocated for PR_SetEnv()
382 : // - Note: this code is not called re-entrantly, nor are restoreOrigLogName
383 : // or mChildCounter touched by any other thread, so this is safe.
384 : static char* restoreOrigLogName = 0;
385 0 : if (!restoreOrigLogName)
386 0 : restoreOrigLogName = strdup(setChildLogName.get());
387 :
388 : // Append child-specific postfix to name
389 0 : setChildLogName.AppendLiteral(".child-");
390 0 : setChildLogName.AppendInt(++mChildCounter);
391 :
392 : // Passing temporary to PR_SetEnv is ok here because env gets copied
393 : // by exec, etc., to permanent storage in child when process launched.
394 0 : PR_SetEnv(setChildLogName.get());
395 0 : bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
396 :
397 : // Revert to original value
398 0 : PR_SetEnv(restoreOrigLogName);
399 :
400 0 : return retval;
401 : }
402 :
403 : bool
404 0 : GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
405 : {
406 : // FIXME/cjones: make this work from non-IO threads, too
407 :
408 : // We rely on the fact that InitializeChannel() has already been processed
409 : // on the IO thread before this point is reached.
410 0 : if (!GetChannel()) {
411 0 : return false;
412 : }
413 :
414 : base::ProcessHandle process;
415 :
416 : // send the child the PID so that it can open a ProcessHandle back to us.
417 : // probably don't want to do this in the long run
418 : char pidstring[32];
419 : PR_snprintf(pidstring, sizeof(pidstring) - 1,
420 0 : "%ld", base::Process::Current().pid());
421 :
422 : const char* const childProcessType =
423 0 : XRE_ChildProcessTypeToString(mProcessType);
424 :
425 : //--------------------------------------------------
426 : #if defined(OS_POSIX)
427 : // For POSIX, we have to be extremely anal about *not* using
428 : // std::wstring in code compiled with Mozilla's -fshort-wchar
429 : // configuration, because chromium is compiled with -fno-short-wchar
430 : // and passing wstrings from one config to the other is unsafe. So
431 : // we split the logic here.
432 :
433 : #if defined(OS_LINUX) || defined(OS_MACOSX)
434 0 : base::environment_map newEnvVars;
435 : // XPCOM may not be initialized in some subprocesses. We don't want
436 : // to initialize XPCOM just for the directory service, especially
437 : // since LD_LIBRARY_PATH is already set correctly in subprocesses
438 : // (meaning that we don't need to set that up in the environment).
439 0 : if (ShouldHaveDirectoryService()) {
440 0 : nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
441 0 : NS_ASSERTION(directoryService, "Expected XPCOM to be available");
442 0 : if (directoryService) {
443 0 : nsCOMPtr<nsIFile> greDir;
444 0 : nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
445 0 : if (NS_SUCCEEDED(rv)) {
446 0 : nsCString path;
447 0 : greDir->GetNativePath(path);
448 : # ifdef OS_LINUX
449 : # ifdef MOZ_WIDGET_ANDROID
450 : path += "/lib";
451 : # endif // MOZ_WIDGET_ANDROID
452 0 : const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
453 0 : nsCString new_ld_lib_path;
454 0 : if (ld_library_path && *ld_library_path) {
455 0 : new_ld_lib_path.Assign(path.get());
456 0 : new_ld_lib_path.AppendLiteral(":");
457 0 : new_ld_lib_path.Append(ld_library_path);
458 0 : newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
459 : } else {
460 0 : newEnvVars["LD_LIBRARY_PATH"] = path.get();
461 : }
462 : # elif OS_MACOSX
463 : newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
464 : // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
465 : // process, and has no effect on other subprocesses (the hooks in
466 : // libplugin_child_interpose.dylib become noops). But currently it
467 : // gets set when launching any kind of subprocess.
468 : //
469 : // Trigger "dyld interposing" for the dylib that contains
470 : // plugin_child_interpose.mm. This allows us to hook OS calls in the
471 : // plugin process (ones that don't work correctly in a background
472 : // process). Don't break any other "dyld interposing" that has already
473 : // been set up by whatever may have launched the browser.
474 : const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
475 : nsCString interpose;
476 : if (prevInterpose) {
477 : interpose.Assign(prevInterpose);
478 : interpose.AppendLiteral(":");
479 : }
480 : interpose.Append(path.get());
481 : interpose.AppendLiteral("/libplugin_child_interpose.dylib");
482 : newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
483 : # endif // OS_LINUX
484 : }
485 : }
486 : }
487 : #endif // OS_LINUX || OS_MACOSX
488 :
489 0 : FilePath exePath;
490 0 : GetPathToBinary(exePath);
491 :
492 : #ifdef MOZ_WIDGET_ANDROID
493 : // The java wrapper unpacks this for us but can't make it executable
494 : chmod(exePath.value().c_str(), 0700);
495 : int cacheCount = 0;
496 : const struct lib_cache_info * cache = getLibraryCache();
497 : nsCString cacheStr;
498 : while (cache &&
499 : cacheCount++ < MAX_LIB_CACHE_ENTRIES &&
500 : strlen(cache->name)) {
501 : mFileMap.push_back(std::pair<int,int>(cache->fd, cache->fd));
502 : cacheStr.Append(cache->name);
503 : cacheStr.AppendPrintf(":%d;", cache->fd);
504 : cache++;
505 : }
506 : // fill the last arg with something if there's no cache
507 : if (cacheStr.IsEmpty())
508 : cacheStr.AppendLiteral("-");
509 :
510 : // Remap the Android property workspace to a well-known int,
511 : // and update the environment to reflect the new value for the
512 : // child process.
513 : const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE");
514 : if (apws) {
515 : int fd = atoi(apws);
516 : mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd));
517 :
518 : char buf[32];
519 : char *szptr = strchr(apws, ',');
520 :
521 : snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr);
522 : newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf;
523 : }
524 : #endif // MOZ_WIDGET_ANDROID
525 :
526 : // remap the IPC socket fd to a well-known int, as the OS does for
527 : // STDOUT_FILENO, for example
528 : int srcChannelFd, dstChannelFd;
529 0 : channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
530 0 : mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
531 :
532 : // no need for kProcessChannelID, the child process inherits the
533 : // other end of the socketpair() from us
534 :
535 0 : std::vector<std::string> childArgv;
536 :
537 0 : childArgv.push_back(exePath.value());
538 :
539 0 : childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
540 :
541 0 : if (Omnijar::IsInitialized()) {
542 : // Make sure that child processes can find the omnijar
543 : // See XRE_InitCommandLine in nsAppRunner.cpp
544 0 : nsCAutoString path;
545 0 : nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
546 0 : if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
547 0 : childArgv.push_back("-greomni");
548 0 : childArgv.push_back(path.get());
549 : }
550 0 : file = Omnijar::GetPath(Omnijar::APP);
551 0 : if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
552 0 : childArgv.push_back("-appomni");
553 0 : childArgv.push_back(path.get());
554 : }
555 : }
556 :
557 0 : childArgv.push_back(pidstring);
558 :
559 : #if defined(MOZ_CRASHREPORTER)
560 : # if defined(OS_LINUX)
561 : int childCrashFd, childCrashRemapFd;
562 0 : if (!CrashReporter::CreateNotificationPipeForChild(
563 0 : &childCrashFd, &childCrashRemapFd))
564 0 : return false;
565 0 : if (0 <= childCrashFd) {
566 0 : mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
567 : // "true" == crash reporting enabled
568 0 : childArgv.push_back("true");
569 : }
570 : else {
571 : // "false" == crash reporting disabled
572 0 : childArgv.push_back("false");
573 : }
574 : # elif defined(MOZ_WIDGET_COCOA)
575 : childArgv.push_back(CrashReporter::GetChildNotificationPipe());
576 : # endif // OS_LINUX
577 : #endif
578 :
579 : #ifdef MOZ_WIDGET_COCOA
580 : // Add a mach port to the command line so the child can communicate its
581 : // 'task_t' back to the parent.
582 : //
583 : // Put a random number into the channel name, so that a compromised renderer
584 : // can't pretend being the child that's forked off.
585 : std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
586 : base::RandInt(0, std::numeric_limits<int>::max()));
587 : childArgv.push_back(mach_connection_name.c_str());
588 : #endif
589 :
590 0 : childArgv.push_back(childProcessType);
591 :
592 : #ifdef MOZ_WIDGET_ANDROID
593 : childArgv.push_back(cacheStr.get());
594 : #endif
595 :
596 : base::LaunchApp(childArgv, mFileMap,
597 : #if defined(OS_LINUX) || defined(OS_MACOSX)
598 : newEnvVars,
599 : #endif
600 0 : false, &process, arch);
601 :
602 : #ifdef MOZ_WIDGET_COCOA
603 : // Wait for the child process to send us its 'task_t' data.
604 : const int kTimeoutMs = 10000;
605 :
606 : MachReceiveMessage child_message;
607 : ReceivePort parent_recv_port(mach_connection_name.c_str());
608 : kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
609 : if (err != KERN_SUCCESS) {
610 : std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
611 : LOG(ERROR) << "parent WaitForMessage() failed: " << errString;
612 : return false;
613 : }
614 :
615 : task_t child_task = child_message.GetTranslatedPort(0);
616 : if (child_task == MACH_PORT_NULL) {
617 : LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
618 : return false;
619 : }
620 :
621 : if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
622 : LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
623 : return false;
624 : }
625 : MachPortSender parent_sender(child_message.GetTranslatedPort(1));
626 :
627 : MachSendMessage parent_message(/* id= */0);
628 : if (!parent_message.AddDescriptor(bootstrap_port)) {
629 : LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
630 : return false;
631 : }
632 :
633 : err = parent_sender.SendMessage(parent_message, kTimeoutMs);
634 : if (err != KERN_SUCCESS) {
635 : std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
636 : LOG(ERROR) << "parent SendMessage() failed: " << errString;
637 : return false;
638 : }
639 : #endif
640 :
641 : //--------------------------------------------------
642 : #elif defined(OS_WIN)
643 :
644 : FilePath exePath;
645 : GetPathToBinary(exePath);
646 :
647 : CommandLine cmdLine(exePath.ToWStringHack());
648 : cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
649 :
650 : for (std::vector<std::string>::iterator it = aExtraOpts.begin();
651 : it != aExtraOpts.end();
652 : ++it) {
653 : cmdLine.AppendLooseValue(UTF8ToWide(*it));
654 : }
655 :
656 : cmdLine.AppendLooseValue(std::wstring(mGroupId.get()));
657 :
658 : if (Omnijar::IsInitialized()) {
659 : // Make sure the child process can find the omnijar
660 : // See XRE_InitCommandLine in nsAppRunner.cpp
661 : nsAutoString path;
662 : nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
663 : if (file && NS_SUCCEEDED(file->GetPath(path))) {
664 : cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
665 : cmdLine.AppendLooseValue(path.get());
666 : }
667 : file = Omnijar::GetPath(Omnijar::APP);
668 : if (file && NS_SUCCEEDED(file->GetPath(path))) {
669 : cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
670 : cmdLine.AppendLooseValue(path.get());
671 : }
672 : }
673 :
674 : cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
675 :
676 : #if defined(MOZ_CRASHREPORTER)
677 : cmdLine.AppendLooseValue(
678 : UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
679 : #endif
680 :
681 : cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
682 :
683 : base::LaunchApp(cmdLine, false, false, &process);
684 :
685 : #else
686 : # error Sorry
687 : #endif
688 :
689 0 : if (!process) {
690 0 : return false;
691 : }
692 0 : SetHandle(process);
693 : #if defined(MOZ_WIDGET_COCOA)
694 : mChildTask = child_task;
695 : #endif
696 :
697 0 : return true;
698 : }
699 :
700 : void
701 0 : GeckoChildProcessHost::OnChannelConnected(int32 peer_pid)
702 : {
703 0 : MonitorAutoLock lock(mMonitor);
704 0 : mLaunched = true;
705 :
706 0 : if (!base::OpenPrivilegedProcessHandle(peer_pid, &mChildProcessHandle))
707 0 : NS_RUNTIMEABORT("can't open handle to child process");
708 :
709 0 : lock.Notify();
710 0 : }
711 :
712 : // XXX/cjones: these next two methods should basically never be called.
713 : // after the process is launched, its channel will be used to create
714 : // one of our channels, AsyncChannel et al.
715 : void
716 0 : GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg)
717 : {
718 0 : }
719 : void
720 0 : GeckoChildProcessHost::OnChannelError()
721 : {
722 : // XXXbent Notify that the child process is gone?
723 0 : }
724 :
725 : void
726 0 : GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event)
727 : {
728 0 : if (mDelegate) {
729 0 : mDelegate->OnWaitableEventSignaled(event);
730 : }
731 0 : ChildProcessHost::OnWaitableEventSignaled(event);
732 0 : }
|