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. */ 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. */ 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. */ 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 */ 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 }; 236 void register_java_lang_ProcessManager(JNIEnv* env) { 237 jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods)); 238 } 239