1 /* 2 * Copyright (C) 2007 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 package com.android.internal.os; 18 19 import android.net.Credentials; 20 import android.net.LocalSocket; 21 import android.os.Process; 22 import android.os.SystemProperties; 23 import android.util.Log; 24 25 import dalvik.system.PathClassLoader; 26 import dalvik.system.Zygote; 27 28 import java.io.BufferedReader; 29 import java.io.DataOutputStream; 30 import java.io.FileDescriptor; 31 import java.io.FileOutputStream; 32 import java.io.IOException; 33 import java.io.InputStreamReader; 34 import java.io.PrintStream; 35 import java.util.ArrayList; 36 37 /** 38 * A connection that can make spawn requests. 39 */ 40 class ZygoteConnection { 41 private static final String TAG = "Zygote"; 42 43 /** a prototype instance for a future List.toArray() */ 44 private static final int[][] intArray2d = new int[0][0]; 45 46 /** 47 * {@link android.net.LocalSocket#setSoTimeout} value for connections. 48 * Effectively, the amount of time a requestor has between the start of 49 * the request and the completed request. The select-loop mode Zygote 50 * doesn't have the logic to return to the select loop in the middle of 51 * a request, so we need to time out here to avoid being denial-of-serviced. 52 */ 53 private static final int CONNECTION_TIMEOUT_MILLIS = 1000; 54 55 /** max number of arguments that a connection can specify */ 56 private static final int MAX_ZYGOTE_ARGC=1024; 57 58 /** 59 * The command socket. 60 * 61 * mSocket is retained in the child process in "peer wait" mode, so 62 * that it closes when the child process terminates. In other cases, 63 * it is closed in the peer. 64 */ 65 private final LocalSocket mSocket; 66 private final DataOutputStream mSocketOutStream; 67 private final BufferedReader mSocketReader; 68 private final Credentials peer; 69 70 /** 71 * A long-lived reference to the original command socket used to launch 72 * this peer. If "peer wait" mode is specified, the process that requested 73 * the new VM instance intends to track the lifetime of the spawned instance 74 * via the command socket. In this case, the command socket is closed 75 * in the Zygote and placed here in the spawned instance so that it will 76 * not be collected and finalized. This field remains null at all times 77 * in the original Zygote process, and in all spawned processes where 78 * "peer-wait" mode was not requested. 79 */ 80 private static LocalSocket sPeerWaitSocket = null; 81 82 /** 83 * Constructs instance from connected socket. 84 * 85 * @param socket non-null; connected socket 86 * @throws IOException 87 */ ZygoteConnection(LocalSocket socket)88 ZygoteConnection(LocalSocket socket) throws IOException { 89 mSocket = socket; 90 91 mSocketOutStream 92 = new DataOutputStream(socket.getOutputStream()); 93 94 mSocketReader = new BufferedReader( 95 new InputStreamReader(socket.getInputStream()), 256); 96 97 mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS); 98 99 try { 100 peer = mSocket.getPeerCredentials(); 101 } catch (IOException ex) { 102 Log.e(TAG, "Cannot read peer credentials", ex); 103 throw ex; 104 } 105 } 106 107 /** 108 * Returns the file descriptor of the associated socket. 109 * 110 * @return null-ok; file descriptor 111 */ getFileDesciptor()112 FileDescriptor getFileDesciptor() { 113 return mSocket.getFileDescriptor(); 114 } 115 116 /** 117 * Reads start commands from an open command socket. 118 * Start commands are presently a pair of newline-delimited lines 119 * indicating a) class to invoke main() on b) nice name to set argv[0] to. 120 * Continues to read commands and forkAndSpecialize children until 121 * the socket is closed. This method is used in ZYGOTE_FORK_MODE 122 * 123 * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() 124 * method in child process 125 */ run()126 void run() throws ZygoteInit.MethodAndArgsCaller { 127 128 int loopCount = ZygoteInit.GC_LOOP_COUNT; 129 130 while (true) { 131 /* 132 * Call gc() before we block in readArgumentList(). 133 * It's work that has to be done anyway, and it's better 134 * to avoid making every child do it. It will also 135 * madvise() any free memory as a side-effect. 136 * 137 * Don't call it every time, because walking the entire 138 * heap is a lot of overhead to free a few hundred bytes. 139 */ 140 if (loopCount <= 0) { 141 ZygoteInit.gc(); 142 loopCount = ZygoteInit.GC_LOOP_COUNT; 143 } else { 144 loopCount--; 145 } 146 147 if (runOnce()) { 148 break; 149 } 150 } 151 } 152 153 /** 154 * Reads one start command from the command socket. If successful, 155 * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} 156 * exception is thrown in that child while in the parent process, 157 * the method returns normally. On failure, the child is not 158 * spawned and messages are printed to the log and stderr. Returns 159 * a boolean status value indicating whether an end-of-file on the command 160 * socket has been encountered. 161 * 162 * @return false if command socket should continue to be read from, or 163 * true if an end-of-file has been encountered. 164 * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() 165 * method in child process 166 */ runOnce()167 boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { 168 169 String args[]; 170 Arguments parsedArgs = null; 171 FileDescriptor[] descriptors; 172 173 try { 174 args = readArgumentList(); 175 descriptors = mSocket.getAncillaryFileDescriptors(); 176 } catch (IOException ex) { 177 Log.w(TAG, "IOException on command socket " + ex.getMessage()); 178 closeSocket(); 179 return true; 180 } 181 182 if (args == null) { 183 // EOF reached. 184 closeSocket(); 185 return true; 186 } 187 188 /** the stderr of the most recent request, if avail */ 189 PrintStream newStderr = null; 190 191 if (descriptors != null && descriptors.length >= 3) { 192 newStderr = new PrintStream( 193 new FileOutputStream(descriptors[2])); 194 } 195 196 int pid; 197 198 try { 199 parsedArgs = new Arguments(args); 200 201 applyUidSecurityPolicy(parsedArgs, peer); 202 applyDebuggerSecurityPolicy(parsedArgs); 203 applyRlimitSecurityPolicy(parsedArgs, peer); 204 applyCapabilitiesSecurityPolicy(parsedArgs, peer); 205 206 int[][] rlimits = null; 207 208 if (parsedArgs.rlimits != null) { 209 rlimits = parsedArgs.rlimits.toArray(intArray2d); 210 } 211 212 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, 213 parsedArgs.gids, parsedArgs.debugFlags, rlimits); 214 } catch (IllegalArgumentException ex) { 215 logAndPrintError (newStderr, "Invalid zygote arguments", ex); 216 pid = -1; 217 } catch (ZygoteSecurityException ex) { 218 logAndPrintError(newStderr, 219 "Zygote security policy prevents request: ", ex); 220 pid = -1; 221 } 222 223 if (pid == 0) { 224 // in child 225 handleChildProc(parsedArgs, descriptors, newStderr); 226 // should never happen 227 return true; 228 } else { /* pid != 0 */ 229 // in parent...pid of < 0 means failure 230 return handleParentProc(pid, descriptors, parsedArgs); 231 } 232 } 233 234 /** 235 * Closes socket associated with this connection. 236 */ closeSocket()237 void closeSocket() { 238 try { 239 mSocket.close(); 240 } catch (IOException ex) { 241 Log.e(TAG, "Exception while closing command " 242 + "socket in parent", ex); 243 } 244 } 245 246 /** 247 * Handles argument parsing for args related to the zygote spawner.<p> 248 249 * Current recognized args: 250 * <ul> 251 * <li> --setuid=<i>uid of child process, defaults to 0</i> 252 * <li> --setgid=<i>gid of child process, defaults to 0</i> 253 * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i> 254 * <li> --capabilities=<i>a pair of comma-separated integer strings 255 * indicating Linux capabilities(2) set for child. The first string 256 * represents the <code>permitted</code> set, and the second the 257 * <code>effective</code> set. Precede each with 0 or 258 * 0x for octal or hexidecimal value. If unspecified, both default to 0. 259 * This parameter is only applied if the uid of the new process will 260 * be non-0. </i> 261 * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call. 262 * <code>r</code> is the resource, <code>c</code> and <code>m</code> 263 * are the settings for current and max value.</i> 264 * <li> --peer-wait indicates that the command socket should 265 * be inherited by (and set to close-on-exec in) the spawned process 266 * and used to track the lifetime of that process. The spawning process 267 * then exits. Without this flag, it is retained by the spawning process 268 * (and closed in the child) in expectation of a new spawn request. 269 * <li> --classpath=<i>colon-separated classpath</i> indicates 270 * that the specified class (which must b first non-flag argument) should 271 * be loaded from jar files in the specified classpath. Incompatible with 272 * --runtime-init 273 * <li> --runtime-init indicates that the remaining arg list should 274 * be handed off to com.android.internal.os.RuntimeInit, rather than 275 * processed directly 276 * Android runtime startup (eg, Binder initialization) is also eschewed. 277 * <li> If <code>--runtime-init</code> is present: 278 * [--] <args for RuntimeInit > 279 * <li> If <code>--runtime-init</code> is absent: 280 * [--] <classname> [args...] 281 * </ul> 282 */ 283 static class Arguments { 284 /** from --setuid */ 285 int uid = 0; 286 boolean uidSpecified; 287 288 /** from --setgid */ 289 int gid = 0; 290 boolean gidSpecified; 291 292 /** from --setgroups */ 293 int[] gids; 294 295 /** from --peer-wait */ 296 boolean peerWait; 297 298 /** 299 * From --enable-debugger, --enable-checkjni, --enable-assert, and 300 * --enable-safemode 301 */ 302 int debugFlags; 303 304 /** from --classpath */ 305 String classpath; 306 307 /** from --runtime-init */ 308 boolean runtimeInit; 309 310 /** from --capabilities */ 311 boolean capabilitiesSpecified; 312 long permittedCapabilities; 313 long effectiveCapabilities; 314 315 /** from all --rlimit=r,c,m */ 316 ArrayList<int[]> rlimits; 317 318 /** 319 * Any args after and including the first non-option arg 320 * (or after a '--') 321 */ 322 String remainingArgs[]; 323 324 /** 325 * Constructs instance and parses args 326 * @param args zygote command-line args 327 * @throws IllegalArgumentException 328 */ Arguments(String args[])329 Arguments(String args[]) throws IllegalArgumentException { 330 parseArgs(args); 331 } 332 333 /** 334 * Parses the commandline arguments intended for the Zygote spawner 335 * (such as "--setuid=" and "--setgid=") and creates an array 336 * containing the remaining args. 337 * 338 * Per security review bug #1112214, duplicate args are disallowed in 339 * critical cases to make injection harder. 340 */ parseArgs(String args[])341 private void parseArgs(String args[]) 342 throws IllegalArgumentException { 343 int curArg = 0; 344 345 for ( /* curArg */ ; curArg < args.length; curArg++) { 346 String arg = args[curArg]; 347 348 if (arg.equals("--")) { 349 curArg++; 350 break; 351 } else if (arg.startsWith("--setuid=")) { 352 if (uidSpecified) { 353 throw new IllegalArgumentException( 354 "Duplicate arg specified"); 355 } 356 uidSpecified = true; 357 uid = Integer.parseInt( 358 arg.substring(arg.indexOf('=') + 1)); 359 } else if (arg.startsWith("--setgid=")) { 360 if (gidSpecified) { 361 throw new IllegalArgumentException( 362 "Duplicate arg specified"); 363 } 364 gidSpecified = true; 365 gid = Integer.parseInt( 366 arg.substring(arg.indexOf('=') + 1)); 367 } else if (arg.equals("--enable-debugger")) { 368 debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; 369 } else if (arg.equals("--enable-safemode")) { 370 debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; 371 } else if (arg.equals("--enable-checkjni")) { 372 debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; 373 } else if (arg.equals("--enable-assert")) { 374 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; 375 } else if (arg.equals("--peer-wait")) { 376 peerWait = true; 377 } else if (arg.equals("--runtime-init")) { 378 runtimeInit = true; 379 } else if (arg.startsWith("--capabilities=")) { 380 if (capabilitiesSpecified) { 381 throw new IllegalArgumentException( 382 "Duplicate arg specified"); 383 } 384 capabilitiesSpecified = true; 385 String capString = arg.substring(arg.indexOf('=')+1); 386 387 String[] capStrings = capString.split(",", 2); 388 389 if (capStrings.length == 1) { 390 effectiveCapabilities = Long.decode(capStrings[0]); 391 permittedCapabilities = effectiveCapabilities; 392 } else { 393 permittedCapabilities = Long.decode(capStrings[0]); 394 effectiveCapabilities = Long.decode(capStrings[1]); 395 } 396 } else if (arg.startsWith("--rlimit=")) { 397 // Duplicate --rlimit arguments are specifically allowed. 398 String[] limitStrings 399 = arg.substring(arg.indexOf('=')+1).split(","); 400 401 if (limitStrings.length != 3) { 402 throw new IllegalArgumentException( 403 "--rlimit= should have 3 comma-delimited ints"); 404 } 405 int[] rlimitTuple = new int[limitStrings.length]; 406 407 for(int i=0; i < limitStrings.length; i++) { 408 rlimitTuple[i] = Integer.parseInt(limitStrings[i]); 409 } 410 411 if (rlimits == null) { 412 rlimits = new ArrayList(); 413 } 414 415 rlimits.add(rlimitTuple); 416 } else if (arg.equals("-classpath")) { 417 if (classpath != null) { 418 throw new IllegalArgumentException( 419 "Duplicate arg specified"); 420 } 421 try { 422 classpath = args[++curArg]; 423 } catch (IndexOutOfBoundsException ex) { 424 throw new IllegalArgumentException( 425 "-classpath requires argument"); 426 } 427 } else if (arg.startsWith("--setgroups=")) { 428 if (gids != null) { 429 throw new IllegalArgumentException( 430 "Duplicate arg specified"); 431 } 432 433 String[] params 434 = arg.substring(arg.indexOf('=') + 1).split(","); 435 436 gids = new int[params.length]; 437 438 for (int i = params.length - 1; i >= 0 ; i--) { 439 gids[i] = Integer.parseInt(params[i]); 440 } 441 } else { 442 break; 443 } 444 } 445 446 if (runtimeInit && classpath != null) { 447 throw new IllegalArgumentException( 448 "--runtime-init and -classpath are incompatible"); 449 } 450 451 remainingArgs = new String[args.length - curArg]; 452 453 System.arraycopy(args, curArg, remainingArgs, 0, 454 remainingArgs.length); 455 } 456 } 457 458 /** 459 * Reads an argument list from the command socket/ 460 * @return Argument list or null if EOF is reached 461 * @throws IOException passed straight through 462 */ readArgumentList()463 private String[] readArgumentList() 464 throws IOException { 465 466 /** 467 * See android.os.Process.zygoteSendArgsAndGetPid() 468 * Presently the wire format to the zygote process is: 469 * a) a count of arguments (argc, in essence) 470 * b) a number of newline-separated argument strings equal to count 471 * 472 * After the zygote process reads these it will write the pid of 473 * the child or -1 on failure. 474 */ 475 476 int argc; 477 478 try { 479 String s = mSocketReader.readLine(); 480 481 if (s == null) { 482 // EOF reached. 483 return null; 484 } 485 argc = Integer.parseInt(s); 486 } catch (NumberFormatException ex) { 487 Log.e(TAG, "invalid Zygote wire format: non-int at argc"); 488 throw new IOException("invalid wire format"); 489 } 490 491 // See bug 1092107: large argc can be used for a DOS attack 492 if (argc > MAX_ZYGOTE_ARGC) { 493 throw new IOException("max arg count exceeded"); 494 } 495 496 String[] result = new String[argc]; 497 for (int i = 0; i < argc; i++) { 498 result[i] = mSocketReader.readLine(); 499 if (result[i] == null) { 500 // We got an unexpected EOF. 501 throw new IOException("truncated request"); 502 } 503 } 504 505 return result; 506 } 507 508 /** 509 * Applies zygote security policy per bugs #875058 and #1082165. 510 * Based on the credentials of the process issuing a zygote command: 511 * <ol> 512 * <li> uid 0 (root) may specify any uid, gid, and setgroups() list 513 * <li> uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal 514 * operation. It may also specify any gid and setgroups() list it chooses. 515 * In factory test mode, it may specify any UID. 516 * <li> Any other uid may not specify any uid, gid, or setgroups list. The 517 * uid and gid will be inherited from the requesting process. 518 * </ul> 519 * 520 * @param args non-null; zygote spawner arguments 521 * @param peer non-null; peer credentials 522 * @throws ZygoteSecurityException 523 */ applyUidSecurityPolicy(Arguments args, Credentials peer)524 private static void applyUidSecurityPolicy(Arguments args, Credentials peer) 525 throws ZygoteSecurityException { 526 527 int peerUid = peer.getUid(); 528 529 if (peerUid == 0) { 530 // Root can do what it wants 531 } else if (peerUid == Process.SYSTEM_UID ) { 532 // System UID is restricted, except in factory test mode 533 String factoryTest = SystemProperties.get("ro.factorytest"); 534 boolean uidRestricted; 535 536 /* In normal operation, SYSTEM_UID can only specify a restricted 537 * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid. 538 */ 539 uidRestricted 540 = !(factoryTest.equals("1") || factoryTest.equals("2")); 541 542 if (uidRestricted 543 && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) { 544 throw new ZygoteSecurityException( 545 "System UID may not launch process with UID < " 546 + Process.SYSTEM_UID); 547 } 548 } else { 549 // Everything else 550 if (args.uidSpecified || args.gidSpecified 551 || args.gids != null) { 552 throw new ZygoteSecurityException( 553 "App UIDs may not specify uid's or gid's"); 554 } 555 } 556 557 // If not otherwise specified, uid and gid are inherited from peer 558 if (!args.uidSpecified) { 559 args.uid = peer.getUid(); 560 args.uidSpecified = true; 561 } 562 if (!args.gidSpecified) { 563 args.gid = peer.getGid(); 564 args.gidSpecified = true; 565 } 566 } 567 568 569 /** 570 * Applies debugger security policy. 571 * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, 572 * the debugger state is specified via the "--enable-debugger" flag 573 * in the spawn request. 574 * 575 * @param args non-null; zygote spawner args 576 */ applyDebuggerSecurityPolicy(Arguments args)577 private static void applyDebuggerSecurityPolicy(Arguments args) { 578 if ("1".equals(SystemProperties.get("ro.debuggable"))) { 579 args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; 580 } 581 } 582 583 /** 584 * Applies zygote security policy per bug #1042973. Based on the credentials 585 * of the process issuing a zygote command: 586 * <ol> 587 * <li> peers of uid 0 (root) and uid 1000 (Process.SYSTEM_UID) 588 * may specify any rlimits. 589 * <li> All other uids may not specify rlimits. 590 * </ul> 591 * @param args non-null; zygote spawner arguments 592 * @param peer non-null; peer credentials 593 * @throws ZygoteSecurityException 594 */ applyRlimitSecurityPolicy( Arguments args, Credentials peer)595 private static void applyRlimitSecurityPolicy( 596 Arguments args, Credentials peer) 597 throws ZygoteSecurityException { 598 599 int peerUid = peer.getUid(); 600 601 if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) { 602 // All peers with UID other than root or SYSTEM_UID 603 if (args.rlimits != null) { 604 throw new ZygoteSecurityException( 605 "This UID may not specify rlimits."); 606 } 607 } 608 } 609 610 /** 611 * Applies zygote security policy per bug #1042973. A root peer may 612 * spawn an instance with any capabilities. All other uids may spawn 613 * instances with any of the capabilities in the peer's permitted set 614 * but no more. 615 * 616 * @param args non-null; zygote spawner arguments 617 * @param peer non-null; peer credentials 618 * @throws ZygoteSecurityException 619 */ applyCapabilitiesSecurityPolicy( Arguments args, Credentials peer)620 private static void applyCapabilitiesSecurityPolicy( 621 Arguments args, Credentials peer) 622 throws ZygoteSecurityException { 623 624 if (args.permittedCapabilities == 0 625 && args.effectiveCapabilities == 0) { 626 // nothing to check 627 return; 628 } 629 630 if (peer.getUid() == 0) { 631 // root may specify anything 632 return; 633 } 634 635 long permittedCaps; 636 637 try { 638 permittedCaps = ZygoteInit.capgetPermitted(peer.getPid()); 639 } catch (IOException ex) { 640 throw new ZygoteSecurityException( 641 "Error retrieving peer's capabilities."); 642 } 643 644 /* 645 * Ensure that the client did not specify an effective set larger 646 * than the permitted set. The kernel will enforce this too, but we 647 * do it here to make the following check easier. 648 */ 649 if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) { 650 throw new ZygoteSecurityException( 651 "Effective capabilities cannot be superset of " 652 + " permitted capabilities" ); 653 } 654 655 /* 656 * Ensure that the new permitted (and thus the new effective) set is 657 * a subset of the peer process's permitted set 658 */ 659 660 if (((~permittedCaps) & args.permittedCapabilities) != 0) { 661 throw new ZygoteSecurityException( 662 "Peer specified unpermitted capabilities" ); 663 } 664 } 665 666 /** 667 * Handles post-fork setup of child proc, closing sockets as appropriate, 668 * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller 669 * if successful or returning if failed. 670 * 671 * @param parsedArgs non-null; zygote args 672 * @param descriptors null-ok; new file descriptors for stdio if available. 673 * @param newStderr null-ok; stream to use for stderr until stdio 674 * is reopened. 675 * 676 * @throws ZygoteInit.MethodAndArgsCaller on success to 677 * trampoline to code that invokes static main. 678 */ handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, PrintStream newStderr)679 private void handleChildProc(Arguments parsedArgs, 680 FileDescriptor[] descriptors, PrintStream newStderr) 681 throws ZygoteInit.MethodAndArgsCaller { 682 683 /* 684 * Close the socket, unless we're in "peer wait" mode, in which 685 * case it's used to track the liveness of this process. 686 */ 687 688 if (parsedArgs.peerWait) { 689 try { 690 ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true); 691 sPeerWaitSocket = mSocket; 692 } catch (IOException ex) { 693 Log.e(TAG, "Zygote Child: error setting peer wait " 694 + "socket to be close-on-exec", ex); 695 } 696 } else { 697 closeSocket(); 698 ZygoteInit.closeServerSocket(); 699 } 700 701 if (descriptors != null) { 702 try { 703 ZygoteInit.reopenStdio(descriptors[0], 704 descriptors[1], descriptors[2]); 705 706 for (FileDescriptor fd: descriptors) { 707 ZygoteInit.closeDescriptor(fd); 708 } 709 newStderr = System.err; 710 } catch (IOException ex) { 711 Log.e(TAG, "Error reopening stdio", ex); 712 } 713 } 714 715 if (parsedArgs.runtimeInit) { 716 RuntimeInit.zygoteInit(parsedArgs.remainingArgs); 717 } else { 718 ClassLoader cloader; 719 720 if (parsedArgs.classpath != null) { 721 cloader 722 = new PathClassLoader(parsedArgs.classpath, 723 ClassLoader.getSystemClassLoader()); 724 } else { 725 cloader = ClassLoader.getSystemClassLoader(); 726 } 727 728 String className; 729 try { 730 className = parsedArgs.remainingArgs[0]; 731 } catch (ArrayIndexOutOfBoundsException ex) { 732 logAndPrintError (newStderr, 733 "Missing required class name argument", null); 734 return; 735 } 736 String[] mainArgs 737 = new String[parsedArgs.remainingArgs.length - 1]; 738 739 System.arraycopy(parsedArgs.remainingArgs, 1, 740 mainArgs, 0, mainArgs.length); 741 742 try { 743 ZygoteInit.invokeStaticMain(cloader, className, mainArgs); 744 } catch (RuntimeException ex) { 745 logAndPrintError (newStderr, "Error starting. ", ex); 746 } 747 } 748 } 749 750 /** 751 * Handles post-fork cleanup of parent proc 752 * 753 * @param pid != 0; pid of child if > 0 or indication of failed fork 754 * if < 0; 755 * @param descriptors null-ok; file descriptors for child's new stdio if 756 * specified. 757 * @param parsedArgs non-null; zygote args 758 * @return true for "exit command loop" and false for "continue command 759 * loop" 760 */ handleParentProc(int pid, FileDescriptor[] descriptors, Arguments parsedArgs)761 private boolean handleParentProc(int pid, 762 FileDescriptor[] descriptors, Arguments parsedArgs) { 763 764 if(pid > 0) { 765 // Try to move the new child into the peer's process group. 766 try { 767 ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); 768 } catch (IOException ex) { 769 // This exception is expected in the case where 770 // the peer is not in our session 771 // TODO get rid of this log message in the case where 772 // getsid(0) != getsid(peer.getPid()) 773 Log.i(TAG, "Zygote: setpgid failed. This is " 774 + "normal if peer is not in our session"); 775 } 776 } 777 778 try { 779 if (descriptors != null) { 780 for (FileDescriptor fd: descriptors) { 781 ZygoteInit.closeDescriptor(fd); 782 } 783 } 784 } catch (IOException ex) { 785 Log.e(TAG, "Error closing passed descriptors in " 786 + "parent process", ex); 787 } 788 789 try { 790 mSocketOutStream.writeInt(pid); 791 } catch (IOException ex) { 792 Log.e(TAG, "Error reading from command socket", ex); 793 return true; 794 } 795 796 /* 797 * If the peer wants to use the socket to wait on the 798 * newly spawned process, then we're all done. 799 */ 800 if (parsedArgs.peerWait) { 801 try { 802 mSocket.close(); 803 } catch (IOException ex) { 804 Log.e(TAG, "Zygote: error closing sockets", ex); 805 } 806 return true; 807 } 808 return false; 809 } 810 811 /** 812 * Logs an error message and prints it to the specified stream, if 813 * provided 814 * 815 * @param newStderr null-ok; a standard error stream 816 * @param message non-null; error message 817 * @param ex null-ok an exception 818 */ logAndPrintError(PrintStream newStderr, String message, Throwable ex)819 private static void logAndPrintError (PrintStream newStderr, 820 String message, Throwable ex) { 821 Log.e(TAG, message, ex); 822 if (newStderr != null) { 823 newStderr.println(message + (ex == null ? "" : ex)); 824 } 825 } 826 } 827