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 <dirent.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/resource.h> 25 #include <sys/types.h> 26 #include <sys/wait.h> 27 #include <unistd.h> 28 29 #include "cutils/log.h" 30 #include "jni.h" 31 #include "ExecStrings.h" 32 #include "JNIHelp.h" 33 #include "JniConstants.h" 34 #include "Portability.h" 35 #include "ScopedLocalRef.h" 36 #include "toStringArray.h" 37 38 static void CloseNonStandardFds(int status_pipe_fd) { 39 // On Cygwin, Linux, and Solaris, the best way to close iterates over "/proc/self/fd/". 40 const char* fd_path = "/proc/self/fd"; 41 #ifdef __APPLE__ 42 // On Mac OS, there's "/dev/fd/" which Linux seems to link to "/proc/self/fd/", 43 // but which on Solaris appears to be something quite different. 44 fd_path = "/dev/fd"; 45 #endif 46 47 // Keep track of the system properties fd so we don't close it. 48 int properties_fd = -1; 49 char* properties_fd_string = getenv("ANDROID_PROPERTY_WORKSPACE"); 50 if (properties_fd_string != NULL) { 51 properties_fd = atoi(properties_fd_string); 52 } 53 54 DIR* d = opendir(fd_path); 55 int dir_fd = dirfd(d); 56 dirent* e; 57 while ((e = readdir(d)) != NULL) { 58 char* end; 59 int fd = strtol(e->d_name, &end, 10); 60 if (!*end) { 61 if (fd > STDERR_FILENO && fd != dir_fd && fd != status_pipe_fd && fd != properties_fd) { 62 close(fd); 63 } 64 } 65 } 66 closedir(d); 67 } 68 69 #define PIPE_COUNT 4 // Number of pipes used to communicate with child. 70 71 static void ClosePipes(int pipes[], int skip_fd) { 72 for (int i = 0; i < PIPE_COUNT * 2; i++) { 73 int fd = pipes[i]; 74 if (fd != -1 && fd != skip_fd) { 75 close(pipes[i]); 76 } 77 } 78 } 79 80 static void AbortChild(int status_pipe_fd) { 81 int error = errno; 82 TEMP_FAILURE_RETRY(write(status_pipe_fd, &error, sizeof(int))); 83 close(status_pipe_fd); 84 _exit(127); 85 } 86 87 /** Executes a command in a child process. */ 88 static pid_t ExecuteProcess(JNIEnv* env, char** commands, char** environment, 89 const char* workingDirectory, jobject inDescriptor, 90 jobject outDescriptor, jobject errDescriptor, 91 jboolean redirectErrorStream) { 92 93 // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe. 94 int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 95 for (int i = 0; i < PIPE_COUNT; i++) { 96 if (pipe(pipes + i * 2) == -1) { 97 jniThrowIOException(env, errno); 98 ClosePipes(pipes, -1); 99 return -1; 100 } 101 } 102 int stdinIn = pipes[0]; 103 int stdinOut = pipes[1]; 104 int stdoutIn = pipes[2]; 105 int stdoutOut = pipes[3]; 106 int stderrIn = pipes[4]; 107 int stderrOut = pipes[5]; 108 int statusIn = pipes[6]; 109 int statusOut = pipes[7]; 110 111 pid_t childPid = fork(); 112 113 // If fork() failed... 114 if (childPid == -1) { 115 jniThrowIOException(env, errno); 116 ClosePipes(pipes, -1); 117 return -1; 118 } 119 120 // If this is the child process... 121 if (childPid == 0) { 122 // Note: We cannot malloc(3) or free(3) after this point! 123 // A thread in the parent that no longer exists in the child may have held the heap lock 124 // when we forked, so an attempt to malloc(3) or free(3) would result in deadlock. 125 126 // Replace stdin, out, and err with pipes. 127 dup2(stdinIn, 0); 128 dup2(stdoutOut, 1); 129 if (redirectErrorStream) { 130 dup2(stdoutOut, 2); 131 } else { 132 dup2(stderrOut, 2); 133 } 134 135 // Close all but statusOut. This saves some work in the next step. 136 ClosePipes(pipes, statusOut); 137 138 // Make statusOut automatically close if execvp() succeeds. 139 fcntl(statusOut, F_SETFD, FD_CLOEXEC); 140 141 // Close remaining unwanted open fds. 142 CloseNonStandardFds(statusOut); 143 144 // Switch to working directory. 145 if (workingDirectory != NULL) { 146 if (chdir(workingDirectory) == -1) { 147 AbortChild(statusOut); 148 } 149 } 150 151 // Set up environment. 152 if (environment != NULL) { 153 extern char** environ; // Standard, but not in any header file. 154 environ = environment; 155 } 156 157 // Execute process. By convention, the first argument in the arg array 158 // should be the command itself. 159 execvp(commands[0], commands); 160 AbortChild(statusOut); 161 } 162 163 // This is the parent process. 164 165 // Close child's pipe ends. 166 close(stdinIn); 167 close(stdoutOut); 168 close(stderrOut); 169 close(statusOut); 170 171 // Check status pipe for an error code. If execvp(2) succeeds, the other 172 // end of the pipe should automatically close, in which case, we'll read 173 // nothing. 174 int child_errno; 175 ssize_t count = TEMP_FAILURE_RETRY(read(statusIn, &child_errno, sizeof(int))); 176 close(statusIn); 177 if (count > 0) { 178 // chdir(2) or execvp(2) in the child failed. 179 // TODO: track which so we can be more specific in the detail message. 180 jniThrowIOException(env, child_errno); 181 182 close(stdoutIn); 183 close(stdinOut); 184 close(stderrIn); 185 186 // Reap our zombie child right away. 187 int status; 188 int rc = TEMP_FAILURE_RETRY(waitpid(childPid, &status, 0)); 189 if (rc == -1) { 190 ALOGW("waitpid on failed exec failed: %s", strerror(errno)); 191 } 192 193 return -1; 194 } 195 196 // Fill in file descriptor wrappers. 197 jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn); 198 jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut); 199 jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn); 200 201 return childPid; 202 } 203 204 /** 205 * Converts Java String[] to char** and delegates to ExecuteProcess(). 206 */ 207 static pid_t ProcessManager_exec(JNIEnv* env, jclass, jobjectArray javaCommands, 208 jobjectArray javaEnvironment, jstring javaWorkingDirectory, 209 jobject inDescriptor, jobject outDescriptor, jobject errDescriptor, 210 jboolean redirectErrorStream) { 211 212 ExecStrings commands(env, javaCommands); 213 ExecStrings environment(env, javaEnvironment); 214 215 // Extract working directory string. 216 const char* workingDirectory = NULL; 217 if (javaWorkingDirectory != NULL) { 218 workingDirectory = env->GetStringUTFChars(javaWorkingDirectory, NULL); 219 } 220 221 pid_t result = ExecuteProcess(env, commands.get(), environment.get(), workingDirectory, 222 inDescriptor, outDescriptor, errDescriptor, redirectErrorStream); 223 224 // Clean up working directory string. 225 if (javaWorkingDirectory != NULL) { 226 env->ReleaseStringUTFChars(javaWorkingDirectory, workingDirectory); 227 } 228 229 return result; 230 } 231 232 static JNINativeMethod gMethods[] = { 233 NATIVE_METHOD(ProcessManager, exec, "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Z)I"), 234 }; 235 void register_java_lang_ProcessManager(JNIEnv* env) { 236 jniRegisterNativeMethods(env, "java/lang/ProcessManager", gMethods, NELEM(gMethods)); 237 } 238