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