Home | History | Annotate | Download | only in native
      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. */
     34 static 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. */
     49 static 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. */
     62 static 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**. */
    186 static 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[]. */
    205 static 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  */
    222 static 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             LOGE("Error rethrowing exception!");
    259         }
    260     }
    261 
    262     return result;
    263 }
    264 
    265 static 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 };
    268 int register_java_lang_ProcessManager(JNIEnv* env) {
    269     return jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods));
    270 }
    271