| /* |
| * 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, |
| }; |
| |
| /* |
| * 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 (%d) in waitpid\n",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 errno: %d", 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 errno: %d", 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; |
| int err; |
| |
| 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. |
| */ |
| 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(); |
| } |
| } |
| |
| /* |
| * Utility routine to fork zygote and specialize the child process. |
| */ |
| static pid_t forkAndSpecializeCommon(const u4* args) |
| { |
| 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]; |
| |
| 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) { |
| LOGW("cannot PR_SET_KEEPCAPS errno: %d", errno); |
| } |
| } |
| |
| #endif /* HAVE_ANDROID_OS */ |
| |
| err = setgroupsIntarray(gids); |
| |
| if (err < 0) { |
| LOGW("cannot setgroups() errno: %d", errno); |
| } |
| |
| err = setrlimitsFromArray(rlimits); |
| |
| if (err < 0) { |
| LOGW("cannot setrlimit() errno: %d", errno); |
| } |
| |
| err = setgid(gid); |
| if (err < 0) { |
| LOGW("cannot setgid(%d) errno: %d", gid, errno); |
| } |
| |
| err = setuid(uid); |
| if (err < 0) { |
| LOGW("cannot setuid(%d) errno: %d", uid, errno); |
| } |
| |
| /* |
| * 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); |
| |
| RETURN_INT(pid); |
| } |
| |
| /* native public static int forkSystemServer(int uid, int gid, |
| * int[] gids, int debugFlags); |
| */ |
| static void Dalvik_dalvik_system_Zygote_forkSystemServer( |
| const u4* args, JValue* pResult) |
| { |
| pid_t pid; |
| pid = forkAndSpecializeCommon(args); |
| |
| /* 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[[I)I", |
| Dalvik_dalvik_system_Zygote_forkSystemServer }, |
| { NULL, NULL, NULL }, |
| }; |
| |