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 #define LOG_TAG "ProcessManager"
18
19 #include <sys/resource.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include "jni.h"
28 #include "JNIHelp.h"
29 #include "JniConstants.h"
30 #include "ScopedLocalRef.h"
31 #include "cutils/log.h"
32
33 /** Close all open fds > 2 (i.e. everything but stdin/out/err), != skipFd. */
closeNonStandardFds(int skipFd1,int skipFd2)34 static void closeNonStandardFds(int skipFd1, int skipFd2) {
35 // TODO: rather than close all these non-open files, we could look in /proc/self/fd.
36 rlimit rlimit;
37 getrlimit(RLIMIT_NOFILE, &rlimit);
38 const int max_fd = rlimit.rlim_max;
39 for (int fd = 3; fd < max_fd; ++fd) {
40 if (fd != skipFd1 && fd != skipFd2) {
41 close(fd);
42 }
43 }
44 }
45
46 #define PIPE_COUNT (4) // number of pipes used to communicate with child proc
47
48 /** Closes all pipes in the given array. */
closePipes(int pipes[],int skipFd)49 static void closePipes(int pipes[], int skipFd) {
50 for (int i = 0; i < PIPE_COUNT * 2; i++) {
51 int fd = pipes[i];
52 if (fd == -1) {
53 return;
54 }
55 if (fd != skipFd) {
56 close(pipes[i]);
57 }
58 }
59 }
60
61 /** Executes a command in a child process. */
executeProcess(JNIEnv * env,char ** commands,char ** environment,const char * workingDirectory,jobject inDescriptor,jobject outDescriptor,jobject errDescriptor,jboolean redirectErrorStream)62 static pid_t executeProcess(JNIEnv* env, char** commands, char** environment,
63 const char* workingDirectory, jobject inDescriptor,
64 jobject outDescriptor, jobject errDescriptor,
65 jboolean redirectErrorStream) {
66
67 // Keep track of the system properties fd so we don't close it.
68 int androidSystemPropertiesFd = -1;
69 char* fdString = getenv("ANDROID_PROPERTY_WORKSPACE");
70 if (fdString) {
71 androidSystemPropertiesFd = atoi(fdString);
72 }
73
74 // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.
75 int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
76 for (int i = 0; i < PIPE_COUNT; i++) {
77 if (pipe(pipes + i * 2) == -1) {
78 jniThrowIOException(env, errno);
79 closePipes(pipes, -1);
80 return -1;
81 }
82 }
83 int stdinIn = pipes[0];
84 int stdinOut = pipes[1];
85 int stdoutIn = pipes[2];
86 int stdoutOut = pipes[3];
87 int stderrIn = pipes[4];
88 int stderrOut = pipes[5];
89 int statusIn = pipes[6];
90 int statusOut = pipes[7];
91
92 pid_t childPid = fork();
93
94 // If fork() failed...
95 if (childPid == -1) {
96 jniThrowIOException(env, errno);
97 closePipes(pipes, -1);
98 return -1;
99 }
100
101 // If this is the child process...
102 if (childPid == 0) {
103 /*
104 * Note: We cannot malloc() or free() after this point!
105 * A no-longer-running thread may be holding on to the heap lock, and
106 * an attempt to malloc() or free() would result in deadlock.
107 */
108
109 // Replace stdin, out, and err with pipes.
110 dup2(stdinIn, 0);
111 dup2(stdoutOut, 1);
112 if (redirectErrorStream) {
113 dup2(stdoutOut, 2);
114 } else {
115 dup2(stderrOut, 2);
116 }
117
118 // Close all but statusOut. This saves some work in the next step.
119 closePipes(pipes, statusOut);
120
121 // Make statusOut automatically close if execvp() succeeds.
122 fcntl(statusOut, F_SETFD, FD_CLOEXEC);
123
124 // Close remaining unwanted open fds.
125 closeNonStandardFds(statusOut, androidSystemPropertiesFd);
126
127 // Switch to working directory.
128 if (workingDirectory != NULL) {
129 if (chdir(workingDirectory) == -1) {
130 goto execFailed;
131 }
132 }
133
134 // Set up environment.
135 if (environment != NULL) {
136 extern char** environ; // Standard, but not in any header file.
137 environ = environment;
138 }
139
140 // Execute process. By convention, the first argument in the arg array
141 // should be the command itself. In fact, I get segfaults when this
142 // isn't the case.
143 execvp(commands[0], commands);
144
145 // If we got here, execvp() failed or the working dir was invalid.
146 execFailed:
147 int error = errno;
148 write(statusOut, &error, sizeof(int));
149 close(statusOut);
150 exit(error);
151 }
152
153 // This is the parent process.
154
155 // Close child's pipe ends.
156 close(stdinIn);
157 close(stdoutOut);
158 close(stderrOut);
159 close(statusOut);
160
161 // Check status pipe for an error code. If execvp() succeeds, the other
162 // end of the pipe should automatically close, in which case, we'll read
163 // nothing.
164 int result;
165 int count = read(statusIn, &result, sizeof(int));
166 close(statusIn);
167 if (count > 0) {
168 jniThrowIOException(env, result);
169
170 close(stdoutIn);
171 close(stdinOut);
172 close(stderrIn);
173
174 return -1;
175 }
176
177 // Fill in file descriptor wrappers.
178 jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);
179 jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);
180 jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);
181
182 return childPid;
183 }
184
185 /** Converts a Java String[] to a 0-terminated char**. */
convertStrings(JNIEnv * env,jobjectArray javaArray)186 static char** convertStrings(JNIEnv* env, jobjectArray javaArray) {
187 if (javaArray == NULL) {
188 return NULL;
189 }
190
191 jsize length = env->GetArrayLength(javaArray);
192 char** array = new char*[length + 1];
193 array[length] = 0;
194 for (jsize i = 0; i < length; ++i) {
195 ScopedLocalRef<jstring> javaEntry(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, i)));
196 // We need to pass these strings to const-unfriendly code.
197 char* entry = const_cast<char*>(env->GetStringUTFChars(javaEntry.get(), NULL));
198 array[i] = entry;
199 }
200
201 return array;
202 }
203
204 /** Frees a char** which was converted from a Java String[]. */
freeStrings(JNIEnv * env,jobjectArray javaArray,char ** array)205 static void freeStrings(JNIEnv* env, jobjectArray javaArray, char** array) {
206 if (javaArray == NULL) {
207 return;
208 }
209
210 jsize length = env->GetArrayLength(javaArray);
211 for (jsize i = 0; i < length; ++i) {
212 ScopedLocalRef<jstring> javaEntry(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, i)));
213 env->ReleaseStringUTFChars(javaEntry.get(), array[i]);
214 }
215
216 delete[] array;
217 }
218
219 /**
220 * Converts Java String[] to char** and delegates to executeProcess().
221 */
ProcessManager_exec(JNIEnv * env,jclass,jobjectArray javaCommands,jobjectArray javaEnvironment,jstring javaWorkingDirectory,jobject inDescriptor,jobject outDescriptor,jobject errDescriptor,jboolean redirectErrorStream)222 static pid_t ProcessManager_exec(JNIEnv* env, jclass, jobjectArray javaCommands,
223 jobjectArray javaEnvironment, jstring javaWorkingDirectory,
224 jobject inDescriptor, jobject outDescriptor, jobject errDescriptor,
225 jboolean redirectErrorStream) {
226
227 // Copy commands into char*[].
228 char** commands = convertStrings(env, javaCommands);
229
230 // Extract working directory string.
231 const char* workingDirectory = NULL;
232 if (javaWorkingDirectory != NULL) {
233 workingDirectory = env->GetStringUTFChars(javaWorkingDirectory, NULL);
234 }
235
236 // Convert environment array.
237 char** environment = convertStrings(env, javaEnvironment);
238
239 pid_t result = executeProcess(env, commands, environment, workingDirectory,
240 inDescriptor, outDescriptor, errDescriptor, redirectErrorStream);
241
242 // Temporarily clear exception so we can clean up.
243 jthrowable exception = env->ExceptionOccurred();
244 env->ExceptionClear();
245
246 freeStrings(env, javaEnvironment, environment);
247
248 // Clean up working directory string.
249 if (javaWorkingDirectory != NULL) {
250 env->ReleaseStringUTFChars(javaWorkingDirectory, workingDirectory);
251 }
252
253 freeStrings(env, javaCommands, commands);
254
255 // Re-throw exception if present.
256 if (exception != NULL) {
257 if (env->Throw(exception) < 0) {
258 ALOGE("Error rethrowing exception!");
259 }
260 }
261
262 return result;
263 }
264
265 static JNINativeMethod methods[] = {
266 NATIVE_METHOD(ProcessManager, exec, "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Z)I"),
267 };
register_java_lang_ProcessManager(JNIEnv * env)268 void register_java_lang_ProcessManager(JNIEnv* env) {
269 jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods));
270 }
271