1 // 2 // Copyright (C) 2016 Google, Inc. 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 #include "com_googlecode_android_scripting_Exec.h" 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <stdlib.h> 22 #include <sys/ioctl.h> 23 #include <sys/types.h> 24 #include <sys/wait.h> 25 #include <termios.h> 26 #include <unistd.h> 27 #include <stdio.h> 28 #include <string.h> 29 30 #include "android/log.h" 31 32 #define LOG_TAG "Exec" 33 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 34 35 int CreateSubprocess(const char* cmd, char* args[], char* vars[], char *wkdir, pid_t* pid) { 36 char* devname; 37 int ptm = open("/dev/ptmx", O_RDWR); 38 if(ptm < 0){ 39 LOGE("Cannot open /dev/ptmx: %s\n", strerror(errno)); 40 return -1; 41 } 42 fcntl(ptm, F_SETFD, FD_CLOEXEC); 43 44 if (grantpt(ptm) || unlockpt(ptm) || 45 ((devname = (char*) ptsname(ptm)) == 0)) { 46 LOGE("Trouble with /dev/ptmx: %s\n", strerror(errno)); 47 return -1; 48 } 49 50 *pid = fork(); 51 if(*pid < 0) { 52 LOGE("Fork failed: %s\n", strerror(errno)); 53 return -1; 54 } 55 56 if(*pid == 0){ 57 int pts; 58 setsid(); 59 pts = open(devname, O_RDWR); 60 if(pts < 0) { 61 exit(-1); 62 } 63 dup2(pts, 0); 64 dup2(pts, 1); 65 dup2(pts, 2); 66 close(ptm); 67 if (wkdir) chdir(wkdir); 68 execve(cmd, args, vars); 69 exit(-1); 70 } else { 71 return ptm; 72 } 73 } 74 75 void JNU_ThrowByName(JNIEnv* env, const char* name, const char* msg) { 76 jclass clazz = env->FindClass(name); 77 if (clazz != NULL) { 78 env->ThrowNew(clazz, msg); 79 } 80 env->DeleteLocalRef(clazz); 81 } 82 83 char* JNU_GetStringNativeChars(JNIEnv* env, jstring jstr) { 84 if (jstr == NULL) { 85 return NULL; 86 } 87 jbyteArray bytes = 0; 88 jthrowable exc; 89 char* result = 0; 90 if (env->EnsureLocalCapacity(2) < 0) { 91 return 0; /* out of memory error */ 92 } 93 jclass Class_java_lang_String = env->FindClass("java/lang/String"); 94 jmethodID MID_String_getBytes = env->GetMethodID( 95 Class_java_lang_String, "getBytes", "()[B"); 96 bytes = (jbyteArray) env->CallObjectMethod(jstr, MID_String_getBytes); 97 exc = env->ExceptionOccurred(); 98 if (!exc) { 99 jint len = env->GetArrayLength(bytes); 100 result = (char*) malloc(len + 1); 101 if (result == 0) { 102 JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0); 103 env->DeleteLocalRef(bytes); 104 return 0; 105 } 106 env->GetByteArrayRegion(bytes, 0, len, (jbyte*) result); 107 result[len] = 0; /* NULL-terminate */ 108 } else { 109 env->DeleteLocalRef(exc); 110 } 111 env->DeleteLocalRef(bytes); 112 return result; 113 } 114 115 int JNU_GetFdFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { 116 jclass Class_java_io_FileDescriptor = env->FindClass("java/io/FileDescriptor"); 117 jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I"); 118 return env->GetIntField(fileDescriptor, descriptor); 119 } 120 121 JNIEXPORT jobject JNICALL Java_com_googlecode_android_1scripting_Exec_createSubprocess( 122 JNIEnv* env, jclass clazz, jstring cmd, jobjectArray argArray, jobjectArray varArray, 123 jstring workingDirectory, 124 jintArray processIdArray) { 125 char* cmd_native = JNU_GetStringNativeChars(env, cmd); 126 char* wkdir_native = JNU_GetStringNativeChars(env, workingDirectory); 127 pid_t pid; 128 jsize len = 0; 129 if (argArray) { 130 len = env->GetArrayLength(argArray); 131 } 132 char* args[len + 2]; 133 args[0] = cmd_native; 134 for (int i = 0; i < len; i++) { 135 jstring arg = (jstring) env->GetObjectArrayElement(argArray, i); 136 char* arg_native = JNU_GetStringNativeChars(env, arg); 137 args[i + 1] = arg_native; 138 } 139 args[len + 1] = NULL; 140 141 len = 0; 142 if (varArray) { 143 len = env->GetArrayLength(varArray); 144 } 145 char* vars[len + 1]; 146 for (int i = 0; i < len; i++) { 147 jstring var = (jstring) env->GetObjectArrayElement(varArray, i); 148 char* var_native = JNU_GetStringNativeChars(env, var); 149 vars[i] = var_native; 150 } 151 vars[len] = NULL; 152 153 int ptm = CreateSubprocess(cmd_native, args, vars, wkdir_native, &pid); 154 if (processIdArray) { 155 if (env->GetArrayLength(processIdArray) > 0) { 156 jboolean isCopy; 157 int* proccessId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); 158 if (proccessId) { 159 *proccessId = (int) pid; 160 env->ReleasePrimitiveArrayCritical(processIdArray, proccessId, 0); 161 } 162 } 163 } 164 165 jclass Class_java_io_FileDescriptor = 166 env->FindClass("java/io/FileDescriptor"); 167 jmethodID init = env->GetMethodID(Class_java_io_FileDescriptor, "<init>", "()V"); 168 jobject result = env->NewObject(Class_java_io_FileDescriptor, init); 169 170 if (!result) { 171 LOGE("Couldn't create a FileDescriptor."); 172 } else { 173 jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I"); 174 env->SetIntField(result, descriptor, ptm); 175 } 176 return result; 177 } 178 179 JNIEXPORT void JNICALL Java_com_googlecode_android_1scripting_Exec_setPtyWindowSize( 180 JNIEnv* env, jclass clazz, jobject fileDescriptor, jint row, jint col, jint xpixel, 181 jint ypixel) { 182 struct winsize sz; 183 int fd = JNU_GetFdFromFileDescriptor(env, fileDescriptor); 184 if (env->ExceptionOccurred() != NULL) { 185 return; 186 } 187 sz.ws_row = row; 188 sz.ws_col = col; 189 sz.ws_xpixel = xpixel; 190 sz.ws_ypixel = ypixel; 191 ioctl(fd, TIOCSWINSZ, &sz); 192 } 193 194 JNIEXPORT jint JNICALL Java_com_googlecode_android_1scripting_Exec_waitFor(JNIEnv* env, jclass clazz, jint procId) { 195 int status; 196 waitpid(procId, &status, 0); 197 int result = 0; 198 if (WIFEXITED(status)) { 199 result = WEXITSTATUS(status); 200 } 201 return result; 202 } 203