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 "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