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