Bumping manifests a=b2g-bump
[gecko.git] / b2g / app / B2GLoader.cpp
blob8846ac794ef94d94235e42fb6fa5952ea113ec5b
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsXULAppAPI.h"
8 #include "application.ini.h"
9 #include "nsXPCOMGlue.h"
10 #include "nsStringGlue.h"
11 #include "nsCOMPtr.h"
12 #include "nsIFile.h"
13 #include "BinaryPath.h"
14 #include "nsAutoPtr.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
23 #include <dlfcn.h>
25 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
27 #define ASSERT(x) if (!(x)) { MOZ_CRASH(); }
29 // Functions being loaded by XPCOMGlue
30 XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun;
31 XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit;
32 XRE_ProcLoaderPreloadType XRE_ProcLoaderPreload;
33 extern XRE_CreateAppDataType XRE_CreateAppData;
34 extern XRE_GetFileFromPathType XRE_GetFileFromPath;
36 static const nsDynamicFunctionLoad kXULFuncs[] = {
37 { "XRE_ProcLoaderServiceRun", (NSFuncPtr*) &XRE_ProcLoaderServiceRun },
38 { "XRE_ProcLoaderClientInit", (NSFuncPtr*) &XRE_ProcLoaderClientInit },
39 { "XRE_ProcLoaderPreload", (NSFuncPtr*) &XRE_ProcLoaderPreload },
40 { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
41 { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
42 { nullptr, nullptr }
45 typedef mozilla::Vector<int> FdArray;
46 static const int kReservedFileDescriptors = 5;
47 static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1;
49 static int
50 GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen)
52 char *lastSlash = strrchr(aPath, XPCOM_FILE_PATH_SEPARATOR[0]);
53 if (lastSlash == nullptr) {
54 return 0;
56 int cpsz = lastSlash - aPath + 1; // include slash
57 if (aMaxLen <= cpsz) {
58 return 0;
60 strncpy(aOutDir, aPath, cpsz);
61 aOutDir[cpsz] = 0;
62 return cpsz;
65 static bool
66 GetXPCOMPath(const char *aProgram, char *aOutPath, int aMaxLen)
68 nsAutoArrayPtr<char> progBuf(new char[aMaxLen]);
69 nsresult rv = mozilla::BinaryPath::Get(aProgram, progBuf);
70 NS_ENSURE_SUCCESS(rv, false);
72 int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen);
73 NS_ENSURE_TRUE(!!len, false);
75 NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < (unsigned)aMaxLen, false);
76 char *afterSlash = aOutPath + len;
77 strcpy(afterSlash, XPCOM_DLL);
78 return true;
81 static bool
82 LoadLibxul(const char *aXPCOMPath)
84 nsresult rv;
86 XPCOMGlueEnablePreload();
87 rv = XPCOMGlueStartup(aXPCOMPath);
88 NS_ENSURE_SUCCESS(rv, false);
90 rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
91 NS_ENSURE_SUCCESS(rv, false);
93 return true;
96 /**
97 * Return true if |arg| matches the given argument name.
99 static bool
100 IsArg(const char* arg, const char* s)
102 if (*arg == '-') {
103 if (*++arg == '-') {
104 ++arg;
106 return !strcasecmp(arg, s);
109 #if defined(XP_WIN)
110 if (*arg == '/') {
111 return !strcasecmp(++arg, s);
113 #endif
115 return false;
118 static already_AddRefed<nsIFile>
119 GetAppIni(int argc, const char *argv[])
121 nsCOMPtr<nsIFile> appini;
122 nsresult rv;
124 // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
125 // Note that -app must be the *first* argument.
126 const char *appDataFile = getenv("XUL_APP_FILE");
127 if (appDataFile && *appDataFile) {
128 rv = XRE_GetFileFromPath(appDataFile, getter_AddRefs(appini));
129 NS_ENSURE_SUCCESS(rv, nullptr);
130 } else if (argc > 1 && IsArg(argv[1], "app")) {
131 if (argc == 2) {
132 return nullptr;
135 rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(appini));
136 NS_ENSURE_SUCCESS(rv, nullptr);
138 char appEnv[MAXPATHLEN];
139 snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
140 if (putenv(appEnv)) {
141 return nullptr;
145 return appini.forget();
148 static bool
149 LoadStaticData(int argc, const char *argv[])
151 char xpcomPath[MAXPATHLEN];
152 bool ok = GetXPCOMPath(argv[0], xpcomPath, MAXPATHLEN);
153 NS_ENSURE_TRUE(ok, false);
155 ok = LoadLibxul(xpcomPath);
156 NS_ENSURE_TRUE(ok, false);
158 char progDir[MAXPATHLEN];
159 ok = GetDirnameSlash(xpcomPath, progDir, MAXPATHLEN);
160 NS_ENSURE_TRUE(ok, false);
162 nsCOMPtr<nsIFile> appini = GetAppIni(argc, argv);
163 const nsXREAppData *appData;
164 if (appini) {
165 nsresult rv =
166 XRE_CreateAppData(appini, const_cast<nsXREAppData**>(&appData));
167 NS_ENSURE_SUCCESS(rv, false);
168 } else {
169 appData = &sAppData;
172 XRE_ProcLoaderPreload(progDir, appData);
174 if (appini) {
175 XRE_FreeAppData(const_cast<nsXREAppData*>(appData));
178 return true;
182 * Fork and run parent and child process.
184 * The parent is the b2g process and child for Nuwa.
186 static int
187 RunProcesses(int argc, const char *argv[], FdArray& aReservedFds)
190 * The original main() of the b2g process. It is renamed to
191 * b2g_main() for the b2g loader.
193 int b2g_main(int argc, const char *argv[]);
195 int ipcSockets[2] = {-1, -1};
196 int r = socketpair(AF_LOCAL, SOCK_STREAM, 0, ipcSockets);
197 ASSERT(r == 0);
198 int parentSock = ipcSockets[0];
199 int childSock = ipcSockets[1];
201 r = fcntl(parentSock, F_SETFL, O_NONBLOCK);
202 ASSERT(r != -1);
203 r = fcntl(childSock, F_SETFL, O_NONBLOCK);
204 ASSERT(r != -1);
206 pid_t pid = fork();
207 ASSERT(pid >= 0);
208 bool isChildProcess = pid == 0;
210 close(isChildProcess ? parentSock : childSock);
212 if (isChildProcess) {
213 /* The Nuwa process */
214 /* This provides the IPC service of loading Nuwa at the process.
215 * The b2g process would send a IPC message of loading Nuwa
216 * as the replacement of forking and executing plugin-container.
218 return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv,
219 aReservedFds);
222 // The b2g process
223 int childPid = pid;
224 XRE_ProcLoaderClientInit(childPid, parentSock, aReservedFds);
225 return b2g_main(argc, argv);
229 * Reserve the file descriptors that shouldn't be taken for other use for the
230 * child process.
232 static void
233 ReserveFileDescriptors(FdArray& aReservedFds)
235 for (int i = 0; i < kReservedFileDescriptors; i++) {
236 struct stat fileState;
237 int target = kBeginReserveFileDescriptor + i;
238 if (fstat(target, &fileState) == 0) {
239 MOZ_CRASH("ProcLoader error: a magic file descriptor is occupied.");
242 int fd = open("/dev/null", O_RDWR);
243 if (fd == -1) {
244 MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor.");
247 aReservedFds.append(target);
249 if (fd == target) {
250 // No need to call dup2(). We already occupy the desired file descriptor.
251 continue;
254 if (dup2(fd, target)) {
255 MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor.");
258 close(fd);
263 * B2G Loader is responsible for loading the b2g process and the
264 * Nuwa process. It forks into the parent process, for the b2g
265 * process, and the child process, for the Nuwa process.
267 * The loader loads libxul and performs initialization of static data
268 * before forking, so relocation of libxul and static data can be
269 * shared between the b2g process, the Nuwa process, and the content
270 * processes.
273 main(int argc, const char* argv[])
276 * Reserve file descriptors before loading static data.
278 FdArray reservedFds;
279 ReserveFileDescriptors(reservedFds);
282 * Before fork(), libxul and static data of Gecko are loaded for
283 * sharing.
285 bool ok = LoadStaticData(argc, argv);
286 if (!ok) {
287 return 255;
290 return RunProcesses(argc, argv, reservedFds);