Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / ipc / glue / ProcessUtils_linux.cpp
blob9366cbfc2b0d71310c9b6eac1c34ac4a04404eb4
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"
9 #include "nsString.h"
11 #include <sys/prctl.h>
13 #ifdef MOZ_B2G_LOADER
15 #include <sys/types.h>
16 #include <unistd.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"
35 #include "prenv.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 */
47 namespace mozilla {
49 namespace ipc {
51 void SetThisProcessName(const char *aName)
53 prctl(PR_SET_NAME, (unsigned long)aName, 0uL, 0uL, 0uL);
56 #ifdef MOZ_B2G_LOADER
57 /**
58 * How does B2G Loader Work?
60 * <<parent process>> <<child process>>
61 * ProcLoaderParent -----> ProcLoaderChild
62 * ^ |
63 * | load() | content_process_main()
64 * | V
65 * ProcLoaderClient Nuwa/plugin-container
66 * ^
67 * | ProcLoaderLoad()
68 * ...
69 * ContentParent
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
79 * start b2g process.
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
128 public:
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;
138 void
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.
147 return;
150 // Destroy self asynchronously.
151 ProcLoaderClientDeinit();
154 static void
155 _ProcLoaderParentDestroy(PProcLoaderParent *aLoader)
157 delete aLoader;
158 sProcLoaderClientOnDeinit = false;
161 bool
162 ProcLoaderParent::RecvLoadComplete(const int32_t &aPid,
163 const int32_t &aCookie)
165 return true;
168 static void
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.
187 static void
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.
200 void
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,
215 sProcLoaderPid,
216 XRE_GetIOMessageLoop(),
217 ParentSide);
218 sProcLoaderLoop = MessageLoop::current();
222 * Shutdown and destroy the client of B2G loader service.
224 static void
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()->
239 PostTask(FROM_HERE,
240 NewRunnableFunction(&_ProcLoaderParentDestroy,
241 procLoaderParent));
244 struct AsyncSendLoadData
246 nsTArray<nsCString> mArgv;
247 nsTArray<nsCString> mEnv;
248 nsTArray<FDRemap> mFdsremap;
249 ChildPrivileges mPrivs;
250 int mCookie;
253 static void
254 AsyncSendLoad(AsyncSendLoadData *aLoad)
256 PProcLoaderParent *loader = sProcLoaderParent;
257 DebugOnly<bool> ok =
258 loader->SendLoad(aLoad->mArgv, aLoad->mEnv, aLoad->mFdsremap,
259 aLoad->mPrivs, aLoad->mCookie);
260 MOZ_ASSERT(ok);
261 delete aLoad;
265 * Request the loader service, the server, to load Nuwa.
267 bool
268 ProcLoaderLoad(const char *aArgv[],
269 const char *aEnvp[],
270 const file_handle_mapping_vector &aFdsRemap,
271 const ChildPrivileges aPrivs,
272 ProcessHandle *aProcessHandle)
274 static int cookie=0;
275 int i;
277 if (sProcLoaderParent == nullptr || sProcLoaderPid == 0) {
278 return false;
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 =
292 aFdsRemap.begin();
293 fdmap != aFdsRemap.end();
294 fdmap++) {
295 fdsremap.AppendElement(FDRemap(FileDescriptor(fdmap->first), fdmap->second));
297 load->mPrivs = aPrivs;
298 load->mCookie = cookie++;
300 *aProcessHandle = sProcLoaderPid;
301 sProcLoaderPid = 0;
303 sProcLoaderLoop->PostTask(FROM_HERE,
304 NewRunnableFunction(AsyncSendLoad, load));
305 return true;
309 class ProcLoaderRunnerBase;
311 static bool sProcLoaderServing = false;
312 static ProcLoaderRunnerBase *sProcLoaderDispatchedTask = nullptr;
314 class ProcLoaderRunnerBase
316 public:
317 virtual int DoWork() = 0;
318 virtual ~ProcLoaderRunnerBase() {}
322 class ProcLoaderNoopRunner : public ProcLoaderRunnerBase {
323 public:
324 virtual int DoWork();
328 ProcLoaderNoopRunner::DoWork() {
329 return 0;
333 * The runner to load Nuwa at the current process.
335 class ProcLoaderLoadRunner : public ProcLoaderRunnerBase {
336 private:
337 const nsTArray<nsCString> mArgv;
338 const nsTArray<nsCString> mEnv;
339 const nsTArray<FDRemap> mFdsRemap;
340 const ChildPrivileges mPrivs;
342 void ShuffleFds();
344 public:
345 ProcLoaderLoadRunner(const InfallibleTArray<nsCString>& aArgv,
346 const InfallibleTArray<nsCString>& aEnv,
347 const InfallibleTArray<FDRemap>& aFdsRemap,
348 const ChildPrivileges aPrivs)
349 : mArgv(aArgv)
350 , mEnv(aEnv)
351 , mFdsRemap(aFdsRemap)
352 , mPrivs(aPrivs) {}
354 int DoWork();
357 void
358 ProcLoaderLoadRunner::ShuffleFds()
360 unsigned int i;
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();
379 toErase++) {
380 if (tofd == *toErase) {
381 sReservedFds->erase(toErase);
382 break;
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()
402 unsigned int i;
404 ShuffleFds();
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++) {
424 free(argv[i]);
426 delete[] argv;
428 return ret;
432 class ProcLoaderChild : public PProcLoaderChild
434 pid_t mPeerPid;
436 public:
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();
450 void
451 ProcLoaderChild::ActorDestroy(ActorDestroyReason aWhy)
455 static void
456 _ProcLoaderChildDestroy(ProcLoaderChild *aChild)
458 aChild->Close();
459 delete aChild;
460 MessageLoop::current()->Quit();
463 bool
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) {
470 return true;
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,
483 this));
484 return true;
487 void
488 ProcLoaderChild::OnChannelError()
490 if (!sProcLoaderServing) {
491 return;
493 sProcLoaderServing = false;
495 PProcLoaderChild::OnChannelError();
497 MOZ_ASSERT(sProcLoaderDispatchedTask == nullptr);
498 sProcLoaderDispatchedTask = new ProcLoaderNoopRunner();
500 MessageLoop::current()->PostTask(FROM_HERE,
501 NewRunnableFunction(_ProcLoaderChildDestroy,
502 this));
506 * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
508 class ScopedLogging
510 public:
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.
523 static int
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;
534 char **_argv;
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;
542 gArgv = _argv;
543 gArgc = aArgc;
546 nsresult rv = XRE_InitCommandLine(aArgc, _argv);
547 if (NS_FAILED(rv)) {
548 MOZ_CRASH();
551 TransportDescriptor fd;
552 fd.mFd = base::FileDescriptor(aFd, /*auto_close =*/false);
554 MOZ_ASSERT(!sProcLoaderServing);
555 MessageLoop loop;
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
564 // (connection).
565 loaderChild->Open(transport, aPeerPid, iothread->message_loop());
567 BackgroundHangMonitor::Prohibit();
569 sProcLoaderServing = true;
570 loop.Run();
572 BackgroundHangMonitor::Allow();
574 XRE_DeinitCommandLine();
577 MOZ_ASSERT(sProcLoaderDispatchedTask != nullptr);
578 ProcLoaderRunnerBase *task = sProcLoaderDispatchedTask;
579 sProcLoaderDispatchedTask = nullptr;
580 int ret = task->DoWork();
581 delete task;
583 for (int i = 0; i < aArgc; i++) {
584 free(_argv[i]);
586 delete[] _argv;
588 return ret;
591 #endif /* MOZ_B2G_LOADER */
593 } // namespace ipc
594 } // namespace mozilla
596 #ifdef MOZ_B2G_LOADER
597 void
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
601 // descriptors .
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,
613 aArgc, aArgv,
614 aReservedFds);
616 #endif /* MOZ_B2G_LOADER */