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