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