Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2008 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 /*
     18  * Copyright (C) 2007 The Android Open Source Project
     19  *
     20  * Licensed under the Apache License, Version 2.0 (the "License");
     21  * you may not use this file except in compliance with the License.
     22  * You may obtain a copy of the License at
     23  *
     24  *      http://www.apache.org/licenses/LICENSE-2.0
     25  *
     26  * Unless required by applicable law or agreed to in writing, software
     27  * distributed under the License is distributed on an "AS IS" BASIS,
     28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     29  * See the License for the specific language governing permissions and
     30  * limitations under the License.
     31  */
     32 
     33 #define LOG_TAG "Exec"
     34 
     35 #include "jni.h"
     36 #include "utils/Log.h"
     37 #include "utils/misc.h"
     38 #include "android_runtime/AndroidRuntime.h"
     39 
     40 #include <sys/types.h>
     41 #include <sys/ioctl.h>
     42 #include <sys/wait.h>
     43 #include <errno.h>
     44 #include <fcntl.h>
     45 #include <stdlib.h>
     46 #include <unistd.h>
     47 #include <termios.h>
     48 
     49 static jclass class_fileDescriptor;
     50 static jfieldID field_fileDescriptor_descriptor;
     51 static jmethodID method_fileDescriptor_init;
     52 
     53 
     54 class String8 {
     55 public:
     56     String8() {
     57         mString = 0;
     58     }
     59 
     60     ~String8() {
     61         if (mString) {
     62             free(mString);
     63         }
     64     }
     65 
     66     void set(const char16_t* o, size_t numChars) {
     67         mString = (char*) malloc(numChars + 1);
     68         for (size_t i = 0; i < numChars; i++) {
     69             mString[i] = (char) o[i];
     70         }
     71         mString[numChars] = '\0';
     72     }
     73 
     74     const char* string() {
     75         return mString;
     76     }
     77 private:
     78     char* mString;
     79 };
     80 
     81 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1,
     82     int* pProcessId)
     83 {
     84     char *devname;
     85     int ptm;
     86     pid_t pid;
     87 
     88     ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
     89     if(ptm < 0){
     90         LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
     91         return -1;
     92     }
     93     fcntl(ptm, F_SETFD, FD_CLOEXEC);
     94 
     95     if(grantpt(ptm) || unlockpt(ptm) ||
     96        ((devname = (char*) ptsname(ptm)) == 0)){
     97         LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
     98         return -1;
     99     }
    100 
    101     pid = fork();
    102     if(pid < 0) {
    103         LOGE("- fork failed: %s -\n", strerror(errno));
    104         return -1;
    105     }
    106 
    107     if(pid == 0){
    108         close(ptm);
    109 
    110         int pts;
    111 
    112         setsid();
    113 
    114         pts = open(devname, O_RDWR);
    115         if(pts < 0) exit(-1);
    116 
    117         dup2(pts, 0);
    118         dup2(pts, 1);
    119         dup2(pts, 2);
    120 
    121         execl(cmd, cmd, arg0, arg1, NULL);
    122         exit(-1);
    123     } else {
    124         *pProcessId = (int) pid;
    125         return ptm;
    126     }
    127 }
    128 
    129 
    130 static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
    131     jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray)
    132 {
    133     const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
    134     String8 cmd_8;
    135     if (str) {
    136         cmd_8.set(str, env->GetStringLength(cmd));
    137         env->ReleaseStringCritical(cmd, str);
    138     }
    139 
    140     str = arg0 ? env->GetStringCritical(arg0, 0) : 0;
    141     const char* arg0Str = 0;
    142     String8 arg0_8;
    143     if (str) {
    144         arg0_8.set(str, env->GetStringLength(arg0));
    145         env->ReleaseStringCritical(arg0, str);
    146         arg0Str = arg0_8.string();
    147     }
    148 
    149     str = arg1 ? env->GetStringCritical(arg1, 0) : 0;
    150     const char* arg1Str = 0;
    151     String8 arg1_8;
    152     if (str) {
    153         arg1_8.set(str, env->GetStringLength(arg1));
    154         env->ReleaseStringCritical(arg1, str);
    155         arg1Str = arg1_8.string();
    156     }
    157 
    158     int procId;
    159     int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId);
    160 
    161     if (processIdArray) {
    162         int procIdLen = env->GetArrayLength(processIdArray);
    163         if (procIdLen > 0) {
    164             jboolean isCopy;
    165 
    166             int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
    167             if (pProcId) {
    168                 *pProcId = procId;
    169                 env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
    170             }
    171         }
    172     }
    173 
    174     jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init);
    175 
    176     if (!result) {
    177         LOGE("Couldn't create a FileDescriptor.");
    178     }
    179     else {
    180         env->SetIntField(result, field_fileDescriptor_descriptor, ptm);
    181     }
    182 
    183     return result;
    184 }
    185 
    186 
    187 static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz,
    188     jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel)
    189 {
    190     int fd;
    191     struct winsize sz;
    192 
    193     fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
    194 
    195     if (env->ExceptionOccurred() != NULL) {
    196         return;
    197     }
    198 
    199     sz.ws_row = row;
    200     sz.ws_col = col;
    201     sz.ws_xpixel = xpixel;
    202     sz.ws_ypixel = ypixel;
    203 
    204     ioctl(fd, TIOCSWINSZ, &sz);
    205 }
    206 
    207 static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz,
    208     jint procId) {
    209     int status;
    210     waitpid(procId, &status, 0);
    211     int result = 0;
    212     if (WIFEXITED(status)) {
    213         result = WEXITSTATUS(status);
    214     }
    215     return result;
    216 }
    217 
    218 static void android_os_Exec_close(JNIEnv *env, jobject clazz, jobject fileDescriptor)
    219 {
    220     int fd;
    221     struct winsize sz;
    222 
    223     fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
    224 
    225     if (env->ExceptionOccurred() != NULL) {
    226         return;
    227     }
    228 
    229     close(fd);
    230 }
    231 
    232 
    233 static int register_FileDescriptor(JNIEnv *env)
    234 {
    235     class_fileDescriptor = env->FindClass("java/io/FileDescriptor");
    236 
    237     if (class_fileDescriptor == NULL) {
    238         LOGE("Can't find java/io/FileDescriptor");
    239         return -1;
    240     }
    241 
    242     field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I");
    243 
    244     if (field_fileDescriptor_descriptor == NULL) {
    245         LOGE("Can't find FileDescriptor.descriptor");
    246         return -1;
    247     }
    248 
    249     method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V");
    250     if (method_fileDescriptor_init == NULL) {
    251         LOGE("Can't find FileDescriptor.init");
    252         return -1;
    253      }
    254      return 0;
    255 }
    256 
    257 
    258 static const char *classPathName = "com/android/term/Exec";
    259 
    260 static JNINativeMethod method_table[] = {
    261     { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;",
    262         (void*) android_os_Exec_createSubProcess },
    263     { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V",
    264         (void*) android_os_Exec_setPtyWindowSize},
    265     { "waitFor", "(I)I",
    266         (void*) android_os_Exec_waitFor},
    267     { "close", "(Ljava/io/FileDescriptor;)V",
    268         (void*) android_os_Exec_close}
    269 };
    270 
    271 /*
    272  * Register several native methods for one class.
    273  */
    274 static int registerNativeMethods(JNIEnv* env, const char* className,
    275     JNINativeMethod* gMethods, int numMethods)
    276 {
    277     jclass clazz;
    278 
    279     clazz = env->FindClass(className);
    280     if (clazz == NULL) {
    281         LOGE("Native registration unable to find class '%s'", className);
    282         return JNI_FALSE;
    283     }
    284     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
    285         LOGE("RegisterNatives failed for '%s'", className);
    286         return JNI_FALSE;
    287     }
    288 
    289     return JNI_TRUE;
    290 }
    291 
    292 /*
    293  * Register native methods for all classes we know about.
    294  *
    295  * returns JNI_TRUE on success.
    296  */
    297 static int registerNatives(JNIEnv* env)
    298 {
    299   if (!registerNativeMethods(env, classPathName, method_table,
    300                  sizeof(method_table) / sizeof(method_table[0]))) {
    301     return JNI_FALSE;
    302   }
    303 
    304   return JNI_TRUE;
    305 }
    306 
    307 
    308 // ----------------------------------------------------------------------------
    309 
    310 /*
    311  * This is called by the VM when the shared library is first loaded.
    312  */
    313 
    314 typedef union {
    315     JNIEnv* env;
    316     void* venv;
    317 } UnionJNIEnvToVoid;
    318 
    319 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    320     UnionJNIEnvToVoid uenv;
    321     uenv.venv = NULL;
    322     jint result = -1;
    323     JNIEnv* env = NULL;
    324 
    325     LOGI("JNI_OnLoad");
    326 
    327     if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
    328         LOGE("ERROR: GetEnv failed");
    329         goto bail;
    330     }
    331     env = uenv.env;
    332 
    333     if ((result = register_FileDescriptor(env)) < 0) {
    334         LOGE("ERROR: registerFileDescriptor failed");
    335         goto bail;
    336     }
    337 
    338     if (registerNatives(env) != JNI_TRUE) {
    339         LOGE("ERROR: registerNatives failed");
    340         goto bail;
    341     }
    342 
    343     result = JNI_VERSION_1_4;
    344 
    345 bail:
    346     return result;
    347 }
    348