The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 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 | |
Narayan Kamath | 6ac7e67 | 2015-01-16 16:26:54 +0000 | [diff] [blame] | 19 | import static android.system.OsConstants.F_SETFD; |
Elliott Hughes | 3fe5951 | 2014-12-12 14:07:34 -0800 | [diff] [blame] | 20 | import static android.system.OsConstants.O_CLOEXEC; |
Elliott Hughes | dac83f5 | 2014-12-15 11:00:25 -0800 | [diff] [blame] | 21 | import static android.system.OsConstants.STDERR_FILENO; |
| 22 | import static android.system.OsConstants.STDIN_FILENO; |
| 23 | import static android.system.OsConstants.STDOUT_FILENO; |
Elliott Hughes | 3fe5951 | 2014-12-12 14:07:34 -0800 | [diff] [blame] | 24 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 25 | import android.net.Credentials; |
| 26 | import android.net.LocalSocket; |
| 27 | import android.os.Process; |
Jeff Sharkey | 5b1ada2 | 2012-08-14 18:47:09 -0700 | [diff] [blame] | 28 | import android.os.SELinux; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 29 | import android.os.SystemProperties; |
Narayan Kamath | fbb32f6 | 2015-06-12 15:34:35 +0100 | [diff] [blame] | 30 | import android.os.Trace; |
Elliott Hughes | 860c591 | 2014-04-28 19:19:13 -0700 | [diff] [blame] | 31 | import android.system.ErrnoException; |
| 32 | import android.system.Os; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 33 | import android.util.Log; |
Narayan Kamath | 37ad4b0 | 2015-01-19 16:05:24 +0000 | [diff] [blame] | 34 | import dalvik.system.VMRuntime; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 35 | import java.io.BufferedReader; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 36 | import java.io.DataInputStream; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 37 | import java.io.DataOutputStream; |
| 38 | import java.io.FileDescriptor; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 39 | import java.io.FileInputStream; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 40 | import java.io.FileOutputStream; |
| 41 | import java.io.IOException; |
| 42 | import java.io.InputStreamReader; |
| 43 | import java.io.PrintStream; |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 44 | import java.nio.charset.StandardCharsets; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 45 | import java.util.ArrayList; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 46 | import libcore.io.IoUtils; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 47 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 48 | /** |
| 49 | * A connection that can make spawn requests. |
| 50 | */ |
| 51 | class ZygoteConnection { |
| 52 | private static final String TAG = "Zygote"; |
| 53 | |
| 54 | /** a prototype instance for a future List.toArray() */ |
| 55 | private static final int[][] intArray2d = new int[0][0]; |
| 56 | |
| 57 | /** |
| 58 | * {@link android.net.LocalSocket#setSoTimeout} value for connections. |
| 59 | * Effectively, the amount of time a requestor has between the start of |
| 60 | * the request and the completed request. The select-loop mode Zygote |
| 61 | * doesn't have the logic to return to the select loop in the middle of |
| 62 | * a request, so we need to time out here to avoid being denial-of-serviced. |
| 63 | */ |
| 64 | private static final int CONNECTION_TIMEOUT_MILLIS = 1000; |
| 65 | |
| 66 | /** max number of arguments that a connection can specify */ |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 67 | private static final int MAX_ZYGOTE_ARGC = 1024; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 68 | |
| 69 | /** |
| 70 | * The command socket. |
| 71 | * |
| 72 | * mSocket is retained in the child process in "peer wait" mode, so |
| 73 | * that it closes when the child process terminates. In other cases, |
| 74 | * it is closed in the peer. |
| 75 | */ |
| 76 | private final LocalSocket mSocket; |
| 77 | private final DataOutputStream mSocketOutStream; |
| 78 | private final BufferedReader mSocketReader; |
| 79 | private final Credentials peer; |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 80 | private final String abiList; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 81 | |
| 82 | /** |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 83 | * Constructs instance from connected socket. |
| 84 | * |
| 85 | * @param socket non-null; connected socket |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 86 | * @param abiList non-null; a list of ABIs this zygote supports. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 87 | * @throws IOException |
| 88 | */ |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 89 | ZygoteConnection(LocalSocket socket, String abiList) throws IOException { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 90 | mSocket = socket; |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 91 | this.abiList = abiList; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 92 | |
| 93 | mSocketOutStream |
| 94 | = new DataOutputStream(socket.getOutputStream()); |
| 95 | |
| 96 | mSocketReader = new BufferedReader( |
| 97 | new InputStreamReader(socket.getInputStream()), 256); |
| 98 | |
| 99 | mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS); |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 100 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 101 | try { |
| 102 | peer = mSocket.getPeerCredentials(); |
| 103 | } catch (IOException ex) { |
| 104 | Log.e(TAG, "Cannot read peer credentials", ex); |
| 105 | throw ex; |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Returns the file descriptor of the associated socket. |
| 111 | * |
| 112 | * @return null-ok; file descriptor |
| 113 | */ |
| 114 | FileDescriptor getFileDesciptor() { |
| 115 | return mSocket.getFileDescriptor(); |
| 116 | } |
| 117 | |
| 118 | /** |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 119 | * Reads one start command from the command socket. If successful, |
| 120 | * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} |
| 121 | * exception is thrown in that child while in the parent process, |
| 122 | * the method returns normally. On failure, the child is not |
| 123 | * spawned and messages are printed to the log and stderr. Returns |
| 124 | * a boolean status value indicating whether an end-of-file on the command |
| 125 | * socket has been encountered. |
| 126 | * |
| 127 | * @return false if command socket should continue to be read from, or |
| 128 | * true if an end-of-file has been encountered. |
| 129 | * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() |
| 130 | * method in child process |
| 131 | */ |
| 132 | boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { |
| 133 | |
| 134 | String args[]; |
| 135 | Arguments parsedArgs = null; |
| 136 | FileDescriptor[] descriptors; |
| 137 | |
| 138 | try { |
| 139 | args = readArgumentList(); |
| 140 | descriptors = mSocket.getAncillaryFileDescriptors(); |
| 141 | } catch (IOException ex) { |
| 142 | Log.w(TAG, "IOException on command socket " + ex.getMessage()); |
| 143 | closeSocket(); |
| 144 | return true; |
| 145 | } |
| 146 | |
| 147 | if (args == null) { |
| 148 | // EOF reached. |
| 149 | closeSocket(); |
| 150 | return true; |
| 151 | } |
| 152 | |
| 153 | /** the stderr of the most recent request, if avail */ |
| 154 | PrintStream newStderr = null; |
| 155 | |
| 156 | if (descriptors != null && descriptors.length >= 3) { |
| 157 | newStderr = new PrintStream( |
| 158 | new FileOutputStream(descriptors[2])); |
| 159 | } |
| 160 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 161 | int pid = -1; |
| 162 | FileDescriptor childPipeFd = null; |
| 163 | FileDescriptor serverPipeFd = null; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 164 | |
| 165 | try { |
| 166 | parsedArgs = new Arguments(args); |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 167 | |
| 168 | if (parsedArgs.abiListQuery) { |
| 169 | return handleAbiListQuery(); |
| 170 | } |
| 171 | |
Elliott Hughes | 42a4bb5 | 2013-11-07 17:21:03 -0800 | [diff] [blame] | 172 | if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) { |
| 173 | throw new ZygoteSecurityException("Client may not specify capabilities: " + |
| 174 | "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) + |
| 175 | ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities)); |
| 176 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 177 | |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 178 | applyUidSecurityPolicy(parsedArgs, peer); |
| 179 | applyInvokeWithSecurityPolicy(parsedArgs, peer); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 180 | |
| 181 | applyDebuggerSystemProperty(parsedArgs); |
| 182 | applyInvokeWithSystemProperty(parsedArgs); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 183 | |
| 184 | int[][] rlimits = null; |
| 185 | |
| 186 | if (parsedArgs.rlimits != null) { |
| 187 | rlimits = parsedArgs.rlimits.toArray(intArray2d); |
| 188 | } |
| 189 | |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 190 | if (parsedArgs.invokeWith != null) { |
Elliott Hughes | 3fe5951 | 2014-12-12 14:07:34 -0800 | [diff] [blame] | 191 | FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 192 | childPipeFd = pipeFds[1]; |
| 193 | serverPipeFd = pipeFds[0]; |
Narayan Kamath | 6ac7e67 | 2015-01-16 16:26:54 +0000 | [diff] [blame] | 194 | Os.fcntlInt(childPipeFd, F_SETFD, 0); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 195 | } |
| 196 | |
Dave Platt | 89d4c89 | 2014-02-05 17:06:42 -0800 | [diff] [blame] | 197 | /** |
| 198 | * In order to avoid leaking descriptors to the Zygote child, |
| 199 | * the native code must close the two Zygote socket descriptors |
| 200 | * in the child process before it switches from Zygote-root to |
| 201 | * the UID and privileges of the application being launched. |
| 202 | * |
| 203 | * In order to avoid "bad file descriptor" errors when the |
| 204 | * two LocalSocket objects are closed, the Posix file |
| 205 | * descriptors are released via a dup2() call which closes |
| 206 | * the socket and substitutes an open descriptor to /dev/null. |
| 207 | */ |
| 208 | |
| 209 | int [] fdsToClose = { -1, -1 }; |
| 210 | |
| 211 | FileDescriptor fd = mSocket.getFileDescriptor(); |
| 212 | |
| 213 | if (fd != null) { |
| 214 | fdsToClose[0] = fd.getInt$(); |
| 215 | } |
| 216 | |
| 217 | fd = ZygoteInit.getServerSocketFileDescriptor(); |
| 218 | |
| 219 | if (fd != null) { |
| 220 | fdsToClose[1] = fd.getInt$(); |
| 221 | } |
| 222 | |
| 223 | fd = null; |
| 224 | |
Jeff Sharkey | 5b1ada2 | 2012-08-14 18:47:09 -0700 | [diff] [blame] | 225 | pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, |
| 226 | parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, |
jgu21 | 2eacd06 | 2014-09-10 06:55:07 -0400 | [diff] [blame] | 227 | parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet, |
| 228 | parsedArgs.appDataDir); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 229 | } catch (ErrnoException ex) { |
| 230 | logAndPrintError(newStderr, "Exception creating pipe", ex); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 231 | } catch (IllegalArgumentException ex) { |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 232 | logAndPrintError(newStderr, "Invalid zygote arguments", ex); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 233 | } catch (ZygoteSecurityException ex) { |
| 234 | logAndPrintError(newStderr, |
| 235 | "Zygote security policy prevents request: ", ex); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 236 | } |
| 237 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 238 | try { |
| 239 | if (pid == 0) { |
| 240 | // in child |
| 241 | IoUtils.closeQuietly(serverPipeFd); |
| 242 | serverPipeFd = null; |
| 243 | handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); |
| 244 | |
| 245 | // should never get here, the child is expected to either |
| 246 | // throw ZygoteInit.MethodAndArgsCaller or exec(). |
| 247 | return true; |
| 248 | } else { |
| 249 | // in parent...pid of < 0 means failure |
| 250 | IoUtils.closeQuietly(childPipeFd); |
| 251 | childPipeFd = null; |
| 252 | return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); |
| 253 | } |
| 254 | } finally { |
| 255 | IoUtils.closeQuietly(childPipeFd); |
| 256 | IoUtils.closeQuietly(serverPipeFd); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 257 | } |
| 258 | } |
| 259 | |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 260 | private boolean handleAbiListQuery() { |
| 261 | try { |
| 262 | final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII); |
| 263 | mSocketOutStream.writeInt(abiListBytes.length); |
| 264 | mSocketOutStream.write(abiListBytes); |
| 265 | return false; |
| 266 | } catch (IOException ioe) { |
| 267 | Log.e(TAG, "Error writing to command socket", ioe); |
| 268 | return true; |
| 269 | } |
| 270 | } |
| 271 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 272 | /** |
| 273 | * Closes socket associated with this connection. |
| 274 | */ |
| 275 | void closeSocket() { |
| 276 | try { |
| 277 | mSocket.close(); |
| 278 | } catch (IOException ex) { |
| 279 | Log.e(TAG, "Exception while closing command " |
| 280 | + "socket in parent", ex); |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | /** |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 285 | * Handles argument parsing for args related to the zygote spawner. |
| 286 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 287 | * Current recognized args: |
| 288 | * <ul> |
| 289 | * <li> --setuid=<i>uid of child process, defaults to 0</i> |
| 290 | * <li> --setgid=<i>gid of child process, defaults to 0</i> |
| 291 | * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i> |
| 292 | * <li> --capabilities=<i>a pair of comma-separated integer strings |
| 293 | * indicating Linux capabilities(2) set for child. The first string |
| 294 | * represents the <code>permitted</code> set, and the second the |
| 295 | * <code>effective</code> set. Precede each with 0 or |
| 296 | * 0x for octal or hexidecimal value. If unspecified, both default to 0. |
| 297 | * This parameter is only applied if the uid of the new process will |
| 298 | * be non-0. </i> |
| 299 | * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call. |
| 300 | * <code>r</code> is the resource, <code>c</code> and <code>m</code> |
| 301 | * are the settings for current and max value.</i> |
Andreas Gampe | aec67dc | 2014-09-02 21:23:06 -0700 | [diff] [blame] | 302 | * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate. |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 303 | * <li> --nice-name=<i>nice name to appear in ps</i> |
| 304 | * <li> --runtime-args indicates that the remaining arg list should |
| 305 | * be handed off to com.android.internal.os.RuntimeInit, rather than |
| 306 | * processed directly. |
| 307 | * Android runtime startup (eg, Binder initialization) is also eschewed. |
| 308 | * <li> [--] <args for RuntimeInit > |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 309 | * </ul> |
| 310 | */ |
| 311 | static class Arguments { |
| 312 | /** from --setuid */ |
| 313 | int uid = 0; |
| 314 | boolean uidSpecified; |
| 315 | |
| 316 | /** from --setgid */ |
| 317 | int gid = 0; |
| 318 | boolean gidSpecified; |
| 319 | |
| 320 | /** from --setgroups */ |
| 321 | int[] gids; |
| 322 | |
Ben Cheng | 23085b7 | 2010-02-08 16:06:32 -0800 | [diff] [blame] | 323 | /** |
Elliott Hughes | ae07ecf | 2011-07-06 17:33:27 -0700 | [diff] [blame] | 324 | * From --enable-debugger, --enable-checkjni, --enable-assert, |
Nicolas Geoffray | 9abbf45 | 2015-11-05 11:29:42 +0000 | [diff] [blame] | 325 | * --enable-safemode, --generate-debug-info and --enable-jni-logging. |
Ben Cheng | 23085b7 | 2010-02-08 16:06:32 -0800 | [diff] [blame] | 326 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 327 | int debugFlags; |
| 328 | |
Jeff Sharkey | 5b1ada2 | 2012-08-14 18:47:09 -0700 | [diff] [blame] | 329 | /** From --mount-external */ |
| 330 | int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; |
| 331 | |
Elliott Hughes | e1dfcb7 | 2011-07-08 11:08:07 -0700 | [diff] [blame] | 332 | /** from --target-sdk-version. */ |
| 333 | int targetSdkVersion; |
| 334 | boolean targetSdkVersionSpecified; |
| 335 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 336 | /** from --nice-name */ |
| 337 | String niceName; |
| 338 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 339 | /** from --capabilities */ |
| 340 | boolean capabilitiesSpecified; |
| 341 | long permittedCapabilities; |
| 342 | long effectiveCapabilities; |
| 343 | |
Stephen Smalley | 83d9eda | 2012-01-13 08:34:17 -0500 | [diff] [blame] | 344 | /** from --seinfo */ |
| 345 | boolean seInfoSpecified; |
| 346 | String seInfo; |
| 347 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 348 | /** from all --rlimit=r,c,m */ |
| 349 | ArrayList<int[]> rlimits; |
| 350 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 351 | /** from --invoke-with */ |
| 352 | String invokeWith; |
| 353 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 354 | /** |
| 355 | * Any args after and including the first non-option arg |
| 356 | * (or after a '--') |
| 357 | */ |
| 358 | String remainingArgs[]; |
| 359 | |
| 360 | /** |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 361 | * Whether the current arguments constitute an ABI list query. |
| 362 | */ |
| 363 | boolean abiListQuery; |
| 364 | |
| 365 | /** |
Andreas Gampe | aec67dc | 2014-09-02 21:23:06 -0700 | [diff] [blame] | 366 | * The instruction set to use, or null when not important. |
| 367 | */ |
| 368 | String instructionSet; |
| 369 | |
| 370 | /** |
jgu21 | 2eacd06 | 2014-09-10 06:55:07 -0400 | [diff] [blame] | 371 | * The app data directory. May be null, e.g., for the system server. Note that this might |
| 372 | * not be reliable in the case of process-sharing apps. |
| 373 | */ |
| 374 | String appDataDir; |
| 375 | |
| 376 | /** |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 377 | * Constructs instance and parses args |
| 378 | * @param args zygote command-line args |
| 379 | * @throws IllegalArgumentException |
| 380 | */ |
| 381 | Arguments(String args[]) throws IllegalArgumentException { |
| 382 | parseArgs(args); |
| 383 | } |
| 384 | |
| 385 | /** |
| 386 | * Parses the commandline arguments intended for the Zygote spawner |
| 387 | * (such as "--setuid=" and "--setgid=") and creates an array |
| 388 | * containing the remaining args. |
| 389 | * |
| 390 | * Per security review bug #1112214, duplicate args are disallowed in |
| 391 | * critical cases to make injection harder. |
| 392 | */ |
| 393 | private void parseArgs(String args[]) |
| 394 | throws IllegalArgumentException { |
| 395 | int curArg = 0; |
| 396 | |
Narayan Kamath | b6b044a | 2015-02-13 17:31:25 +0000 | [diff] [blame] | 397 | boolean seenRuntimeArgs = false; |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 398 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 399 | for ( /* curArg */ ; curArg < args.length; curArg++) { |
| 400 | String arg = args[curArg]; |
| 401 | |
| 402 | if (arg.equals("--")) { |
| 403 | curArg++; |
| 404 | break; |
| 405 | } else if (arg.startsWith("--setuid=")) { |
| 406 | if (uidSpecified) { |
| 407 | throw new IllegalArgumentException( |
| 408 | "Duplicate arg specified"); |
| 409 | } |
| 410 | uidSpecified = true; |
| 411 | uid = Integer.parseInt( |
| 412 | arg.substring(arg.indexOf('=') + 1)); |
| 413 | } else if (arg.startsWith("--setgid=")) { |
| 414 | if (gidSpecified) { |
| 415 | throw new IllegalArgumentException( |
| 416 | "Duplicate arg specified"); |
| 417 | } |
| 418 | gidSpecified = true; |
| 419 | gid = Integer.parseInt( |
| 420 | arg.substring(arg.indexOf('=') + 1)); |
Elliott Hughes | e1dfcb7 | 2011-07-08 11:08:07 -0700 | [diff] [blame] | 421 | } else if (arg.startsWith("--target-sdk-version=")) { |
| 422 | if (targetSdkVersionSpecified) { |
| 423 | throw new IllegalArgumentException( |
| 424 | "Duplicate target-sdk-version specified"); |
| 425 | } |
| 426 | targetSdkVersionSpecified = true; |
| 427 | targetSdkVersion = Integer.parseInt( |
| 428 | arg.substring(arg.indexOf('=') + 1)); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 429 | } else if (arg.equals("--enable-debugger")) { |
| 430 | debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; |
Ben Cheng | 23085b7 | 2010-02-08 16:06:32 -0800 | [diff] [blame] | 431 | } else if (arg.equals("--enable-safemode")) { |
| 432 | debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 433 | } else if (arg.equals("--enable-checkjni")) { |
| 434 | debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; |
David Srbecky | 065075e | 2015-05-28 17:16:09 +0100 | [diff] [blame] | 435 | } else if (arg.equals("--generate-debug-info")) { |
| 436 | debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; |
Tamas Berghammer | df6cb28 | 2016-01-29 12:07:00 +0000 | [diff] [blame] | 437 | } else if (arg.equals("--always-jit")) { |
| 438 | debugFlags |= Zygote.DEBUG_ALWAYS_JIT; |
| 439 | } else if (arg.equals("--native-debuggable")) { |
| 440 | debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; |
Elliott Hughes | ae07ecf | 2011-07-06 17:33:27 -0700 | [diff] [blame] | 441 | } else if (arg.equals("--enable-jni-logging")) { |
| 442 | debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 443 | } else if (arg.equals("--enable-assert")) { |
| 444 | debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 445 | } else if (arg.equals("--runtime-args")) { |
| 446 | seenRuntimeArgs = true; |
Stephen Smalley | 83d9eda | 2012-01-13 08:34:17 -0500 | [diff] [blame] | 447 | } else if (arg.startsWith("--seinfo=")) { |
| 448 | if (seInfoSpecified) { |
| 449 | throw new IllegalArgumentException( |
| 450 | "Duplicate arg specified"); |
| 451 | } |
| 452 | seInfoSpecified = true; |
| 453 | seInfo = arg.substring(arg.indexOf('=') + 1); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 454 | } else if (arg.startsWith("--capabilities=")) { |
| 455 | if (capabilitiesSpecified) { |
| 456 | throw new IllegalArgumentException( |
| 457 | "Duplicate arg specified"); |
| 458 | } |
| 459 | capabilitiesSpecified = true; |
| 460 | String capString = arg.substring(arg.indexOf('=')+1); |
| 461 | |
| 462 | String[] capStrings = capString.split(",", 2); |
| 463 | |
| 464 | if (capStrings.length == 1) { |
| 465 | effectiveCapabilities = Long.decode(capStrings[0]); |
| 466 | permittedCapabilities = effectiveCapabilities; |
| 467 | } else { |
| 468 | permittedCapabilities = Long.decode(capStrings[0]); |
| 469 | effectiveCapabilities = Long.decode(capStrings[1]); |
| 470 | } |
| 471 | } else if (arg.startsWith("--rlimit=")) { |
| 472 | // Duplicate --rlimit arguments are specifically allowed. |
| 473 | String[] limitStrings |
| 474 | = arg.substring(arg.indexOf('=')+1).split(","); |
| 475 | |
| 476 | if (limitStrings.length != 3) { |
| 477 | throw new IllegalArgumentException( |
| 478 | "--rlimit= should have 3 comma-delimited ints"); |
| 479 | } |
| 480 | int[] rlimitTuple = new int[limitStrings.length]; |
| 481 | |
| 482 | for(int i=0; i < limitStrings.length; i++) { |
| 483 | rlimitTuple[i] = Integer.parseInt(limitStrings[i]); |
| 484 | } |
| 485 | |
| 486 | if (rlimits == null) { |
| 487 | rlimits = new ArrayList(); |
| 488 | } |
| 489 | |
| 490 | rlimits.add(rlimitTuple); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 491 | } else if (arg.startsWith("--setgroups=")) { |
| 492 | if (gids != null) { |
| 493 | throw new IllegalArgumentException( |
| 494 | "Duplicate arg specified"); |
| 495 | } |
| 496 | |
| 497 | String[] params |
| 498 | = arg.substring(arg.indexOf('=') + 1).split(","); |
| 499 | |
| 500 | gids = new int[params.length]; |
| 501 | |
| 502 | for (int i = params.length - 1; i >= 0 ; i--) { |
| 503 | gids[i] = Integer.parseInt(params[i]); |
| 504 | } |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 505 | } else if (arg.equals("--invoke-with")) { |
| 506 | if (invokeWith != null) { |
| 507 | throw new IllegalArgumentException( |
| 508 | "Duplicate arg specified"); |
| 509 | } |
| 510 | try { |
| 511 | invokeWith = args[++curArg]; |
| 512 | } catch (IndexOutOfBoundsException ex) { |
| 513 | throw new IllegalArgumentException( |
| 514 | "--invoke-with requires argument"); |
| 515 | } |
| 516 | } else if (arg.startsWith("--nice-name=")) { |
| 517 | if (niceName != null) { |
| 518 | throw new IllegalArgumentException( |
| 519 | "Duplicate arg specified"); |
| 520 | } |
| 521 | niceName = arg.substring(arg.indexOf('=') + 1); |
Jeff Sharkey | 4887789 | 2015-03-18 11:27:19 -0700 | [diff] [blame] | 522 | } else if (arg.equals("--mount-external-default")) { |
| 523 | mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT; |
Jeff Sharkey | 9527b22 | 2015-06-24 15:24:48 -0700 | [diff] [blame] | 524 | } else if (arg.equals("--mount-external-read")) { |
| 525 | mountExternal = Zygote.MOUNT_EXTERNAL_READ; |
| 526 | } else if (arg.equals("--mount-external-write")) { |
| 527 | mountExternal = Zygote.MOUNT_EXTERNAL_WRITE; |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 528 | } else if (arg.equals("--query-abi-list")) { |
| 529 | abiListQuery = true; |
Andreas Gampe | aec67dc | 2014-09-02 21:23:06 -0700 | [diff] [blame] | 530 | } else if (arg.startsWith("--instruction-set=")) { |
| 531 | instructionSet = arg.substring(arg.indexOf('=') + 1); |
jgu21 | 2eacd06 | 2014-09-10 06:55:07 -0400 | [diff] [blame] | 532 | } else if (arg.startsWith("--app-data-dir=")) { |
| 533 | appDataDir = arg.substring(arg.indexOf('=') + 1); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 534 | } else { |
| 535 | break; |
| 536 | } |
| 537 | } |
| 538 | |
Narayan Kamath | b6b044a | 2015-02-13 17:31:25 +0000 | [diff] [blame] | 539 | if (abiListQuery) { |
| 540 | if (args.length - curArg > 0) { |
| 541 | throw new IllegalArgumentException("Unexpected arguments after --query-abi-list."); |
| 542 | } |
| 543 | } else { |
| 544 | if (!seenRuntimeArgs) { |
| 545 | throw new IllegalArgumentException("Unexpected argument : " + args[curArg]); |
| 546 | } |
| 547 | |
| 548 | remainingArgs = new String[args.length - curArg]; |
| 549 | System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 550 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 551 | } |
| 552 | } |
| 553 | |
| 554 | /** |
| 555 | * Reads an argument list from the command socket/ |
| 556 | * @return Argument list or null if EOF is reached |
| 557 | * @throws IOException passed straight through |
| 558 | */ |
| 559 | private String[] readArgumentList() |
| 560 | throws IOException { |
| 561 | |
| 562 | /** |
| 563 | * See android.os.Process.zygoteSendArgsAndGetPid() |
| 564 | * Presently the wire format to the zygote process is: |
| 565 | * a) a count of arguments (argc, in essence) |
| 566 | * b) a number of newline-separated argument strings equal to count |
| 567 | * |
| 568 | * After the zygote process reads these it will write the pid of |
| 569 | * the child or -1 on failure. |
| 570 | */ |
| 571 | |
| 572 | int argc; |
| 573 | |
| 574 | try { |
| 575 | String s = mSocketReader.readLine(); |
| 576 | |
| 577 | if (s == null) { |
| 578 | // EOF reached. |
| 579 | return null; |
| 580 | } |
| 581 | argc = Integer.parseInt(s); |
| 582 | } catch (NumberFormatException ex) { |
| 583 | Log.e(TAG, "invalid Zygote wire format: non-int at argc"); |
| 584 | throw new IOException("invalid wire format"); |
| 585 | } |
| 586 | |
| 587 | // See bug 1092107: large argc can be used for a DOS attack |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 588 | if (argc > MAX_ZYGOTE_ARGC) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 589 | throw new IOException("max arg count exceeded"); |
| 590 | } |
| 591 | |
| 592 | String[] result = new String[argc]; |
| 593 | for (int i = 0; i < argc; i++) { |
| 594 | result[i] = mSocketReader.readLine(); |
| 595 | if (result[i] == null) { |
| 596 | // We got an unexpected EOF. |
| 597 | throw new IOException("truncated request"); |
| 598 | } |
| 599 | } |
| 600 | |
| 601 | return result; |
| 602 | } |
| 603 | |
| 604 | /** |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 605 | * uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 606 | * operation. It may also specify any gid and setgroups() list it chooses. |
| 607 | * In factory test mode, it may specify any UID. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 608 | * |
| 609 | * @param args non-null; zygote spawner arguments |
| 610 | * @param peer non-null; peer credentials |
| 611 | * @throws ZygoteSecurityException |
| 612 | */ |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 613 | private static void applyUidSecurityPolicy(Arguments args, Credentials peer) |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 614 | throws ZygoteSecurityException { |
| 615 | |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 616 | if (peer.getUid() == Process.SYSTEM_UID) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 617 | String factoryTest = SystemProperties.get("ro.factorytest"); |
| 618 | boolean uidRestricted; |
| 619 | |
| 620 | /* In normal operation, SYSTEM_UID can only specify a restricted |
| 621 | * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid. |
| 622 | */ |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 623 | uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2")); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 624 | |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 625 | if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 626 | throw new ZygoteSecurityException( |
| 627 | "System UID may not launch process with UID < " |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 628 | + Process.SYSTEM_UID); |
Stephen Smalley | 83d9eda | 2012-01-13 08:34:17 -0500 | [diff] [blame] | 629 | } |
| 630 | } |
| 631 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 632 | // If not otherwise specified, uid and gid are inherited from peer |
| 633 | if (!args.uidSpecified) { |
| 634 | args.uid = peer.getUid(); |
| 635 | args.uidSpecified = true; |
| 636 | } |
| 637 | if (!args.gidSpecified) { |
| 638 | args.gid = peer.getGid(); |
| 639 | args.gidSpecified = true; |
| 640 | } |
| 641 | } |
| 642 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 643 | /** |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 644 | * Applies debugger system properties to the zygote arguments. |
| 645 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 646 | * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, |
| 647 | * the debugger state is specified via the "--enable-debugger" flag |
| 648 | * in the spawn request. |
| 649 | * |
| 650 | * @param args non-null; zygote spawner args |
| 651 | */ |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 652 | public static void applyDebuggerSystemProperty(Arguments args) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 653 | if ("1".equals(SystemProperties.get("ro.debuggable"))) { |
| 654 | args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | /** |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 659 | * Applies zygote security policy. |
| 660 | * Based on the credentials of the process issuing a zygote command: |
| 661 | * <ol> |
| 662 | * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a |
| 663 | * wrapper command. |
| 664 | * <li> Any other uid may not specify any invoke-with argument. |
| 665 | * </ul> |
| 666 | * |
| 667 | * @param args non-null; zygote spawner arguments |
| 668 | * @param peer non-null; peer credentials |
| 669 | * @throws ZygoteSecurityException |
| 670 | */ |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 671 | private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer) |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 672 | throws ZygoteSecurityException { |
| 673 | int peerUid = peer.getUid(); |
| 674 | |
| 675 | if (args.invokeWith != null && peerUid != 0) { |
| 676 | throw new ZygoteSecurityException("Peer is not permitted to specify " |
| 677 | + "an explicit invoke-with wrapper command"); |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | /** |
| 682 | * Applies invoke-with system properties to the zygote arguments. |
| 683 | * |
Narayan Kamath | 973b466 | 2014-03-31 13:41:26 +0100 | [diff] [blame] | 684 | * @param args non-null; zygote args |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 685 | */ |
| 686 | public static void applyInvokeWithSystemProperty(Arguments args) { |
| 687 | if (args.invokeWith == null && args.niceName != null) { |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 688 | String property = "wrap." + args.niceName; |
| 689 | if (property.length() > 31) { |
| 690 | // Properties with a trailing "." are illegal. |
| 691 | if (property.charAt(30) != '.') { |
| 692 | property = property.substring(0, 31); |
| 693 | } else { |
| 694 | property = property.substring(0, 30); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 695 | } |
dcashman | fc4c0bf8 | 2015-03-05 17:17:47 -0800 | [diff] [blame] | 696 | } |
| 697 | args.invokeWith = SystemProperties.get(property); |
| 698 | if (args.invokeWith != null && args.invokeWith.length() == 0) { |
| 699 | args.invokeWith = null; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 700 | } |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | /** |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 705 | * Handles post-fork setup of child proc, closing sockets as appropriate, |
| 706 | * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller |
| 707 | * if successful or returning if failed. |
| 708 | * |
| 709 | * @param parsedArgs non-null; zygote args |
| 710 | * @param descriptors null-ok; new file descriptors for stdio if available. |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 711 | * @param pipeFd null-ok; pipe for communication back to Zygote. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 712 | * @param newStderr null-ok; stream to use for stderr until stdio |
| 713 | * is reopened. |
| 714 | * |
| 715 | * @throws ZygoteInit.MethodAndArgsCaller on success to |
| 716 | * trampoline to code that invokes static main. |
| 717 | */ |
| 718 | private void handleChildProc(Arguments parsedArgs, |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 719 | FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 720 | throws ZygoteInit.MethodAndArgsCaller { |
Dave Platt | 89d4c89 | 2014-02-05 17:06:42 -0800 | [diff] [blame] | 721 | /** |
| 722 | * By the time we get here, the native code has closed the two actual Zygote |
| 723 | * socket connections, and substituted /dev/null in their place. The LocalSocket |
| 724 | * objects still need to be closed properly. |
| 725 | */ |
| 726 | |
Nick Kralevich | 468f6c1 | 2013-01-30 08:45:03 -0800 | [diff] [blame] | 727 | closeSocket(); |
| 728 | ZygoteInit.closeServerSocket(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 729 | |
| 730 | if (descriptors != null) { |
| 731 | try { |
Elliott Hughes | dac83f5 | 2014-12-15 11:00:25 -0800 | [diff] [blame] | 732 | Os.dup2(descriptors[0], STDIN_FILENO); |
| 733 | Os.dup2(descriptors[1], STDOUT_FILENO); |
| 734 | Os.dup2(descriptors[2], STDERR_FILENO); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 735 | |
| 736 | for (FileDescriptor fd: descriptors) { |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 737 | IoUtils.closeQuietly(fd); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 738 | } |
| 739 | newStderr = System.err; |
Elliott Hughes | dac83f5 | 2014-12-15 11:00:25 -0800 | [diff] [blame] | 740 | } catch (ErrnoException ex) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 741 | Log.e(TAG, "Error reopening stdio", ex); |
| 742 | } |
| 743 | } |
| 744 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 745 | if (parsedArgs.niceName != null) { |
| 746 | Process.setArgV0(parsedArgs.niceName); |
| 747 | } |
| 748 | |
Narayan Kamath | fbb32f6 | 2015-06-12 15:34:35 +0100 | [diff] [blame] | 749 | // End of the postFork event. |
| 750 | Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 751 | if (parsedArgs.invokeWith != null) { |
| 752 | WrapperInit.execApplication(parsedArgs.invokeWith, |
| 753 | parsedArgs.niceName, parsedArgs.targetSdkVersion, |
Narayan Kamath | 37ad4b0 | 2015-01-19 16:05:24 +0000 | [diff] [blame] | 754 | VMRuntime.getCurrentInstructionSet(), |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 755 | pipeFd, parsedArgs.remainingArgs); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 756 | } else { |
Narayan Kamath | f48029f | 2015-01-08 12:45:37 +0000 | [diff] [blame] | 757 | RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, |
| 758 | parsedArgs.remainingArgs, null /* classLoader */); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 759 | } |
| 760 | } |
| 761 | |
| 762 | /** |
| 763 | * Handles post-fork cleanup of parent proc |
| 764 | * |
| 765 | * @param pid != 0; pid of child if > 0 or indication of failed fork |
| 766 | * if < 0; |
| 767 | * @param descriptors null-ok; file descriptors for child's new stdio if |
| 768 | * specified. |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 769 | * @param pipeFd null-ok; pipe for communication with child. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 770 | * @param parsedArgs non-null; zygote args |
| 771 | * @return true for "exit command loop" and false for "continue command |
| 772 | * loop" |
| 773 | */ |
| 774 | private boolean handleParentProc(int pid, |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 775 | FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 776 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 777 | if (pid > 0) { |
| 778 | setChildPgid(pid); |
| 779 | } |
| 780 | |
| 781 | if (descriptors != null) { |
| 782 | for (FileDescriptor fd: descriptors) { |
| 783 | IoUtils.closeQuietly(fd); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 784 | } |
| 785 | } |
| 786 | |
Jeff Brown | 3f9dd28 | 2011-07-08 20:02:19 -0700 | [diff] [blame] | 787 | boolean usingWrapper = false; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 788 | if (pipeFd != null && pid > 0) { |
| 789 | DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); |
| 790 | int innerPid = -1; |
| 791 | try { |
| 792 | innerPid = is.readInt(); |
| 793 | } catch (IOException ex) { |
| 794 | Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex); |
| 795 | } finally { |
| 796 | try { |
| 797 | is.close(); |
| 798 | } catch (IOException ex) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 799 | } |
| 800 | } |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 801 | |
| 802 | // Ensure that the pid reported by the wrapped process is either the |
| 803 | // child process that we forked, or a descendant of it. |
| 804 | if (innerPid > 0) { |
| 805 | int parentPid = innerPid; |
| 806 | while (parentPid > 0 && parentPid != pid) { |
| 807 | parentPid = Process.getParentPid(parentPid); |
| 808 | } |
| 809 | if (parentPid > 0) { |
| 810 | Log.i(TAG, "Wrapped process has pid " + innerPid); |
| 811 | pid = innerPid; |
Jeff Brown | 3f9dd28 | 2011-07-08 20:02:19 -0700 | [diff] [blame] | 812 | usingWrapper = true; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 813 | } else { |
| 814 | Log.w(TAG, "Wrapped process reported a pid that is not a child of " |
| 815 | + "the process that we forked: childPid=" + pid |
| 816 | + " innerPid=" + innerPid); |
| 817 | } |
| 818 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 819 | } |
| 820 | |
| 821 | try { |
| 822 | mSocketOutStream.writeInt(pid); |
Jeff Brown | 3f9dd28 | 2011-07-08 20:02:19 -0700 | [diff] [blame] | 823 | mSocketOutStream.writeBoolean(usingWrapper); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 824 | } catch (IOException ex) { |
Narayan Kamath | c41638c | 2014-04-07 13:56:15 +0100 | [diff] [blame] | 825 | Log.e(TAG, "Error writing to command socket", ex); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 826 | return true; |
| 827 | } |
| 828 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 829 | return false; |
| 830 | } |
| 831 | |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 832 | private void setChildPgid(int pid) { |
| 833 | // Try to move the new child into the peer's process group. |
| 834 | try { |
Elliott Hughes | 26b56e6 | 2014-12-17 12:28:29 -0800 | [diff] [blame] | 835 | Os.setpgid(pid, Os.getpgid(peer.getPid())); |
| 836 | } catch (ErrnoException ex) { |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 837 | // This exception is expected in the case where |
| 838 | // the peer is not in our session |
| 839 | // TODO get rid of this log message in the case where |
| 840 | // getsid(0) != getsid(peer.getPid()) |
| 841 | Log.i(TAG, "Zygote: setpgid failed. This is " |
| 842 | + "normal if peer is not in our session"); |
| 843 | } |
| 844 | } |
| 845 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 846 | /** |
| 847 | * Logs an error message and prints it to the specified stream, if |
| 848 | * provided |
| 849 | * |
| 850 | * @param newStderr null-ok; a standard error stream |
| 851 | * @param message non-null; error message |
| 852 | * @param ex null-ok an exception |
| 853 | */ |
| 854 | private static void logAndPrintError (PrintStream newStderr, |
| 855 | String message, Throwable ex) { |
| 856 | Log.e(TAG, message, ex); |
| 857 | if (newStderr != null) { |
| 858 | newStderr.println(message + (ex == null ? "" : ex)); |
| 859 | } |
| 860 | } |
| 861 | } |