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