Termux 原理探究
HumphreyDan详细介绍请移步: 官网,中文镜像,清华源
源码探究
1 2 3 4 5 6 7 8
| static int create_subprocess(JNIEnv* env, char const* cmd, char const* cwd, char* const argv[], char** envp, int* pProcessId, jint rows, jint columns)
|
此方法即为 termux 的核心,通过该方法将用户输入的命令和当前工作的文件夹传入打开的终端去执行.
1、打开伪终端
1 2 3 4 5
| int ptm = open("/dev/ptmx", O_RDWR | O_CLOEXEC); if (ptm < 0) return throw_runtime_exception(env, "Cannot open /dev/ptmx");
|
2、初始化设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #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"); }
struct termios tios;
tcgetattr(ptm, &tios);
tios.c_iflag |= IUTF8; tios.c_iflag &= ~(IXON | IXOFF); tcsetattr(ptm, TCSANOW, &tios);
struct winsize sz = { .ws_row = (unsigned short) rows, .ws_col = (unsigned short) columns }; ioctl(ptm, TIOCSWINSZ, &sz);
|
3、创建新进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| pid_t pid = fork(); if (pid < 0) { return throw_runtime_exception(env, "Fork failed"); } else if (pid > 0) { *pProcessId = (int) pid; return ptm; } else { 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; if (asprintf(&error_message, "chdir(\"%s\")", cwd) == -1) error_message = "chdir()"; perror(error_message); fflush(stderr); } execvp(cmd, argv); char* error_message; if (asprintf(&error_message, "exec(\"%s\")", cmd) == -1) error_message = "exec()"; perror(error_message); _exit(1); }
|
因为 termux 的主界面是一个 psuedo terminal,所以第一步先建立伪终端设备,然后创建当前进程的子进程去执行命令.因此,termux 能访问的就只能 termux app 私有文件目录及 SD 卡公开目录(需要特殊命令支持)