blob: 4d2fe721d311ea94f5c5f1de78a4e6358bbafa17 [file] [log] [blame]
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08001/*
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#define LOG_TAG "ProcessManager"
18
crazybob410530d2009-06-18 17:19:04 -050019#include <sys/resource.h>
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080020#include <sys/types.h>
21#include <sys/wait.h>
22#include <unistd.h>
23#include <fcntl.h>
24#include <signal.h>
25#include <stdlib.h>
26#include <string.h>
27#include <errno.h>
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080028
29#include "jni.h"
30#include "JNIHelp.h"
Elliott Hughesa9f5c162010-06-16 16:32:18 -070031#include "JniConstants.h"
Elliott Hughese9f12042011-05-11 11:48:46 -070032#include "ScopedLocalRef.h"
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080033#include "utils/Log.h"
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080034
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080035/*
36 * These are constants shared with the higher level code in
37 * ProcessManager.java.
38 */
39#define WAIT_STATUS_UNKNOWN (-1) // unknown child status
40#define WAIT_STATUS_NO_CHILDREN (-2) // no children to wait for
41#define WAIT_STATUS_STRANGE_ERRNO (-3) // observed an undocumented errno
42
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080043/**
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080044 * Loops indefinitely and calls ProcessManager.onExit() when children exit.
45 */
Elliott Hughesa5fb7062011-04-28 15:36:47 -070046static void ProcessManager_watchChildren(JNIEnv* env, jclass processManagerClass, jobject processManager) {
47 static jmethodID onExitMethod = env->GetMethodID(processManagerClass, "onExit", "(II)V");
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080048 if (onExitMethod == NULL) {
Elliott Hughesa5fb7062011-04-28 15:36:47 -070049 return;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080050 }
51
Elliott Hughesa5fb7062011-04-28 15:36:47 -070052 while (true) {
53 // Wait for children in our process group.
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080054 int status;
Andy McFaddena327fd22009-04-08 12:51:10 -070055 pid_t pid = waitpid(0, &status, 0);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080056
57 if (pid >= 0) {
58 // Extract real status.
59 if (WIFEXITED(status)) {
60 status = WEXITSTATUS(status);
61 } else if (WIFSIGNALED(status)) {
62 status = WTERMSIG(status);
63 } else if (WIFSTOPPED(status)) {
64 status = WSTOPSIG(status);
65 } else {
66 status = WAIT_STATUS_UNKNOWN;
67 }
68 } else {
69 /*
70 * The pid should be -1 already, but force it here just in case
71 * we somehow end up with some other negative value.
72 */
73 pid = -1;
74
75 switch (errno) {
76 case ECHILD: {
77 /*
78 * Expected errno: There are no children to wait()
79 * for. The callback will sleep until it is
80 * informed of another child coming to life.
81 */
82 status = WAIT_STATUS_NO_CHILDREN;
83 break;
84 }
85 case EINTR: {
86 /*
87 * An unblocked signal came in while waiting; just
88 * retry the wait().
89 */
90 continue;
91 }
92 default: {
93 /*
94 * Unexpected errno, so squawk! Note: Per the
95 * Linux docs, there are no errnos defined for
96 * wait() other than the two that are handled
97 * immediately above.
98 */
Elliott Hughesa5fb7062011-04-28 15:36:47 -070099 LOGE("Error %d calling wait(): %s", errno, strerror(errno));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800100 status = WAIT_STATUS_STRANGE_ERRNO;
101 break;
102 }
103 }
104 }
105
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700106 env->CallVoidMethod(processManager, onExitMethod, pid, status);
Elliott Hughes94367e02009-10-23 18:10:44 -0700107 if (env->ExceptionOccurred()) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800108 /*
109 * The callback threw, so break out of the loop and return,
110 * letting the exception percolate up.
111 */
112 break;
113 }
114 }
115}
116
crazybob410530d2009-06-18 17:19:04 -0500117/** Close all open fds > 2 (i.e. everything but stdin/out/err), != skipFd. */
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700118static void closeNonStandardFds(int skipFd1, int skipFd2) {
Elliott Hughes94367e02009-10-23 18:10:44 -0700119 // TODO: rather than close all these non-open files, we could look in /proc/self/fd.
Elliott Hughese22935d2010-08-12 17:27:27 -0700120 rlimit rlimit;
crazybob410530d2009-06-18 17:19:04 -0500121 getrlimit(RLIMIT_NOFILE, &rlimit);
Elliott Hughes94367e02009-10-23 18:10:44 -0700122 const int max_fd = rlimit.rlim_max;
123 for (int fd = 3; fd < max_fd; ++fd) {
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700124 if (fd != skipFd1 && fd != skipFd2) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800125 close(fd);
crazybob410530d2009-06-18 17:19:04 -0500126 }
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800127 }
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800128}
129
130#define PIPE_COUNT (4) // number of pipes used to communicate with child proc
131
132/** Closes all pipes in the given array. */
133static void closePipes(int pipes[], int skipFd) {
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700134 for (int i = 0; i < PIPE_COUNT * 2; i++) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800135 int fd = pipes[i];
136 if (fd == -1) {
137 return;
138 }
139 if (fd != skipFd) {
140 close(pipes[i]);
141 }
142 }
143}
144
145/** Executes a command in a child process. */
146static pid_t executeProcess(JNIEnv* env, char** commands, char** environment,
147 const char* workingDirectory, jobject inDescriptor,
Elliott Hughes18057272009-10-27 13:39:09 -0700148 jobject outDescriptor, jobject errDescriptor,
149 jboolean redirectErrorStream) {
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700150
151 // Keep track of the system properties fd so we don't close it.
152 int androidSystemPropertiesFd = -1;
153 char* fdString = getenv("ANDROID_PROPERTY_WORKSPACE");
154 if (fdString) {
155 androidSystemPropertiesFd = atoi(fdString);
156 }
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800157
158 // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.
159 int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700160 for (int i = 0; i < PIPE_COUNT; i++) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800161 if (pipe(pipes + i * 2) == -1) {
162 jniThrowIOException(env, errno);
163 closePipes(pipes, -1);
164 return -1;
165 }
166 }
167 int stdinIn = pipes[0];
168 int stdinOut = pipes[1];
169 int stdoutIn = pipes[2];
170 int stdoutOut = pipes[3];
171 int stderrIn = pipes[4];
172 int stderrOut = pipes[5];
173 int statusIn = pipes[6];
174 int statusOut = pipes[7];
175
176 pid_t childPid = fork();
177
178 // If fork() failed...
179 if (childPid == -1) {
180 jniThrowIOException(env, errno);
181 closePipes(pipes, -1);
182 return -1;
183 }
184
185 // If this is the child process...
186 if (childPid == 0) {
crazybob410530d2009-06-18 17:19:04 -0500187 /*
188 * Note: We cannot malloc() or free() after this point!
189 * A no-longer-running thread may be holding on to the heap lock, and
190 * an attempt to malloc() or free() would result in deadlock.
191 */
192
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800193 // Replace stdin, out, and err with pipes.
194 dup2(stdinIn, 0);
195 dup2(stdoutOut, 1);
Elliott Hughes18057272009-10-27 13:39:09 -0700196 if (redirectErrorStream) {
197 dup2(stdoutOut, 2);
198 } else {
199 dup2(stderrOut, 2);
200 }
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800201
202 // Close all but statusOut. This saves some work in the next step.
203 closePipes(pipes, statusOut);
204
205 // Make statusOut automatically close if execvp() succeeds.
206 fcntl(statusOut, F_SETFD, FD_CLOEXEC);
207
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700208 // Close remaining unwanted open fds.
209 closeNonStandardFds(statusOut, androidSystemPropertiesFd);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800210
211 // Switch to working directory.
212 if (workingDirectory != NULL) {
213 if (chdir(workingDirectory) == -1) {
214 goto execFailed;
215 }
216 }
217
218 // Set up environment.
219 if (environment != NULL) {
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700220 extern char** environ; // Standard, but not in any header file.
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800221 environ = environment;
222 }
223
224 // Execute process. By convention, the first argument in the arg array
225 // should be the command itself. In fact, I get segfaults when this
226 // isn't the case.
227 execvp(commands[0], commands);
228
229 // If we got here, execvp() failed or the working dir was invalid.
230 execFailed:
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700231 int error = errno;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800232 write(statusOut, &error, sizeof(int));
233 close(statusOut);
234 exit(error);
235 }
236
237 // This is the parent process.
238
239 // Close child's pipe ends.
240 close(stdinIn);
241 close(stdoutOut);
242 close(stderrOut);
243 close(statusOut);
244
245 // Check status pipe for an error code. If execvp() succeeds, the other
246 // end of the pipe should automatically close, in which case, we'll read
247 // nothing.
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700248 int result;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800249 int count = read(statusIn, &result, sizeof(int));
250 close(statusIn);
251 if (count > 0) {
252 jniThrowIOException(env, result);
253
254 close(stdoutIn);
255 close(stdinOut);
256 close(stderrIn);
257
258 return -1;
259 }
260
261 // Fill in file descriptor wrappers.
262 jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);
263 jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);
264 jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);
265
266 return childPid;
267}
268
269/** Converts a Java String[] to a 0-terminated char**. */
270static char** convertStrings(JNIEnv* env, jobjectArray javaArray) {
271 if (javaArray == NULL) {
272 return NULL;
273 }
274
Elliott Hughes94367e02009-10-23 18:10:44 -0700275 jsize length = env->GetArrayLength(javaArray);
Elliott Hughes583ce472010-07-22 10:23:42 -0700276 char** array = new char*[length + 1];
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800277 array[length] = 0;
Elliott Hughese9f12042011-05-11 11:48:46 -0700278 for (jsize i = 0; i < length; ++i) {
279 ScopedLocalRef<jstring> javaEntry(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, i)));
Elliott Hughes583ce472010-07-22 10:23:42 -0700280 // We need to pass these strings to const-unfriendly code.
Elliott Hughese9f12042011-05-11 11:48:46 -0700281 char* entry = const_cast<char*>(env->GetStringUTFChars(javaEntry.get(), NULL));
282 array[i] = entry;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800283 }
284
285 return array;
286}
287
288/** Frees a char** which was converted from a Java String[]. */
289static void freeStrings(JNIEnv* env, jobjectArray javaArray, char** array) {
290 if (javaArray == NULL) {
291 return;
292 }
293
Elliott Hughes94367e02009-10-23 18:10:44 -0700294 jsize length = env->GetArrayLength(javaArray);
Elliott Hughese9f12042011-05-11 11:48:46 -0700295 for (jsize i = 0; i < length; ++i) {
296 ScopedLocalRef<jstring> javaEntry(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, i)));
297 env->ReleaseStringUTFChars(javaEntry.get(), array[i]);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800298 }
299
Elliott Hughes583ce472010-07-22 10:23:42 -0700300 delete[] array;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800301}
302
303/**
304 * Converts Java String[] to char** and delegates to executeProcess().
305 */
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700306static pid_t ProcessManager_exec(JNIEnv* env, jclass, jobjectArray javaCommands,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800307 jobjectArray javaEnvironment, jstring javaWorkingDirectory,
Elliott Hughes18057272009-10-27 13:39:09 -0700308 jobject inDescriptor, jobject outDescriptor, jobject errDescriptor,
309 jboolean redirectErrorStream) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800310
311 // Copy commands into char*[].
312 char** commands = convertStrings(env, javaCommands);
313
314 // Extract working directory string.
315 const char* workingDirectory = NULL;
316 if (javaWorkingDirectory != NULL) {
Elliott Hughes94367e02009-10-23 18:10:44 -0700317 workingDirectory = env->GetStringUTFChars(javaWorkingDirectory, NULL);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800318 }
319
320 // Convert environment array.
321 char** environment = convertStrings(env, javaEnvironment);
322
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700323 pid_t result = executeProcess(env, commands, environment, workingDirectory,
Elliott Hughes18057272009-10-27 13:39:09 -0700324 inDescriptor, outDescriptor, errDescriptor, redirectErrorStream);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800325
326 // Temporarily clear exception so we can clean up.
Elliott Hughes94367e02009-10-23 18:10:44 -0700327 jthrowable exception = env->ExceptionOccurred();
328 env->ExceptionClear();
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800329
330 freeStrings(env, javaEnvironment, environment);
331
332 // Clean up working directory string.
333 if (javaWorkingDirectory != NULL) {
Elliott Hughes94367e02009-10-23 18:10:44 -0700334 env->ReleaseStringUTFChars(javaWorkingDirectory, workingDirectory);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800335 }
336
337 freeStrings(env, javaCommands, commands);
338
339 // Re-throw exception if present.
340 if (exception != NULL) {
Elliott Hughes94367e02009-10-23 18:10:44 -0700341 if (env->Throw(exception) < 0) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800342 LOGE("Error rethrowing exception!");
343 }
344 }
345
346 return result;
347}
348
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800349static JNINativeMethod methods[] = {
Elliott Hughesa5fb7062011-04-28 15:36:47 -0700350 NATIVE_METHOD(ProcessManager, watchChildren, "(Ljava/lang/ProcessManager;)V"),
Elliott Hughese22935d2010-08-12 17:27:27 -0700351 NATIVE_METHOD(ProcessManager, exec, "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Z)I"),
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800352};
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800353int register_java_lang_ProcessManager(JNIEnv* env) {
Elliott Hughesc08f9fb2010-04-16 17:44:12 -0700354 return jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800355}