| /* |
| * 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 |
| } |
| |
| #ifdef 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 }, |
| }; |