1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 autoindent cindent expandtab: */
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 "ProcessUtils.h"
11 #include <sys/prctl.h>
15 #include <sys/types.h>
18 #include "nsAutoPtr.h"
20 #include "mozilla/Assertions.h"
21 #include "mozilla/ipc/PProcLoaderParent.h"
22 #include "mozilla/ipc/PProcLoaderChild.h"
23 #include "mozilla/ipc/Transport.h"
24 #include "mozilla/ipc/FileDescriptorUtils.h"
25 #include "mozilla/ipc/IOThreadChild.h"
26 #include "mozilla/dom/ContentProcess.h"
27 #include "base/file_descriptor_shuffle.h"
28 #include "mozilla/BackgroundHangMonitor.h"
29 #include "mozilla/DebugOnly.h"
30 #include "mozilla/UniquePtr.h"
31 #include "mozilla/unused.h"
32 #include "base/process_util.h"
33 #include "base/eintr_wrapper.h"
37 #include "nsXULAppAPI.h" // export XRE_* functions
39 #include "nsAppRunner.h"
41 int content_process_main(int argc
, char *argv
[]);
43 typedef mozilla::Vector
<int> FdArray
;
45 #endif /* MOZ_B2G_LOADER */
51 void SetThisProcessName(const char *aName
)
53 prctl(PR_SET_NAME
, (unsigned long)aName
, 0uL, 0uL, 0uL);
58 * How does B2G Loader Work?
60 * <<parent process>> <<child process>>
61 * ProcLoaderParent -----> ProcLoaderChild
63 * | load() | content_process_main()
65 * ProcLoaderClient Nuwa/plugin-container
72 * B2G loader includes an IPC protocol PProcLoader for communication
73 * between client (parent) and server (child). The b2g process is the
74 * client. It requests the server to load/start the Nuwa process with
75 * the given arguments, env variables, and file descriptors.
77 * ProcLoaderClientInit() is called by B2G loader to initialize the
78 * client side, the b2g process. Then the b2g_main() is called to
81 * ProcLoaderClientGoannaInit() is called by XRE_main() to create the
82 * parent actor, |ProcLoaderParent|, of PProcLoader for servicing the
83 * request to run Nuwa process later once Goanna has been initialized.
85 * ProcLoaderServiceRun() is called by the server process. It starts
86 * an IOThread and event loop to serve the |ProcLoaderChild|
87 * implmentation of PProcLoader protocol as the child actor. Once it
88 * recieves a load() request, it stops the IOThread and event loop,
89 * then starts running the main function of the content process with
90 * the given arguments.
92 * NOTE: The server process serves at most one load() request.
95 using namespace mozilla::dom
;
97 using base::ChildPrivileges
;
98 using base::InjectionArc
;
99 using base::InjectiveMultimap
;
100 using base::ProcessHandle
;
101 using base::ProcessId
;
102 using base::SetCurrentProcessPrivileges
;
103 using base::ShuffleFileDescriptors
;
104 using base::file_handle_mapping_vector
;
106 static bool sProcLoaderClientOnDeinit
= false;
107 static DebugOnly
<bool> sProcLoaderClientInitialized
= false;
108 static DebugOnly
<bool> sProcLoaderClientGoannaInitialized
= false;
109 static pid_t sProcLoaderPid
= 0;
110 static int sProcLoaderChannelFd
= -1;
111 static PProcLoaderParent
*sProcLoaderParent
= nullptr;
112 static MessageLoop
*sProcLoaderLoop
= nullptr;
113 static mozilla::UniquePtr
<FdArray
> sReservedFds
;
115 static void ProcLoaderClientDeinit();
118 * Some file descriptors, like the child IPC channel FD, must be opened at
119 * specific numbers. To ensure this, we pre-reserve kReservedFileDescriptors FDs
120 * starting from kBeginReserveFileDescriptor so that operations like
121 * __android_log_print() won't take these magic FDs.
123 static const size_t kReservedFileDescriptors
= 5;
124 static const int kBeginReserveFileDescriptor
= STDERR_FILENO
+ 1;
126 class ProcLoaderParent
: public PProcLoaderParent
129 ProcLoaderParent() {}
130 virtual ~ProcLoaderParent() {}
132 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
134 virtual bool RecvLoadComplete(const int32_t &aPid
,
135 const int32_t &aCookie
) override
;
139 ProcLoaderParent::ActorDestroy(ActorDestroyReason aWhy
)
141 if (aWhy
== AbnormalShutdown
) {
142 NS_WARNING("ProcLoaderParent is destroyed abnormally.");
145 if (sProcLoaderClientOnDeinit
) {
146 // Get error for closing while the channel is already error.
150 // Destroy self asynchronously.
151 ProcLoaderClientDeinit();
155 _ProcLoaderParentDestroy(PProcLoaderParent
*aLoader
)
158 sProcLoaderClientOnDeinit
= false;
162 ProcLoaderParent::RecvLoadComplete(const int32_t &aPid
,
163 const int32_t &aCookie
)
169 CloseFileDescriptors(FdArray
& aFds
)
171 for (size_t i
= 0; i
< aFds
.length(); i
++) {
172 unused
<< HANDLE_EINTR(close(aFds
[i
]));
177 * Initialize the client of B2G loader for loader itself.
179 * The initialization of B2G loader are divided into two stages. First
180 * stage is to collect child info passed from the main program of the
181 * loader. Second stage is to initialize Goanna according to info from the
182 * first stage and make the client of loader service ready.
184 * \param aPeerPid is the pid of the child.
185 * \param aChannelFd is the file descriptor of the socket used for IPC.
188 ProcLoaderClientInit(pid_t aPeerPid
, int aChannelFd
)
190 MOZ_ASSERT(!sProcLoaderClientInitialized
, "call ProcLoaderClientInit() more than once");
191 MOZ_ASSERT(aPeerPid
!= 0 && aChannelFd
!= -1, "invalid argument");
192 sProcLoaderPid
= aPeerPid
;
193 sProcLoaderChannelFd
= aChannelFd
;
194 sProcLoaderClientInitialized
= true;
198 * Initialize the client of B2G loader for Goanna.
201 ProcLoaderClientGoannaInit()
203 MOZ_ASSERT(sProcLoaderClientInitialized
, "call ProcLoaderClientInit() at first");
204 MOZ_ASSERT(!sProcLoaderClientGoannaInitialized
,
205 "call ProcLoaderClientGoannaInit() more than once");
207 sProcLoaderClientGoannaInitialized
= true;
209 TransportDescriptor fd
;
210 fd
.mFd
= base::FileDescriptor(sProcLoaderChannelFd
, /*auto_close=*/ false);
211 sProcLoaderChannelFd
= -1;
212 Transport
*transport
= OpenDescriptor(fd
, Transport::MODE_CLIENT
);
213 sProcLoaderParent
= new ProcLoaderParent();
214 sProcLoaderParent
->Open(transport
,
216 XRE_GetIOMessageLoop(),
218 sProcLoaderLoop
= MessageLoop::current();
222 * Shutdown and destroy the client of B2G loader service.
225 ProcLoaderClientDeinit()
227 MOZ_ASSERT(sProcLoaderClientGoannaInitialized
&& sProcLoaderClientInitialized
);
228 sProcLoaderClientGoannaInitialized
= false;
229 sProcLoaderClientInitialized
= false;
231 sProcLoaderClientOnDeinit
= true;
233 MOZ_ASSERT(sProcLoaderParent
!= nullptr);
234 PProcLoaderParent
*procLoaderParent
= sProcLoaderParent
;
235 sProcLoaderParent
= nullptr;
236 sProcLoaderLoop
= nullptr;
238 MessageLoop::current()->
240 NewRunnableFunction(&_ProcLoaderParentDestroy
,
244 struct AsyncSendLoadData
246 nsTArray
<nsCString
> mArgv
;
247 nsTArray
<nsCString
> mEnv
;
248 nsTArray
<FDRemap
> mFdsremap
;
249 ChildPrivileges mPrivs
;
254 AsyncSendLoad(AsyncSendLoadData
*aLoad
)
256 PProcLoaderParent
*loader
= sProcLoaderParent
;
258 loader
->SendLoad(aLoad
->mArgv
, aLoad
->mEnv
, aLoad
->mFdsremap
,
259 aLoad
->mPrivs
, aLoad
->mCookie
);
265 * Request the loader service, the server, to load Nuwa.
268 ProcLoaderLoad(const char *aArgv
[],
270 const file_handle_mapping_vector
&aFdsRemap
,
271 const ChildPrivileges aPrivs
,
272 ProcessHandle
*aProcessHandle
)
277 if (sProcLoaderParent
== nullptr || sProcLoaderPid
== 0) {
281 AsyncSendLoadData
*load
= new AsyncSendLoadData();
282 nsTArray
<nsCString
> &argv
= load
->mArgv
;
283 for (i
= 0; aArgv
[i
] != nullptr; i
++) {
284 argv
.AppendElement(nsCString(aArgv
[i
]));
286 nsTArray
<nsCString
> &env
= load
->mEnv
;
287 for (i
= 0; aEnvp
[i
] != nullptr; i
++) {
288 env
.AppendElement(nsCString(aEnvp
[i
]));
290 nsTArray
<FDRemap
> &fdsremap
= load
->mFdsremap
;
291 for (file_handle_mapping_vector::const_iterator fdmap
=
293 fdmap
!= aFdsRemap
.end();
295 fdsremap
.AppendElement(FDRemap(FileDescriptor(fdmap
->first
), fdmap
->second
));
297 load
->mPrivs
= aPrivs
;
298 load
->mCookie
= cookie
++;
300 *aProcessHandle
= sProcLoaderPid
;
303 sProcLoaderLoop
->PostTask(FROM_HERE
,
304 NewRunnableFunction(AsyncSendLoad
, load
));
309 class ProcLoaderRunnerBase
;
311 static bool sProcLoaderServing
= false;
312 static ProcLoaderRunnerBase
*sProcLoaderDispatchedTask
= nullptr;
314 class ProcLoaderRunnerBase
317 virtual int DoWork() = 0;
318 virtual ~ProcLoaderRunnerBase() {}
322 class ProcLoaderNoopRunner
: public ProcLoaderRunnerBase
{
324 virtual int DoWork();
328 ProcLoaderNoopRunner::DoWork() {
333 * The runner to load Nuwa at the current process.
335 class ProcLoaderLoadRunner
: public ProcLoaderRunnerBase
{
337 const nsTArray
<nsCString
> mArgv
;
338 const nsTArray
<nsCString
> mEnv
;
339 const nsTArray
<FDRemap
> mFdsRemap
;
340 const ChildPrivileges mPrivs
;
345 ProcLoaderLoadRunner(const InfallibleTArray
<nsCString
>& aArgv
,
346 const InfallibleTArray
<nsCString
>& aEnv
,
347 const InfallibleTArray
<FDRemap
>& aFdsRemap
,
348 const ChildPrivileges aPrivs
)
351 , mFdsRemap(aFdsRemap
)
358 ProcLoaderLoadRunner::ShuffleFds()
362 MOZ_ASSERT(mFdsRemap
.Length() <= kReservedFileDescriptors
);
364 InjectiveMultimap fd_shuffle1
, fd_shuffle2
;
365 fd_shuffle1
.reserve(mFdsRemap
.Length());
366 fd_shuffle2
.reserve(mFdsRemap
.Length());
368 for (i
= 0; i
< mFdsRemap
.Length(); i
++) {
369 const FDRemap
*map
= &mFdsRemap
[i
];
370 int fd
= map
->fd().PlatformHandle();
371 int tofd
= map
->mapto();
373 fd_shuffle1
.push_back(InjectionArc(fd
, tofd
, false));
374 fd_shuffle2
.push_back(InjectionArc(fd
, tofd
, false));
376 // Erase from sReservedFds we will use.
377 for (int* toErase
= sReservedFds
->begin();
378 toErase
< sReservedFds
->end();
380 if (tofd
== *toErase
) {
381 sReservedFds
->erase(toErase
);
387 DebugOnly
<bool> ok
= ShuffleFileDescriptors(&fd_shuffle1
);
389 // Close the FDs that are reserved but not used after
390 // ShuffleFileDescriptors().
391 MOZ_ASSERT(sReservedFds
);
392 CloseFileDescriptors(*sReservedFds
);
393 sReservedFds
= nullptr;
395 // Note that we don'e call ::base::CloseSuperfluousFds() here, assuming that
396 // The file descriptor inherited from the parent are also necessary for us.
400 ProcLoaderLoadRunner::DoWork()
406 unsigned int argc
= mArgv
.Length();
407 char **argv
= new char *[argc
+ 1];
408 for (i
= 0; i
< argc
; i
++) {
409 argv
[i
] = ::strdup(mArgv
[i
].get());
411 argv
[argc
] = nullptr;
413 unsigned int envc
= mEnv
.Length();
414 for (i
= 0; i
< envc
; i
++) {
415 PR_SetEnv(mEnv
[i
].get());
418 SetCurrentProcessPrivileges(mPrivs
);
420 // Start Nuwa (main function)
421 int ret
= content_process_main(argc
, argv
);
423 for (i
= 0; i
< argc
; i
++) {
432 class ProcLoaderChild
: public PProcLoaderChild
437 ProcLoaderChild(pid_t aPeerPid
) : mPeerPid(aPeerPid
) {}
439 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
441 virtual bool RecvLoad(InfallibleTArray
<nsCString
>&& aArgv
,
442 InfallibleTArray
<nsCString
>&& aEnv
,
443 InfallibleTArray
<FDRemap
>&& aFdsremap
,
444 const uint32_t& aPrivs
,
445 const int32_t& aCookie
);
447 virtual void OnChannelError();
451 ProcLoaderChild::ActorDestroy(ActorDestroyReason aWhy
)
456 _ProcLoaderChildDestroy(ProcLoaderChild
*aChild
)
460 MessageLoop::current()->Quit();
464 ProcLoaderChild::RecvLoad(InfallibleTArray
<nsCString
>&& aArgv
,
465 InfallibleTArray
<nsCString
>&& aEnv
,
466 InfallibleTArray
<FDRemap
>&& aFdsRemap
,
467 const uint32_t& aPrivs
,
468 const int32_t& aCookie
) {
469 if (!sProcLoaderServing
) {
472 sProcLoaderServing
= false;
474 MOZ_ASSERT(sProcLoaderDispatchedTask
== nullptr);
475 ChildPrivileges privs
= static_cast<ChildPrivileges
>(aPrivs
);
476 sProcLoaderDispatchedTask
=
477 new ProcLoaderLoadRunner(aArgv
, aEnv
, aFdsRemap
, privs
);
479 SendLoadComplete(mPeerPid
, aCookie
);
481 MessageLoop::current()->PostTask(FROM_HERE
,
482 NewRunnableFunction(_ProcLoaderChildDestroy
,
488 ProcLoaderChild::OnChannelError()
490 if (!sProcLoaderServing
) {
493 sProcLoaderServing
= false;
495 PProcLoaderChild::OnChannelError();
497 MOZ_ASSERT(sProcLoaderDispatchedTask
== nullptr);
498 sProcLoaderDispatchedTask
= new ProcLoaderNoopRunner();
500 MessageLoop::current()->PostTask(FROM_HERE
,
501 NewRunnableFunction(_ProcLoaderChildDestroy
,
506 * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
511 ScopedLogging() { NS_LogInit(); }
512 ~ScopedLogging() { NS_LogTerm(); }
516 * Run service of ProcLoader.
518 * \param aPeerPid is the pid of the parent.
519 * \param aFd is the file descriptor of the socket for IPC.
521 * See the comment near the head of this file.
524 ProcLoaderServiceRun(pid_t aPeerPid
, int aFd
,
525 int aArgc
, const char *aArgv
[],
526 FdArray
& aReservedFds
)
528 // Make a copy of aReservedFds. It will be used when we dup() the magic file
529 // descriptors when ProcLoaderChild::RecvLoad() runs.
530 sReservedFds
= MakeUnique
<FdArray
>(mozilla::Move(aReservedFds
));
532 ScopedLogging logging
;
535 _argv
= new char *[aArgc
+ 1];
536 for (int i
= 0; i
< aArgc
; i
++) {
537 _argv
[i
] = ::strdup(aArgv
[i
]);
538 MOZ_ASSERT(_argv
[i
] != nullptr);
540 _argv
[aArgc
] = nullptr;
546 nsresult rv
= XRE_InitCommandLine(aArgc
, _argv
);
551 TransportDescriptor fd
;
552 fd
.mFd
= base::FileDescriptor(aFd
, /*auto_close =*/false);
554 MOZ_ASSERT(!sProcLoaderServing
);
557 nsAutoPtr
<ContentProcess
> process
;
558 process
= new ContentProcess(aPeerPid
);
559 ChildThread
*iothread
= process
->child_thread();
561 Transport
*transport
= OpenDescriptor(fd
, Transport::MODE_CLIENT
);
562 ProcLoaderChild
*loaderChild
= new ProcLoaderChild(aPeerPid
);
563 // Pass a message loop to initialize (connect) the channel
565 loaderChild
->Open(transport
, aPeerPid
, iothread
->message_loop());
567 BackgroundHangMonitor::Prohibit();
569 sProcLoaderServing
= true;
572 BackgroundHangMonitor::Allow();
574 XRE_DeinitCommandLine();
577 MOZ_ASSERT(sProcLoaderDispatchedTask
!= nullptr);
578 ProcLoaderRunnerBase
*task
= sProcLoaderDispatchedTask
;
579 sProcLoaderDispatchedTask
= nullptr;
580 int ret
= task
->DoWork();
583 for (int i
= 0; i
< aArgc
; i
++) {
591 #endif /* MOZ_B2G_LOADER */
594 } // namespace mozilla
596 #ifdef MOZ_B2G_LOADER
598 XRE_ProcLoaderClientInit(pid_t aPeerPid
, int aChannelFd
, FdArray
& aReservedFds
)
600 // We already performed fork(). It's safe to free the "danger zone" of file
602 mozilla::ipc::CloseFileDescriptors(aReservedFds
);
604 mozilla::ipc::ProcLoaderClientInit(aPeerPid
, aChannelFd
);
608 XRE_ProcLoaderServiceRun(pid_t aPeerPid
, int aFd
,
609 int aArgc
, const char *aArgv
[],
610 FdArray
& aReservedFds
)
612 return mozilla::ipc::ProcLoaderServiceRun(aPeerPid
, aFd
,
616 #endif /* MOZ_B2G_LOADER */