1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.os; 18 19 import static android.system.OsConstants.POLLIN; 20 21 import android.net.LocalServerSocket; 22 import android.net.LocalSocket; 23 import android.system.Os; 24 import android.system.ErrnoException; 25 import android.system.StructPollfd; 26 import android.util.Log; 27 28 import android.util.Slog; 29 import java.io.IOException; 30 import java.io.FileDescriptor; 31 import java.util.ArrayList; 32 33 /** 34 * Server socket class for zygote processes. 35 * 36 * Provides functions to wait for commands on a UNIX domain socket, and fork 37 * off child processes that inherit the initial state of the VM.% 38 * 39 * Please see {@link ZygoteConnection.Arguments} for documentation on the 40 * client protocol. 41 */ 42 class ZygoteServer { 43 public static final String TAG = "ZygoteServer"; 44 45 private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; 46 47 /** 48 * Listening socket that accepts new server connections. 49 */ 50 private LocalServerSocket mServerSocket; 51 52 /** 53 * Whether or not mServerSocket's underlying FD should be closed directly. 54 * If mServerSocket is created with an existing FD, closing the socket does 55 * not close the FD and it must be closed explicitly. If the socket is created 56 * with a name instead, then closing the socket will close the underlying FD 57 * and it should not be double-closed. 58 */ 59 private boolean mCloseSocketFd; 60 61 /** 62 * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}. 63 */ 64 private boolean mIsForkChild; 65 ZygoteServer()66 ZygoteServer() { 67 } 68 setForkChild()69 void setForkChild() { 70 mIsForkChild = true; 71 } 72 73 /** 74 * Registers a server socket for zygote command connections. This locates the server socket 75 * file descriptor through an ANDROID_SOCKET_ environment variable. 76 * 77 * @throws RuntimeException when open fails 78 */ registerServerSocketFromEnv(String socketName)79 void registerServerSocketFromEnv(String socketName) { 80 if (mServerSocket == null) { 81 int fileDesc; 82 final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; 83 try { 84 String env = System.getenv(fullSocketName); 85 fileDesc = Integer.parseInt(env); 86 } catch (RuntimeException ex) { 87 throw new RuntimeException(fullSocketName + " unset or invalid", ex); 88 } 89 90 try { 91 FileDescriptor fd = new FileDescriptor(); 92 fd.setInt$(fileDesc); 93 mServerSocket = new LocalServerSocket(fd); 94 mCloseSocketFd = true; 95 } catch (IOException ex) { 96 throw new RuntimeException( 97 "Error binding to local socket '" + fileDesc + "'", ex); 98 } 99 } 100 } 101 102 /** 103 * Registers a server socket for zygote command connections. This opens the server socket 104 * at the specified name in the abstract socket namespace. 105 */ registerServerSocketAtAbstractName(String socketName)106 void registerServerSocketAtAbstractName(String socketName) { 107 if (mServerSocket == null) { 108 try { 109 mServerSocket = new LocalServerSocket(socketName); 110 mCloseSocketFd = false; 111 } catch (IOException ex) { 112 throw new RuntimeException( 113 "Error binding to abstract socket '" + socketName + "'", ex); 114 } 115 } 116 } 117 118 /** 119 * Waits for and accepts a single command connection. Throws 120 * RuntimeException on failure. 121 */ acceptCommandPeer(String abiList)122 private ZygoteConnection acceptCommandPeer(String abiList) { 123 try { 124 return createNewConnection(mServerSocket.accept(), abiList); 125 } catch (IOException ex) { 126 throw new RuntimeException( 127 "IOException during accept()", ex); 128 } 129 } 130 createNewConnection(LocalSocket socket, String abiList)131 protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList) 132 throws IOException { 133 return new ZygoteConnection(socket, abiList); 134 } 135 136 /** 137 * Close and clean up zygote sockets. Called on shutdown and on the 138 * child's exit path. 139 */ closeServerSocket()140 void closeServerSocket() { 141 try { 142 if (mServerSocket != null) { 143 FileDescriptor fd = mServerSocket.getFileDescriptor(); 144 mServerSocket.close(); 145 if (fd != null && mCloseSocketFd) { 146 Os.close(fd); 147 } 148 } 149 } catch (IOException ex) { 150 Log.e(TAG, "Zygote: error closing sockets", ex); 151 } catch (ErrnoException ex) { 152 Log.e(TAG, "Zygote: error closing descriptor", ex); 153 } 154 155 mServerSocket = null; 156 } 157 158 /** 159 * Return the server socket's underlying file descriptor, so that 160 * ZygoteConnection can pass it to the native code for proper 161 * closure after a child process is forked off. 162 */ 163 getServerSocketFileDescriptor()164 FileDescriptor getServerSocketFileDescriptor() { 165 return mServerSocket.getFileDescriptor(); 166 } 167 168 /** 169 * Runs the zygote process's select loop. Accepts new connections as 170 * they happen, and reads commands from connections one spawn-request's 171 * worth at a time. 172 */ runSelectLoop(String abiList)173 Runnable runSelectLoop(String abiList) { 174 ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); 175 ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); 176 177 fds.add(mServerSocket.getFileDescriptor()); 178 peers.add(null); 179 180 while (true) { 181 StructPollfd[] pollFds = new StructPollfd[fds.size()]; 182 for (int i = 0; i < pollFds.length; ++i) { 183 pollFds[i] = new StructPollfd(); 184 pollFds[i].fd = fds.get(i); 185 pollFds[i].events = (short) POLLIN; 186 } 187 try { 188 Os.poll(pollFds, -1); 189 } catch (ErrnoException ex) { 190 throw new RuntimeException("poll failed", ex); 191 } 192 for (int i = pollFds.length - 1; i >= 0; --i) { 193 if ((pollFds[i].revents & POLLIN) == 0) { 194 continue; 195 } 196 197 if (i == 0) { 198 ZygoteConnection newPeer = acceptCommandPeer(abiList); 199 peers.add(newPeer); 200 fds.add(newPeer.getFileDesciptor()); 201 } else { 202 try { 203 ZygoteConnection connection = peers.get(i); 204 final Runnable command = connection.processOneCommand(this); 205 206 if (mIsForkChild) { 207 // We're in the child. We should always have a command to run at this 208 // stage if processOneCommand hasn't called "exec". 209 if (command == null) { 210 throw new IllegalStateException("command == null"); 211 } 212 213 return command; 214 } else { 215 // We're in the server - we should never have any commands to run. 216 if (command != null) { 217 throw new IllegalStateException("command != null"); 218 } 219 220 // We don't know whether the remote side of the socket was closed or 221 // not until we attempt to read from it from processOneCommand. This shows up as 222 // a regular POLLIN event in our regular processing loop. 223 if (connection.isClosedByPeer()) { 224 connection.closeSocket(); 225 peers.remove(i); 226 fds.remove(i); 227 } 228 } 229 } catch (Exception e) { 230 if (!mIsForkChild) { 231 // We're in the server so any exception here is one that has taken place 232 // pre-fork while processing commands or reading / writing from the 233 // control socket. Make a loud noise about any such exceptions so that 234 // we know exactly what failed and why. 235 236 Slog.e(TAG, "Exception executing zygote command: ", e); 237 238 // Make sure the socket is closed so that the other end knows immediately 239 // that something has gone wrong and doesn't time out waiting for a 240 // response. 241 ZygoteConnection conn = peers.remove(i); 242 conn.closeSocket(); 243 244 fds.remove(i); 245 } else { 246 // We're in the child so any exception caught here has happened post 247 // fork and before we execute ActivityThread.main (or any other main() 248 // method). Log the details of the exception and bring down the process. 249 Log.e(TAG, "Caught post-fork exception in child process.", e); 250 throw e; 251 } 252 } finally { 253 // Reset the child flag, in the event that the child process is a child- 254 // zygote. The flag will not be consulted this loop pass after the Runnable 255 // is returned. 256 mIsForkChild = false; 257 } 258 } 259 } 260 } 261 } 262 } 263