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