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