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. */ 34static 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. */ 49static 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. */ 62static 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**. */ 186static 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[]. */ 205static 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 */ 222static 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 265static 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}; 268void register_java_lang_ProcessManager(JNIEnv* env) { 269 jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods)); 270} 271