summaryrefslogtreecommitdiff
path: root/vm/native/dalvik_system_Zygote.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm/native/dalvik_system_Zygote.c')
-rw-r--r--vm/native/dalvik_system_Zygote.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/vm/native/dalvik_system_Zygote.c b/vm/native/dalvik_system_Zygote.c
new file mode 100644
index 0000000..bcc2313
--- /dev/null
+++ b/vm/native/dalvik_system_Zygote.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.Zygote
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <grp.h>
+#include <errno.h>
+
+#if defined(HAVE_PRCTL)
+# include <sys/prctl.h>
+#endif
+
+#define ZYGOTE_LOG_TAG "Zygote"
+
+/* must match values in dalvik.system.Zygote */
+enum {
+ DEBUG_ENABLE_DEBUGGER = 1,
+ DEBUG_ENABLE_CHECKJNI = 1 << 1,
+ DEBUG_ENABLE_ASSERT = 1 << 2,
+ DEBUG_ENABLE_SAFEMODE = 1 << 3,
+};
+
+/*
+ * This signal handler is for zygote mode, since the zygote
+ * must reap its children
+ */
+static void sigchldHandler(int s)
+{
+ pid_t pid;
+ int status;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ /* Log process-death status that we care about. In general it is not
+ safe to call LOG(...) from a signal handler because of possible
+ reentrancy. However, we know a priori that the current implementation
+ of LOG() is safe to call from a SIGCHLD handler in the zygote process.
+ If the LOG() implementation changes its locking strategy or its use
+ of syscalls within the lazy-init critical section, its use here may
+ become unsafe. */
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ LOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)\n",
+ (int) pid, WEXITSTATUS(status));
+ } else {
+ IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) {
+ LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+ "Process %d exited cleanly (%d)\n",
+ (int) pid, WEXITSTATUS(status));
+ }
+ }
+ } else if (WIFSIGNALED(status)) {
+ if (WTERMSIG(status) != SIGKILL) {
+ LOG(LOG_DEBUG, ZYGOTE_LOG_TAG,
+ "Process %d terminated by signal (%d)\n",
+ (int) pid, WTERMSIG(status));
+ } else {
+ IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) {
+ LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+ "Process %d terminated by signal (%d)\n",
+ (int) pid, WTERMSIG(status));
+ }
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ LOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core\n",
+ (int) pid);
+ }
+#endif /* ifdef WCOREDUMP */
+ }
+
+ /*
+ * If the just-crashed process is the system_server, bring down zygote
+ * so that it is restarted by init and system server will be restarted
+ * from there.
+ */
+ if (pid == gDvm.systemServerPid) {
+ LOG(LOG_INFO, ZYGOTE_LOG_TAG,
+ "Exit zygote because system server (%d) has terminated\n",
+ (int) pid);
+ kill(getpid(), SIGKILL);
+ }
+ }
+
+ if (pid < 0) {
+ LOG(LOG_WARN, ZYGOTE_LOG_TAG,
+ "Zygote SIGCHLD error in waitpid: %s\n",strerror(errno));
+ }
+}
+
+/*
+ * configure sigchld handler for the zygote process
+ * This is configured very late, because earlier in the dalvik lifecycle
+ * we can fork() and exec() for the verifier/optimizer, and we
+ * want to waitpid() for those rather than have them be harvested immediately.
+ *
+ * This ends up being called repeatedly before each fork(), but there's
+ * no real harm in that.
+ */
+static void setSignalHandler()
+{
+ int err;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_handler = sigchldHandler;
+
+ err = sigaction (SIGCHLD, &sa, NULL);
+
+ if (err < 0) {
+ LOGW("Error setting SIGCHLD handler: %s", strerror(errno));
+ }
+}
+
+/*
+ * Set the SIGCHLD handler back to default behavior in zygote children
+ */
+static void unsetSignalHandler()
+{
+ int err;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_handler = SIG_DFL;
+
+ err = sigaction (SIGCHLD, &sa, NULL);
+
+ if (err < 0) {
+ LOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
+ }
+}
+
+/*
+ * Calls POSIX setgroups() using the int[] object as an argument.
+ * A NULL argument is tolerated.
+ */
+
+static int setgroupsIntarray(ArrayObject* gidArray)
+{
+ gid_t *gids;
+ u4 i;
+ s4 *contents;
+
+ if (gidArray == NULL) {
+ return 0;
+ }
+
+ /* just in case gid_t and u4 are different... */
+ gids = alloca(sizeof(gid_t) * gidArray->length);
+ contents = (s4 *)gidArray->contents;
+
+ for (i = 0 ; i < gidArray->length ; i++) {
+ gids[i] = (gid_t) contents[i];
+ }
+
+ return setgroups((size_t) gidArray->length, gids);
+}
+
+/*
+ * Sets the resource limits via setrlimit(2) for the values in the
+ * two-dimensional array of integers that's passed in. The second dimension
+ * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
+ * treated as an empty array.
+ *
+ * -1 is returned on error.
+ */
+static int setrlimitsFromArray(ArrayObject* rlimits)
+{
+ u4 i;
+ struct rlimit rlim;
+
+ if (rlimits == NULL) {
+ return 0;
+ }
+
+ memset (&rlim, 0, sizeof(rlim));
+
+ ArrayObject** tuples = (ArrayObject **)(rlimits->contents);
+
+ for (i = 0; i < rlimits->length; i++) {
+ ArrayObject * rlimit_tuple = tuples[i];
+ s4* contents = (s4 *)rlimit_tuple->contents;
+ int err;
+
+ if (rlimit_tuple->length != 3) {
+ LOGE("rlimits array must have a second dimension of size 3");
+ return -1;
+ }
+
+ rlim.rlim_cur = contents[1];
+ rlim.rlim_max = contents[2];
+
+ err = setrlimit(contents[0], &rlim);
+
+ if (err < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* native public static int fork(); */
+static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
+{
+ pid_t pid;
+
+ if (!gDvm.zygote) {
+ dvmThrowException("Ljava/lang/IllegalStateException;",
+ "VM instance not started with -Xzygote");
+
+ RETURN_VOID();
+ }
+
+ if (!dvmGcPreZygoteFork()) {
+ LOGE("pre-fork heap failed\n");
+ dvmAbort();
+ }
+
+ setSignalHandler();
+
+ dvmDumpLoaderStats("zygote");
+ pid = fork();
+
+#ifdef HAVE_ANDROID_OS
+ if (pid == 0) {
+ /* child process */
+ extern int gMallocLeakZygoteChild;
+ gMallocLeakZygoteChild = 1;
+ }
+#endif
+
+ RETURN_INT(pid);
+}
+
+/*
+ * Enable/disable debug features requested by the caller.
+ *
+ * debugger
+ * If set, enable debugging; if not set, disable debugging. This is
+ * easy to handle, because the JDWP thread isn't started until we call
+ * dvmInitAfterZygote().
+ * checkjni
+ * If set, make sure "check JNI" is eabled. This is a little weird,
+ * because we already have the JNIEnv for the main thread set up. However,
+ * since we only have one thread at this point, it's easy to patch up.
+ * assert
+ * If set, make sure assertions are enabled. This gets fairly weird,
+ * because it affects the result of a method called by class initializers,
+ * and hence can't affect pre-loaded/initialized classes.
+ * safemode
+ * If set, operates the VM in the safe mode. The definition of "safe mode" is
+ * implementation dependent and currently only the JIT compiler is disabled.
+ * This is easy to handle because the compiler thread and associated resources
+ * are not requested until we call dvmInitAfterZygote().
+ */
+static void enableDebugFeatures(u4 debugFlags)
+{
+ LOGV("debugFlags is 0x%02x\n", debugFlags);
+
+ gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
+
+ if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) {
+ /* turn it on if it's not already enabled */
+ dvmLateEnableCheckedJni();
+ }
+
+ if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) {
+ /* turn it on if it's not already enabled */
+ dvmLateEnableAssertions();
+ }
+
+ if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) {
+#if defined(WITH_JIT)
+ /* turn off the jit if it is explicitly requested by the app */
+ if (gDvm.executionMode == kExecutionModeJit)
+ gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+ }
+
+#if HAVE_ANDROID_OS
+ if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) {
+ /* To let a non-privileged gdbserver attach to this
+ * process, we must set its dumpable bit flag. However
+ * we are not interested in generating a coredump in
+ * case of a crash, so also set the coredump size to 0
+ * to disable that
+ */
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ LOGE("could not set dumpable bit flag for pid %d: %s",
+ getpid(), strerror(errno));
+ } else {
+ struct rlimit rl;
+ rl.rlim_cur = 0;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+ LOGE("could not disable core file generation for pid %d: %s",
+ getpid(), strerror(errno));
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * Set Linux capability flags.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+static int setCapabilities(int64_t permitted, int64_t effective)
+{
+#ifdef HAVE_ANDROID_OS
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
+
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
+
+ capdata.effective = effective;
+ capdata.permitted = permitted;
+
+ LOGV("CAPSET perm=%llx eff=%llx\n", permitted, effective);
+ if (capset(&capheader, &capdata) != 0)
+ return errno;
+#endif /*HAVE_ANDROID_OS*/
+
+ return 0;
+}
+
+/*
+ * Utility routine to fork zygote and specialize the child process.
+ */
+static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
+{
+ pid_t pid;
+
+ uid_t uid = (uid_t) args[0];
+ gid_t gid = (gid_t) args[1];
+ ArrayObject* gids = (ArrayObject *)args[2];
+ u4 debugFlags = args[3];
+ ArrayObject *rlimits = (ArrayObject *)args[4];
+ int64_t permittedCapabilities, effectiveCapabilities;
+
+ if (isSystemServer) {
+ /*
+ * Don't use GET_ARG_LONG here for now. gcc is generating code
+ * that uses register d8 as a temporary, and that's coming out
+ * scrambled in the child process. b/3138621
+ */
+ //permittedCapabilities = GET_ARG_LONG(args, 5);
+ //effectiveCapabilities = GET_ARG_LONG(args, 7);
+ permittedCapabilities = args[5] | (int64_t) args[6] << 32;
+ effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
+ } else {
+ permittedCapabilities = effectiveCapabilities = 0;
+ }
+
+ if (!gDvm.zygote) {
+ dvmThrowException("Ljava/lang/IllegalStateException;",
+ "VM instance not started with -Xzygote");
+
+ return -1;
+ }
+
+ if (!dvmGcPreZygoteFork()) {
+ LOGE("pre-fork heap failed\n");
+ dvmAbort();
+ }
+
+ setSignalHandler();
+
+ dvmDumpLoaderStats("zygote");
+ pid = fork();
+
+ if (pid == 0) {
+ int err;
+ /* The child process */
+
+#ifdef HAVE_ANDROID_OS
+ extern int gMallocLeakZygoteChild;
+ gMallocLeakZygoteChild = 1;
+
+ /* keep caps across UID change, unless we're staying root */
+ if (uid != 0) {
+ err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ if (err < 0) {
+ LOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
+ dvmAbort();
+ }
+ }
+
+#endif /* HAVE_ANDROID_OS */
+
+ err = setgroupsIntarray(gids);
+
+ if (err < 0) {
+ LOGE("cannot setgroups(): %s", strerror(errno));
+ dvmAbort();
+ }
+
+ err = setrlimitsFromArray(rlimits);
+
+ if (err < 0) {
+ LOGE("cannot setrlimit(): %s", strerror(errno));
+ dvmAbort();
+ }
+
+ err = setgid(gid);
+ if (err < 0) {
+ LOGE("cannot setgid(%d): %s", gid, strerror(errno));
+ dvmAbort();
+ }
+
+ err = setuid(uid);
+ if (err < 0) {
+ LOGE("cannot setuid(%d): %s", uid, strerror(errno));
+ dvmAbort();
+ }
+
+ err = setCapabilities(permittedCapabilities, effectiveCapabilities);
+ if (err != 0) {
+ LOGE("cannot set capabilities (%llx,%llx): %s\n",
+ permittedCapabilities, effectiveCapabilities, strerror(err));
+ dvmAbort();
+ }
+
+ /*
+ * Our system thread ID has changed. Get the new one.
+ */
+ Thread* thread = dvmThreadSelf();
+ thread->systemTid = dvmGetSysThreadId();
+
+ /* configure additional debug options */
+ enableDebugFeatures(debugFlags);
+
+ unsetSignalHandler();
+ gDvm.zygote = false;
+ if (!dvmInitAfterZygote()) {
+ LOGE("error in post-zygote initialization\n");
+ dvmAbort();
+ }
+ } else if (pid > 0) {
+ /* the parent process */
+ }
+
+ return pid;
+}
+
+/* native public static int forkAndSpecialize(int uid, int gid,
+ * int[] gids, int debugFlags);
+ */
+static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
+ JValue* pResult)
+{
+ pid_t pid;
+
+ pid = forkAndSpecializeCommon(args, false);
+
+ RETURN_INT(pid);
+}
+
+/* native public static int forkSystemServer(int uid, int gid,
+ * int[] gids, int debugFlags, long permittedCapabilities,
+ * long effectiveCapabilities);
+ */
+static void Dalvik_dalvik_system_Zygote_forkSystemServer(
+ const u4* args, JValue* pResult)
+{
+ pid_t pid;
+ pid = forkAndSpecializeCommon(args, true);
+
+ /* The zygote process checks whether the child process has died or not. */
+ if (pid > 0) {
+ int status;
+
+ LOGI("System server process %d has been created", pid);
+ gDvm.systemServerPid = pid;
+ /* There is a slight window that the system server process has crashed
+ * but it went unnoticed because we haven't published its pid yet. So
+ * we recheck here just to make sure that all is well.
+ */
+ if (waitpid(pid, &status, WNOHANG) == pid) {
+ LOGE("System server process %d has died. Restarting Zygote!", pid);
+ kill(getpid(), SIGKILL);
+ }
+ }
+ RETURN_INT(pid);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
+ { "fork", "()I",
+ Dalvik_dalvik_system_Zygote_fork },
+ { "forkAndSpecialize", "(II[II[[I)I",
+ Dalvik_dalvik_system_Zygote_forkAndSpecialize },
+ { "forkSystemServer", "(II[II[[IJJ)I",
+ Dalvik_dalvik_system_Zygote_forkSystemServer },
+ { NULL, NULL, NULL },
+};