static int create_subproc_thread(const char *name) { stinfo *sti; adb_thread_t t; int ret_fd; pid_t pid; if (name) { ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid); } else { ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid); } D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); sti = malloc(sizeof(stinfo)); if (sti == 0) fatal("cannot allocate stinfo"); sti->func = subproc_waiter_service; sti->cookie = (void *) pid; sti->fd = ret_fd; if (adb_thread_create(&t, service_bootstrap_func, sti)) { free(sti); adb_close(ret_fd); printf("cannot create service thread\n"); return -1; } D("service thread started, fd=%d pid=%d\n", ret_fd, pid); return ret_fd; }
static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, jintArray processIdArray) { int procId; int ptm = create_subprocess(&procId); if (processIdArray) { int procIdLen = env->GetArrayLength(processIdArray); if (procIdLen > 0) { jboolean isCopy; int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); if (pProcId) { *pProcId = procId; env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); } } } jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init); if (!result) { LOGE("Couldn't create a FileDescriptor."); } else { env->SetIntField(result, field_fileDescriptor_descriptor, ptm); } return result; }
static int create_subproc_thread(const char *name) { adb_thread_t t; int ret_fd; pid_t pid; long mem_free = 0; read_meminfo(&mem_free); D("read_meminfo() mem_free=%d\n", mem_free); XLOGV("read_meminfo() mem_free=%d\n", mem_free); if(name) { ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid); } else { ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid); } D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); XLOGV("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); if ( sti == 0 ) { sti = malloc(sizeof(stinfo)); if(sti == 0) fatal("cannot allocate stinfo"); sti->func = subproc_waiter_service; sti->cookie = (void*)pid; sti->fd = ret_fd; int nRet = adb_thread_create( &t, service_bootstrap_func, sti); if(nRet) { D("adb_thread_create() nRet=%d errno=%d\n", nRet, errno); XLOGW("adb_thread_create() nRet=%d errno=%d\n", nRet, errno); free(sti); sti = 0; adb_close(ret_fd); printf("cannot create service thread\n"); return -1; } } D("service thread started, fd=%d pid=%d\n",ret_fd, pid); return ret_fd; }
static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray) { const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; String8 cmd_8; if (str) { cmd_8.set(str, env->GetStringLength(cmd)); env->ReleaseStringCritical(cmd, str); } str = arg0 ? env->GetStringCritical(arg0, 0) : 0; const char* arg0Str = 0; String8 arg0_8; if (str) { arg0_8.set(str, env->GetStringLength(arg0)); env->ReleaseStringCritical(arg0, str); arg0Str = arg0_8.string(); } str = arg1 ? env->GetStringCritical(arg1, 0) : 0; const char* arg1Str = 0; String8 arg1_8; if (str) { arg1_8.set(str, env->GetStringLength(arg1)); env->ReleaseStringCritical(arg1, str); arg1Str = arg1_8.string(); } int procId; int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId); if (processIdArray) { int procIdLen = env->GetArrayLength(processIdArray); if (procIdLen > 0) { jboolean isCopy; int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); if (pProcId) { *pProcId = procId; env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); } } } jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init); if (!result) { LOGE("Couldn't create a FileDescriptor."); } else { env->SetIntField(result, field_fileDescriptor_descriptor, ptm); } return result; }
JNIEXPORT jobject JNICALL Java_com_google_ase_Exec_createSubprocess( JNIEnv* env, jclass clazz, jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray) { char* cmd_8 = JNU_GetStringNativeChars(env, cmd); char* arg0_8 = JNU_GetStringNativeChars(env, arg0); char* arg1_8 = JNU_GetStringNativeChars(env, arg1); int procId; int ptm = create_subprocess(cmd_8, arg0_8, arg1_8, &procId); if (processIdArray) { int procIdLen = env->GetArrayLength(processIdArray); if (procIdLen > 0) { jboolean isCopy; int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); if (pProcId) { *pProcId = procId; env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); } } } jclass Class_java_io_FileDescriptor = env->FindClass("java/io/FileDescriptor"); jmethodID init = env->GetMethodID(Class_java_io_FileDescriptor, "<init>", "()V"); jobject result = env->NewObject(Class_java_io_FileDescriptor, init); if (!result) { LOG("Couldn't create a FileDescriptor."); } else { jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I"); env->SetIntField(result, descriptor, ptm); } return result; }
static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, jstring cmd, jobjectArray args, jobjectArray envVars, jintArray processIdArray) { const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; String8 cmd_8; if (str) { cmd_8.set(str, env->GetStringLength(cmd)); env->ReleaseStringCritical(cmd, str); } jsize size = args ? env->GetArrayLength(args) : 0; char **argv = NULL; String8 tmp_8; if (size > 0) { argv = (char **)malloc((size+1)*sizeof(char *)); if (!argv) { throwOutOfMemoryError(env, "Couldn't allocate argv array"); return NULL; } for (int i = 0; i < size; ++i) { jstring arg = reinterpret_cast<jstring>(env->GetObjectArrayElement(args, i)); str = env->GetStringCritical(arg, 0); if (!str) { throwOutOfMemoryError(env, "Couldn't get argument from array"); return NULL; } tmp_8.set(str, env->GetStringLength(arg)); env->ReleaseStringCritical(arg, str); argv[i] = strdup(tmp_8.string()); } argv[size] = NULL; } size = envVars ? env->GetArrayLength(envVars) : 0; char **envp = NULL; if (size > 0) { envp = (char **)malloc((size+1)*sizeof(char *)); if (!envp) { throwOutOfMemoryError(env, "Couldn't allocate envp array"); return NULL; } for (int i = 0; i < size; ++i) { jstring var = reinterpret_cast<jstring>(env->GetObjectArrayElement(envVars, i)); str = env->GetStringCritical(var, 0); if (!str) { throwOutOfMemoryError(env, "Couldn't get env var from array"); return NULL; } tmp_8.set(str, env->GetStringLength(var)); env->ReleaseStringCritical(var, str); envp[i] = strdup(tmp_8.string()); } envp[size] = NULL; } int procId; int ptm = create_subprocess(cmd_8.string(), argv, envp, &procId); if (argv) { for (char **tmp = argv; *tmp; ++tmp) { free(*tmp); } free(argv); } if (envp) { for (char **tmp = envp; *tmp; ++tmp) { free(*tmp); } free(envp); } if (processIdArray) { int procIdLen = env->GetArrayLength(processIdArray); if (procIdLen > 0) { jboolean isCopy; int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); if (pProcId) { *pProcId = procId; env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); } } } jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init); if (!result) { LOGE("Couldn't create a FileDescriptor."); } else { env->SetIntField(result, field_fileDescriptor_descriptor, ptm); } return result; }
JNIEXPORT jint JNICALL Java_jackpal_androidterm_TermExec_createSubprocessInternal(JNIEnv *env, jclass clazz, jstring cmd, jobjectArray args, jobjectArray envVars, jint masterFd) { const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; String8 cmd_8; if (str) { cmd_8.set(str, env->GetStringLength(cmd)); env->ReleaseStringCritical(cmd, str); } jsize size = args ? env->GetArrayLength(args) : 0; char **argv = NULL; String8 tmp_8; if (size > 0) { argv = (char **)malloc((size+1)*sizeof(char *)); if (!argv) { throwOutOfMemoryError(env, "Couldn't allocate argv array"); return 0; } for (int i = 0; i < size; ++i) { jstring arg = reinterpret_cast<jstring>(env->GetObjectArrayElement(args, i)); str = env->GetStringCritical(arg, 0); if (!str) { throwOutOfMemoryError(env, "Couldn't get argument from array"); return 0; } tmp_8.set(str, env->GetStringLength(arg)); env->ReleaseStringCritical(arg, str); argv[i] = strdup(tmp_8.string()); } argv[size] = NULL; } size = envVars ? env->GetArrayLength(envVars) : 0; char **envp = NULL; if (size > 0) { envp = (char **)malloc((size+1)*sizeof(char *)); if (!envp) { throwOutOfMemoryError(env, "Couldn't allocate envp array"); return 0; } for (int i = 0; i < size; ++i) { jstring var = reinterpret_cast<jstring>(env->GetObjectArrayElement(envVars, i)); str = env->GetStringCritical(var, 0); if (!str) { throwOutOfMemoryError(env, "Couldn't get env var from array"); return 0; } tmp_8.set(str, env->GetStringLength(var)); env->ReleaseStringCritical(var, str); envp[i] = strdup(tmp_8.string()); } envp[size] = NULL; } int ptm = create_subprocess(env, cmd_8.string(), argv, envp, masterFd); if (argv) { for (char **tmp = argv; *tmp; ++tmp) { free(*tmp); } free(argv); } if (envp) { for (char **tmp = envp; *tmp; ++tmp) { free(*tmp); } free(envp); } return ptm; }
static int create_subprocess(JNIEnv* env, char const* cmd, char const* cwd, char* const argv[], char** envp, int* pProcessId) { int ptm = open("/dev/ptmx", O_RDWR | O_CLOEXEC); if (ptm < 0) return throw_runtime_exception(env, "Cannot open /dev/ptmx"); #ifdef LACKS_PTSNAME_R char* devname; #else char devname[64]; #endif if (grantpt(ptm) || unlockpt(ptm) || #ifdef LACKS_PTSNAME_R (devname = ptsname(ptm)) == NULL #else ptsname_r(ptm, devname, sizeof(devname)) #endif ) { return throw_runtime_exception(env, "Cannot grantpt()/unlockpt()/ptsname_r() on /dev/ptmx"); } // Enable UTF-8 mode and disable flow control to prevent Ctrl+S from locking up the display. struct termios tios; tcgetattr(ptm, &tios); tios.c_iflag |= IUTF8; tios.c_iflag &= ~(IXON | IXOFF); tcsetattr(ptm, TCSANOW, &tios); /** Set initial winsize (better too small than too large). */ struct winsize sz = { .ws_row = 20, .ws_col = 20 }; ioctl(ptm, TIOCSWINSZ, &sz); pid_t pid = fork(); if (pid < 0) { return throw_runtime_exception(env, "Fork failed"); } else if (pid > 0) { *pProcessId = (int) pid; return ptm; } else { // Clear signals which the Android java process may have blocked: sigset_t signals_to_unblock; sigfillset(&signals_to_unblock); sigprocmask(SIG_UNBLOCK, &signals_to_unblock, 0); close(ptm); setsid(); int pts = open(devname, O_RDWR); if (pts < 0) exit(-1); dup2(pts, 0); dup2(pts, 1); dup2(pts, 2); DIR* self_dir = opendir("/proc/self/fd"); if (self_dir != NULL) { int self_dir_fd = dirfd(self_dir); struct dirent* entry; while ((entry = readdir(self_dir)) != NULL) { int fd = atoi(entry->d_name); if(fd > 2 && fd != self_dir_fd) close(fd); } closedir(self_dir); } clearenv(); if (envp) for (; *envp; ++envp) putenv(*envp); if (chdir(cwd) != 0) { char* error_message; // No need to free asprintf()-allocated memory since doing execvp() or exit() below. if (asprintf(&error_message, "chdir(\"%s\")", cwd) == -1) error_message = "chdir()"; perror(error_message); fflush(stderr); } execvp(cmd, argv); // Show terminal output about failing exec() call: char* error_message; if (asprintf(&error_message, "exec(\"%s\")", cmd) == -1) error_message = "exec()"; perror(error_message); _exit(1); } } JNIEXPORT jint JNICALL Java_com_termux_terminal_JNI_createSubprocess(JNIEnv* env, jclass TERMUX_UNUSED(clazz), jstring cmd, jstring cwd, jobjectArray args, jobjectArray envVars, jintArray processIdArray) { jsize size = args ? (*env)->GetArrayLength(env, args) : 0; char** argv = NULL; if (size > 0) { argv = (char**) malloc((size + 1) * sizeof(char*)); if (!argv) return throw_runtime_exception(env, "Couldn't allocate argv array"); for (int i = 0; i < size; ++i) { jstring arg_java_string = (jstring) (*env)->GetObjectArrayElement(env, args, i); char const* arg_utf8 = (*env)->GetStringUTFChars(env, arg_java_string, NULL); if (!arg_utf8) return throw_runtime_exception(env, "GetStringUTFChars() failed for argv"); argv[i] = strdup(arg_utf8); (*env)->ReleaseStringUTFChars(env, arg_java_string, arg_utf8); } argv[size] = NULL; } size = envVars ? (*env)->GetArrayLength(env, envVars) : 0; char** envp = NULL; if (size > 0) { envp = (char**) malloc((size + 1) * sizeof(char *)); if (!envp) return throw_runtime_exception(env, "malloc() for envp array failed"); for (int i = 0; i < size; ++i) { jstring env_java_string = (jstring) (*env)->GetObjectArrayElement(env, envVars, i); char const* env_utf8 = (*env)->GetStringUTFChars(env, env_java_string, 0); if (!env_utf8) return throw_runtime_exception(env, "GetStringUTFChars() failed for env"); envp[i] = strdup(env_utf8); (*env)->ReleaseStringUTFChars(env, env_java_string, env_utf8); } envp[size] = NULL; } int procId = 0; char const* cmd_cwd = (*env)->GetStringUTFChars(env, cwd, NULL); char const* cmd_utf8 = (*env)->GetStringUTFChars(env, cmd, NULL); int ptm = create_subprocess(env, cmd_utf8, cmd_cwd, argv, envp, &procId); (*env)->ReleaseStringUTFChars(env, cmd, cmd_utf8); (*env)->ReleaseStringUTFChars(env, cmd, cmd_cwd); if (argv) { for (char** tmp = argv; *tmp; ++tmp) free(*tmp); free(argv); } if (envp) { for (char** tmp = envp; *tmp; ++tmp) free(*tmp); free(envp); } int* pProcId = (int*) (*env)->GetPrimitiveArrayCritical(env, processIdArray, NULL); if (!pProcId) return throw_runtime_exception(env, "JNI call GetPrimitiveArrayCritical(processIdArray, &isCopy) failed"); *pProcId = procId; (*env)->ReleasePrimitiveArrayCritical(env, processIdArray, pProcId, 0); return ptm; }
int service_to_fd(const char *name) { int ret = -1; if(!strncmp(name, "tcp:", 4)) { int port = atoi(name + 4); name = strchr(name + 4, ':'); if(name == 0) { ret = socket_loopback_client(port, SOCK_STREAM); if (ret >= 0) disable_tcp_nagle(ret); } else { #if ADB_HOST adb_mutex_lock(&dns_lock); ret = socket_network_client(name + 1, port, SOCK_STREAM); adb_mutex_unlock(&dns_lock); #else return -1; #endif } #ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */ } else if(!strncmp(name, "local:", 6)) { ret = socket_local_client(name + 6, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); } else if(!strncmp(name, "localreserved:", 14)) { ret = socket_local_client(name + 14, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); } else if(!strncmp(name, "localabstract:", 14)) { ret = socket_local_client(name + 14, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); } else if(!strncmp(name, "localfilesystem:", 16)) { ret = socket_local_client(name + 16, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM); #endif #if ADB_HOST } else if(!strncmp("dns:", name, 4)){ char *n = strdup(name + 4); if(n == 0) return -1; ret = create_service_thread(dns_service, n); #else /* !ADB_HOST */ } else if(!strncmp("dev:", name, 4)) { ret = unix_open(name + 4, O_RDWR); } else if(!strncmp(name, "framebuffer:", 12)) { ret = create_service_thread(framebuffer_service, 0); } else if(recovery_mode && !strncmp(name, "recover:", 8)) { ret = create_service_thread(recover_service, (void*) atoi(name + 8)); } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); } else if (!strncmp(name, "log:", 4)) { ret = create_service_thread(log_service, get_log_file_path(name + 4)); #endif } else if(!HOST && !strncmp(name, "shell:", 6)) { if(name[6]) { ret = create_subprocess(SHELL_COMMAND, "-c", name + 6); } else { ret = create_subprocess(SHELL_COMMAND, "-", 0); } #if !ADB_HOST } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread(file_sync_service, NULL); } else if(!strncmp(name, "remount:", 8)) { ret = create_service_thread(remount_service, NULL); } else if(!strncmp(name, "reboot:", 7)) { void* arg = strdup(name + 7); if(arg == 0) return -1; ret = create_service_thread(reboot_service, arg); } else if(!strncmp(name, "root:", 5)) { ret = create_service_thread(restart_root_service, NULL); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) == 0) { port = 0; } ret = create_service_thread(restart_tcp_service, (void *)port); } else if(!strncmp(name, "usb:", 4)) { ret = create_service_thread(restart_usb_service, NULL); #endif #if 0 } else if(!strncmp(name, "echo:", 5)){ ret = create_service_thread(echo_service, 0); #endif } if (ret >= 0) { close_on_exec(ret); } return ret; }
static jobject DEF_JNI(createSubprocess, jstring cmd, jstring arg0, jstring arg1, jobjectArray envVars, jintArray processIdArray) { char const* cmd_str = cmd ? env->GetStringUTFChars(cmd, NULL) : NULL; char const* arg0_str = arg0 ? env->GetStringUTFChars(arg0, NULL) : NULL; char const* arg1_str = arg1 ? env->GetStringUTFChars(arg1, NULL) : NULL; LOGI("cmd_str = '%s'", cmd_str); LOGI("arg0_str = '%s'", arg0_str); LOGI("arg1_str = '%s'", arg1_str); int num_env_vars = envVars ? env->GetArrayLength(envVars) : 0; char **envp = NULL; char const* tmp = NULL; if (num_env_vars > 0) { envp = (char **)malloc((num_env_vars + 1) * sizeof(char*)); if (!envp) { throwOutOfMemoryError(env, "Couldn't allocate envp array"); return NULL; } for (int i = 0; i < num_env_vars; ++i) { jobject obj = env->GetObjectArrayElement(envVars, i); jstring env_var = reinterpret_cast<jstring>(obj); tmp = env->GetStringUTFChars(env_var, 0); if (tmp == NULL) { throwOutOfMemoryError(env, "Couldn't get env var from array"); return NULL; } envp[i] = strdup(tmp); if (envp[i] == NULL) { throwOutOfMemoryError(env, "Couldn't strdup() env var"); return NULL; } env->ReleaseStringUTFChars(env_var, tmp); } envp[num_env_vars] = NULL; } int procId; int ptm = create_subprocess(cmd_str, arg0_str, arg1_str, envp, &procId); env->ReleaseStringUTFChars(cmd, cmd_str); if(arg0 != NULL) env->ReleaseStringUTFChars(arg0, arg0_str); if(arg1 != NULL) env->ReleaseStringUTFChars(arg1, arg1_str); for (int i = 0; i < num_env_vars; ++i) free(envp[i]); free(envp); if (processIdArray) { int procIdLen = env->GetArrayLength(processIdArray); if (procIdLen > 0) { jboolean isCopy; int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); if (pProcId) { *pProcId = procId; env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); } } } jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init); if (!result) { LOGE("Couldn't create a FileDescriptor."); } else { env->SetIntField(result, field_fileDescriptor_descriptor, ptm); } return result; }