dalvik_system_Zygote.cpp revision 21c93dc795dfbea3932e0273b1aa873051dde72c
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * dalvik.system.Zygote 19 */ 20#include "Dalvik.h" 21#include "native/InternalNativePriv.h" 22 23#ifdef HAVE_SELINUX 24#include <selinux/android.h> 25#endif 26 27#include <signal.h> 28#include <sys/types.h> 29#include <sys/wait.h> 30#include <grp.h> 31#include <errno.h> 32#include <paths.h> 33#include <sys/personality.h> 34#include <sys/stat.h> 35#include <sys/mount.h> 36#include <linux/fs.h> 37#include <cutils/fs.h> 38#include <cutils/sched_policy.h> 39#include <cutils/multiuser.h> 40#include <sched.h> 41 42#if defined(HAVE_PRCTL) 43# include <sys/prctl.h> 44#endif 45 46#define ZYGOTE_LOG_TAG "Zygote" 47 48/* must match values in dalvik.system.Zygote */ 49enum { 50 DEBUG_ENABLE_DEBUGGER = 1, 51 DEBUG_ENABLE_CHECKJNI = 1 << 1, 52 DEBUG_ENABLE_ASSERT = 1 << 2, 53 DEBUG_ENABLE_SAFEMODE = 1 << 3, 54 DEBUG_ENABLE_JNI_LOGGING = 1 << 4, 55}; 56 57/* must match values in dalvik.system.Zygote */ 58enum { 59 MOUNT_EXTERNAL_NONE = 0, 60 MOUNT_EXTERNAL_SINGLEUSER = 1, 61 MOUNT_EXTERNAL_MULTIUSER = 2, 62 MOUNT_EXTERNAL_MULTIUSER_ALL = 3, 63}; 64 65/* 66 * This signal handler is for zygote mode, since the zygote 67 * must reap its children 68 */ 69static void sigchldHandler(int s) 70{ 71 pid_t pid; 72 int status; 73 74 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 75 /* Log process-death status that we care about. In general it is not 76 safe to call ALOG(...) from a signal handler because of possible 77 reentrancy. However, we know a priori that the current implementation 78 of ALOG() is safe to call from a SIGCHLD handler in the zygote process. 79 If the ALOG() implementation changes its locking strategy or its use 80 of syscalls within the lazy-init critical section, its use here may 81 become unsafe. */ 82 if (WIFEXITED(status)) { 83 if (WEXITSTATUS(status)) { 84 ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)", 85 (int) pid, WEXITSTATUS(status)); 86 } else { 87 IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) { 88 ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG, 89 "Process %d exited cleanly (%d)", 90 (int) pid, WEXITSTATUS(status)); 91 } 92 } 93 } else if (WIFSIGNALED(status)) { 94 if (WTERMSIG(status) != SIGKILL) { 95 ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG, 96 "Process %d terminated by signal (%d)", 97 (int) pid, WTERMSIG(status)); 98 } else { 99 IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) { 100 ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG, 101 "Process %d terminated by signal (%d)", 102 (int) pid, WTERMSIG(status)); 103 } 104 } 105#ifdef WCOREDUMP 106 if (WCOREDUMP(status)) { 107 ALOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core", 108 (int) pid); 109 } 110#endif /* ifdef WCOREDUMP */ 111 } 112 113 /* 114 * If the just-crashed process is the system_server, bring down zygote 115 * so that it is restarted by init and system server will be restarted 116 * from there. 117 */ 118 if (pid == gDvm.systemServerPid) { 119 ALOG(LOG_INFO, ZYGOTE_LOG_TAG, 120 "Exit zygote because system server (%d) has terminated", 121 (int) pid); 122 kill(getpid(), SIGKILL); 123 } 124 } 125 126 if (pid < 0) { 127 ALOG(LOG_WARN, ZYGOTE_LOG_TAG, 128 "Zygote SIGCHLD error in waitpid: %s",strerror(errno)); 129 } 130} 131 132/* 133 * configure sigchld handler for the zygote process 134 * This is configured very late, because earlier in the dalvik lifecycle 135 * we can fork() and exec() for the verifier/optimizer, and we 136 * want to waitpid() for those rather than have them be harvested immediately. 137 * 138 * This ends up being called repeatedly before each fork(), but there's 139 * no real harm in that. 140 */ 141static void setSignalHandler() 142{ 143 int err; 144 struct sigaction sa; 145 146 memset(&sa, 0, sizeof(sa)); 147 148 sa.sa_handler = sigchldHandler; 149 150 err = sigaction (SIGCHLD, &sa, NULL); 151 152 if (err < 0) { 153 ALOGW("Error setting SIGCHLD handler: %s", strerror(errno)); 154 } 155} 156 157/* 158 * Set the SIGCHLD handler back to default behavior in zygote children 159 */ 160static void unsetSignalHandler() 161{ 162 int err; 163 struct sigaction sa; 164 165 memset(&sa, 0, sizeof(sa)); 166 167 sa.sa_handler = SIG_DFL; 168 169 err = sigaction (SIGCHLD, &sa, NULL); 170 171 if (err < 0) { 172 ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno)); 173 } 174} 175 176/* 177 * Calls POSIX setgroups() using the int[] object as an argument. 178 * A NULL argument is tolerated. 179 */ 180 181static int setgroupsIntarray(ArrayObject* gidArray) 182{ 183 gid_t *gids; 184 u4 i; 185 s4 *contents; 186 187 if (gidArray == NULL) { 188 return 0; 189 } 190 191 /* just in case gid_t and u4 are different... */ 192 gids = (gid_t *)alloca(sizeof(gid_t) * gidArray->length); 193 contents = (s4 *)(void *)gidArray->contents; 194 195 for (i = 0 ; i < gidArray->length ; i++) { 196 gids[i] = (gid_t) contents[i]; 197 } 198 199 return setgroups((size_t) gidArray->length, gids); 200} 201 202/* 203 * Sets the resource limits via setrlimit(2) for the values in the 204 * two-dimensional array of integers that's passed in. The second dimension 205 * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is 206 * treated as an empty array. 207 * 208 * -1 is returned on error. 209 */ 210static int setrlimitsFromArray(ArrayObject* rlimits) 211{ 212 u4 i; 213 struct rlimit rlim; 214 215 if (rlimits == NULL) { 216 return 0; 217 } 218 219 memset (&rlim, 0, sizeof(rlim)); 220 221 ArrayObject** tuples = (ArrayObject **)(void *)rlimits->contents; 222 223 for (i = 0; i < rlimits->length; i++) { 224 ArrayObject * rlimit_tuple = tuples[i]; 225 s4* contents = (s4 *)(void *)rlimit_tuple->contents; 226 int err; 227 228 if (rlimit_tuple->length != 3) { 229 ALOGE("rlimits array must have a second dimension of size 3"); 230 return -1; 231 } 232 233 rlim.rlim_cur = contents[1]; 234 rlim.rlim_max = contents[2]; 235 236 err = setrlimit(contents[0], &rlim); 237 238 if (err < 0) { 239 return -1; 240 } 241 } 242 243 return 0; 244} 245 246/* 247 * Create private mount space for this process and mount SD card 248 * into it, based on active user. See storage config details at 249 * http://source.android.com/tech/storage/ 250 */ 251static int mountExternalStorage(uid_t uid, u4 mountExternal) { 252 userid_t userid = multiuser_get_user_id(uid); 253 254 // Create private mount namespace for our process 255 if (unshare(CLONE_NEWNS) == -1) { 256 SLOGE("Failed to unshare(): %s", strerror(errno)); 257 return -1; 258 } 259 260 // Mark rootfs as being a slave in our process so that changes 261 // from parent namespace flow into our process. 262 if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) { 263 SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno)); 264 return -1; 265 } 266 267 // Create bind mounts to expose external storage 268 if (mountExternal == MOUNT_EXTERNAL_MULTIUSER 269 || mountExternal == MOUNT_EXTERNAL_MULTIUSER_ALL) { 270 const char* storage_base = getenv("ANDROID_STORAGE"); 271 const char* target = getenv("EXTERNAL_STORAGE"); 272 const char* source_base = getenv("MULTIUSER_EXTERNAL_STORAGE"); 273 if (storage_base == NULL || target == NULL || source_base == NULL) { 274 SLOGE("Storage environment undefined; unable to provide external storage"); 275 return -1; 276 } 277 278 if (mountExternal == MOUNT_EXTERNAL_MULTIUSER_ALL) { 279 // External storage for all users 280 if (mount(source_base, target, NULL, MS_BIND, NULL) == -1) { 281 SLOGE("Failed to mount %s to %s: %s", source_base, target, strerror(errno)); 282 return -1; 283 } 284 285 } else { 286 // External storage for specific user 287 std::string source(StringPrintf("%s/%d", source_base, userid)); 288 if (fs_prepare_dir(source.c_str(), 0000, 0, 0) == -1) { 289 return -1; 290 } 291 if (mount(source.c_str(), target, NULL, MS_BIND, NULL) == -1) { 292 SLOGE("Failed to mount %s to %s: %s", source.c_str(), target, strerror(errno)); 293 return -1; 294 } 295 296 // Mount shared OBB storage into place 297 std::string obb_source(StringPrintf("%s/obb", source_base)); 298 std::string android_target(StringPrintf("%s/Android", target)); 299 std::string android_obb_target(StringPrintf("%s/Android/obb", target)); 300 if (fs_prepare_dir(obb_source.c_str(), 0000, 0, 0) == -1 301 || fs_prepare_dir(android_target.c_str(), 0000, 0, 0) == -1 302 || fs_prepare_dir(android_obb_target.c_str(), 0000, 0, 0) == -1) { 303 return -1; 304 } 305 if (mount(obb_source.c_str(), android_obb_target.c_str(), NULL, MS_BIND, NULL) == -1) { 306 SLOGE("Failed to bind mount %s to %s: %s", 307 obb_source.c_str(), android_obb_target.c_str(), strerror(errno)); 308 return -1; 309 } 310 } 311 312 } else { 313 SLOGE("Mount mode %d unsupported", mountExternal); 314 return -1; 315 } 316 317 return 0; 318} 319 320/* native public static int fork(); */ 321static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult) 322{ 323 pid_t pid; 324 325 if (!gDvm.zygote) { 326 dvmThrowIllegalStateException( 327 "VM instance not started with -Xzygote"); 328 329 RETURN_VOID(); 330 } 331 332 if (!dvmGcPreZygoteFork()) { 333 ALOGE("pre-fork heap failed"); 334 dvmAbort(); 335 } 336 337 setSignalHandler(); 338 339 dvmDumpLoaderStats("zygote"); 340 pid = fork(); 341 342#ifdef HAVE_ANDROID_OS 343 if (pid == 0) { 344 /* child process */ 345 extern int gMallocLeakZygoteChild; 346 gMallocLeakZygoteChild = 1; 347 } 348#endif 349 350 RETURN_INT(pid); 351} 352 353/* 354 * Enable/disable debug features requested by the caller. 355 * 356 * debugger 357 * If set, enable debugging; if not set, disable debugging. This is 358 * easy to handle, because the JDWP thread isn't started until we call 359 * dvmInitAfterZygote(). 360 * checkjni 361 * If set, make sure "check JNI" is enabled. 362 * assert 363 * If set, make sure assertions are enabled. This gets fairly weird, 364 * because it affects the result of a method called by class initializers, 365 * and hence can't affect pre-loaded/initialized classes. 366 * safemode 367 * If set, operates the VM in the safe mode. The definition of "safe mode" is 368 * implementation dependent and currently only the JIT compiler is disabled. 369 * This is easy to handle because the compiler thread and associated resources 370 * are not requested until we call dvmInitAfterZygote(). 371 */ 372static void enableDebugFeatures(u4 debugFlags) 373{ 374 ALOGV("debugFlags is 0x%02x", debugFlags); 375 376 gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0); 377 378 if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) { 379 /* turn it on if it's not already enabled */ 380 dvmLateEnableCheckedJni(); 381 } 382 383 if ((debugFlags & DEBUG_ENABLE_JNI_LOGGING) != 0) { 384 gDvmJni.logThirdPartyJni = true; 385 } 386 387 if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) { 388 /* turn it on if it's not already enabled */ 389 dvmLateEnableAssertions(); 390 } 391 392 if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) { 393#if defined(WITH_JIT) 394 /* turn off the jit if it is explicitly requested by the app */ 395 if (gDvm.executionMode == kExecutionModeJit) 396 gDvm.executionMode = kExecutionModeInterpFast; 397#endif 398 } 399 400#ifdef HAVE_ANDROID_OS 401 if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) { 402 /* To let a non-privileged gdbserver attach to this 403 * process, we must set its dumpable bit flag. However 404 * we are not interested in generating a coredump in 405 * case of a crash, so also set the coredump size to 0 406 * to disable that 407 */ 408 if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { 409 ALOGE("could not set dumpable bit flag for pid %d: %s", 410 getpid(), strerror(errno)); 411 } else { 412 struct rlimit rl; 413 rl.rlim_cur = 0; 414 rl.rlim_max = RLIM_INFINITY; 415 if (setrlimit(RLIMIT_CORE, &rl) < 0) { 416 ALOGE("could not disable core file generation for pid %d: %s", 417 getpid(), strerror(errno)); 418 } 419 } 420 } 421#endif 422} 423 424/* 425 * Set Linux capability flags. 426 * 427 * Returns 0 on success, errno on failure. 428 */ 429static int setCapabilities(int64_t permitted, int64_t effective) 430{ 431#ifdef HAVE_ANDROID_OS 432 struct __user_cap_header_struct capheader; 433 struct __user_cap_data_struct capdata; 434 435 memset(&capheader, 0, sizeof(capheader)); 436 memset(&capdata, 0, sizeof(capdata)); 437 438 capheader.version = _LINUX_CAPABILITY_VERSION; 439 capheader.pid = 0; 440 441 capdata.effective = effective; 442 capdata.permitted = permitted; 443 444 ALOGV("CAPSET perm=%llx eff=%llx", permitted, effective); 445 if (capset(&capheader, &capdata) != 0) 446 return errno; 447#endif /*HAVE_ANDROID_OS*/ 448 449 return 0; 450} 451 452#ifdef HAVE_SELINUX 453/* 454 * Set SELinux security context. 455 * 456 * Returns 0 on success, -1 on failure. 457 */ 458static int setSELinuxContext(uid_t uid, bool isSystemServer, 459 const char *seInfo, const char *niceName) 460{ 461#ifdef HAVE_ANDROID_OS 462 return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName); 463#else 464 return 0; 465#endif 466} 467#endif 468 469/* 470 * Utility routine to fork zygote and specialize the child process. 471 */ 472static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer) 473{ 474 pid_t pid; 475 476 uid_t uid = (uid_t) args[0]; 477 gid_t gid = (gid_t) args[1]; 478 ArrayObject* gids = (ArrayObject *)args[2]; 479 u4 debugFlags = args[3]; 480 ArrayObject *rlimits = (ArrayObject *)args[4]; 481 u4 mountExternal = MOUNT_EXTERNAL_NONE; 482 int64_t permittedCapabilities, effectiveCapabilities; 483#ifdef HAVE_SELINUX 484 char *seInfo = NULL; 485 char *niceName = NULL; 486#endif 487 488 if (isSystemServer) { 489 /* 490 * Don't use GET_ARG_LONG here for now. gcc is generating code 491 * that uses register d8 as a temporary, and that's coming out 492 * scrambled in the child process. b/3138621 493 */ 494 //permittedCapabilities = GET_ARG_LONG(args, 5); 495 //effectiveCapabilities = GET_ARG_LONG(args, 7); 496 permittedCapabilities = args[5] | (int64_t) args[6] << 32; 497 effectiveCapabilities = args[7] | (int64_t) args[8] << 32; 498 } else { 499 mountExternal = args[5]; 500 permittedCapabilities = effectiveCapabilities = 0; 501#ifdef HAVE_SELINUX 502 StringObject* seInfoObj = (StringObject*)args[6]; 503 if (seInfoObj) { 504 seInfo = dvmCreateCstrFromString(seInfoObj); 505 if (!seInfo) { 506 ALOGE("seInfo dvmCreateCstrFromString failed"); 507 dvmAbort(); 508 } 509 } 510 StringObject* niceNameObj = (StringObject*)args[7]; 511 if (niceNameObj) { 512 niceName = dvmCreateCstrFromString(niceNameObj); 513 if (!niceName) { 514 ALOGE("niceName dvmCreateCstrFromString failed"); 515 dvmAbort(); 516 } 517 } 518#endif 519 } 520 521 if (!gDvm.zygote) { 522 dvmThrowIllegalStateException( 523 "VM instance not started with -Xzygote"); 524 525 return -1; 526 } 527 528 if (!dvmGcPreZygoteFork()) { 529 ALOGE("pre-fork heap failed"); 530 dvmAbort(); 531 } 532 533 setSignalHandler(); 534 535 dvmDumpLoaderStats("zygote"); 536 pid = fork(); 537 538 if (pid == 0) { 539 int err; 540 /* The child process */ 541 542#ifdef HAVE_ANDROID_OS 543 extern int gMallocLeakZygoteChild; 544 gMallocLeakZygoteChild = 1; 545 546 /* keep caps across UID change, unless we're staying root */ 547 if (uid != 0) { 548 err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); 549 550 if (err < 0) { 551 ALOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno)); 552 dvmAbort(); 553 } 554 } 555 556#endif /* HAVE_ANDROID_OS */ 557 558 if (mountExternal != MOUNT_EXTERNAL_NONE) { 559 err = mountExternalStorage(uid, mountExternal); 560 if (err < 0) { 561 ALOGE("cannot mountExternalStorage(): %s", strerror(errno)); 562 563 if (errno == ENOTCONN || errno == EROFS) { 564 // When device is actively encrypting, we get ENOTCONN here 565 // since FUSE was mounted before the framework restarted. 566 // When encrypted device is booting, we get EROFS since 567 // FUSE hasn't been created yet by init. 568 // In either case, continue without external storage. 569 } else { 570 dvmAbort(); 571 } 572 } 573 } 574 575 err = setgroupsIntarray(gids); 576 if (err < 0) { 577 ALOGE("cannot setgroups(): %s", strerror(errno)); 578 dvmAbort(); 579 } 580 581 err = setrlimitsFromArray(rlimits); 582 if (err < 0) { 583 ALOGE("cannot setrlimit(): %s", strerror(errno)); 584 dvmAbort(); 585 } 586 587 err = setgid(gid); 588 if (err < 0) { 589 ALOGE("cannot setgid(%d): %s", gid, strerror(errno)); 590 dvmAbort(); 591 } 592 593 err = setuid(uid); 594 if (err < 0) { 595 ALOGE("cannot setuid(%d): %s", uid, strerror(errno)); 596 dvmAbort(); 597 } 598 599 int current = personality(0xffffFFFF); 600 int success = personality((ADDR_NO_RANDOMIZE | current)); 601 if (success == -1) { 602 ALOGW("Personality switch failed. current=%d error=%d\n", current, errno); 603 } 604 605 err = setCapabilities(permittedCapabilities, effectiveCapabilities); 606 if (err != 0) { 607 ALOGE("cannot set capabilities (%llx,%llx): %s", 608 permittedCapabilities, effectiveCapabilities, strerror(err)); 609 dvmAbort(); 610 } 611 612 err = set_sched_policy(0, SP_DEFAULT); 613 if (err < 0) { 614 ALOGE("cannot set_sched_policy(0, SP_DEFAULT): %s", strerror(-err)); 615 dvmAbort(); 616 } 617 618#ifdef HAVE_SELINUX 619 err = setSELinuxContext(uid, isSystemServer, seInfo, niceName); 620 if (err < 0) { 621 ALOGE("cannot set SELinux context: %s\n", strerror(errno)); 622 dvmAbort(); 623 } 624 // These free(3) calls are safe because we know we're only ever forking 625 // a single-threaded process, so we know no other thread held the heap 626 // lock when we forked. 627 free(seInfo); 628 free(niceName); 629#endif 630 631 /* 632 * Our system thread ID has changed. Get the new one. 633 */ 634 Thread* thread = dvmThreadSelf(); 635 thread->systemTid = dvmGetSysThreadId(); 636 637 /* configure additional debug options */ 638 enableDebugFeatures(debugFlags); 639 640 unsetSignalHandler(); 641 gDvm.zygote = false; 642 if (!dvmInitAfterZygote()) { 643 ALOGE("error in post-zygote initialization"); 644 dvmAbort(); 645 } 646 } else if (pid > 0) { 647 /* the parent process */ 648#ifdef HAVE_SELINUX 649 free(seInfo); 650 free(niceName); 651#endif 652 } 653 654 return pid; 655} 656 657/* 658 * native public static int nativeForkAndSpecialize(int uid, int gid, 659 * int[] gids, int debugFlags, int[][] rlimits, int mountExternal, 660 * String seInfo, String niceName); 661 */ 662static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args, 663 JValue* pResult) 664{ 665 pid_t pid; 666 667 pid = forkAndSpecializeCommon(args, false); 668 669 RETURN_INT(pid); 670} 671 672/* 673 * native public static int nativeForkSystemServer(int uid, int gid, 674 * int[] gids, int debugFlags, int[][] rlimits, 675 * long permittedCapabilities, long effectiveCapabilities); 676 */ 677static void Dalvik_dalvik_system_Zygote_forkSystemServer( 678 const u4* args, JValue* pResult) 679{ 680 pid_t pid; 681 pid = forkAndSpecializeCommon(args, true); 682 683 /* The zygote process checks whether the child process has died or not. */ 684 if (pid > 0) { 685 int status; 686 687 ALOGI("System server process %d has been created", pid); 688 gDvm.systemServerPid = pid; 689 /* There is a slight window that the system server process has crashed 690 * but it went unnoticed because we haven't published its pid yet. So 691 * we recheck here just to make sure that all is well. 692 */ 693 if (waitpid(pid, &status, WNOHANG) == pid) { 694 ALOGE("System server process %d has died. Restarting Zygote!", pid); 695 kill(getpid(), SIGKILL); 696 } 697 } 698 RETURN_INT(pid); 699} 700 701/* native private static void nativeExecShell(String command); 702 */ 703static void Dalvik_dalvik_system_Zygote_execShell( 704 const u4* args, JValue* pResult) 705{ 706 StringObject* command = (StringObject*)args[0]; 707 708 const char *argp[] = {_PATH_BSHELL, "-c", NULL, NULL}; 709 argp[2] = dvmCreateCstrFromString(command); 710 711 ALOGI("Exec: %s %s %s", argp[0], argp[1], argp[2]); 712 713 execv(_PATH_BSHELL, (char**)argp); 714 exit(127); 715} 716 717const DalvikNativeMethod dvm_dalvik_system_Zygote[] = { 718 { "nativeFork", "()I", 719 Dalvik_dalvik_system_Zygote_fork }, 720 { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;)I", 721 Dalvik_dalvik_system_Zygote_forkAndSpecialize }, 722 { "nativeForkSystemServer", "(II[II[[IJJ)I", 723 Dalvik_dalvik_system_Zygote_forkSystemServer }, 724 { "nativeExecShell", "(Ljava/lang/String;)V", 725 Dalvik_dalvik_system_Zygote_execShell }, 726 { NULL, NULL, NULL }, 727}; 728