• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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