From c4847ed2885c08f7c57a1861fa84dc6180b6110a Mon Sep 17 00:00:00 2001 From: LoveSy Date: Wed, 9 Apr 2025 20:46:32 +0800 Subject: [PATCH] Move pts to rust, and avoid using thread --- native/src/Android.mk | 1 - native/src/core/lib.rs | 5 + native/src/core/su/mod.rs | 2 + native/src/core/su/pts.cpp | 294 ------------------------------------- native/src/core/su/pts.hpp | 102 ------------- native/src/core/su/pts.rs | 133 +++++++++++++++++ native/src/core/su/su.cpp | 6 +- 7 files changed, 141 insertions(+), 402 deletions(-) delete mode 100644 native/src/core/su/pts.cpp delete mode 100644 native/src/core/su/pts.hpp create mode 100644 native/src/core/su/pts.rs diff --git a/native/src/Android.mk b/native/src/Android.mk index beb189f1e..ef8b57e01 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -27,7 +27,6 @@ LOCAL_SRC_FILES := \ core/resetprop/resetprop.cpp \ core/su/su.cpp \ core/su/connect.cpp \ - core/su/pts.cpp \ core/zygisk/entry.cpp \ core/zygisk/module.cpp \ core/zygisk/hook.cpp \ diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 65768d7b4..95e276dbf 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -16,6 +16,7 @@ use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd use mount::{find_preinit_device, revert_unmount}; use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; use socket::{recv_fd, recv_fds, send_fd, send_fds}; +use su::{pump_stdin_stdout, get_pty_num, restore_stdin}; use std::fs::File; use std::mem::ManuallyDrop; use std::ops::DerefMut; @@ -207,6 +208,10 @@ pub mod ffi { #[namespace = "rust"] fn daemon_entry(); + + fn pump_stdin_stdout(infd: i32, outfd: i32); + fn get_pty_num(fd: i32) -> i32; + fn restore_stdin() -> bool; } // Default constructors diff --git a/native/src/core/su/mod.rs b/native/src/core/su/mod.rs index 067d5b65a..b77db4b2e 100644 --- a/native/src/core/su/mod.rs +++ b/native/src/core/su/mod.rs @@ -1,4 +1,6 @@ mod daemon; mod db; +mod pts; pub use daemon::SuInfo; +pub use pts::{pump_stdin_stdout, get_pty_num, restore_stdin}; diff --git a/native/src/core/su/pts.cpp b/native/src/core/su/pts.cpp deleted file mode 100644 index 3700cf093..000000000 --- a/native/src/core/su/pts.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 2013, Tan Chee Eng (@tan-ce) - */ - - /* - * pts.c - * - * Manages the pseudo-terminal driver on Linux/Android and provides some - * helper functions to handle raw input mode and terminal window resizing - */ - -#include -#include -#include - -#include - -#include "pts.hpp" - -/** - * Helper functions - */ -// Ensures all the data is written out -static int write_blocking(int fd, char *buf, ssize_t bufsz) { - ssize_t ret, written; - - written = 0; - do { - ret = write(fd, buf + written, bufsz - written); - if (ret == -1) return -1; - written += ret; - } while (written < bufsz); - - return 0; -} - -/** - * Pump data from input FD to output FD. If close_output is - * true, then close the output FD when we're done. - */ -static void pump(int input, int output, bool close_output = true) { - char buf[4096]; - int len; - while ((len = read(input, buf, 4096)) > 0) { - if (write_blocking(output, buf, len) == -1) break; - } - close(input); - if (close_output) close(output); -} - -static void* pump_thread(void* data) { - int *fds = (int*) data; - pump(fds[0], fds[1]); - delete[] fds; - return nullptr; -} - -static void pump_async(int input, int output) { - pthread_t writer; - int *fds = new int[2]; - fds[0] = input; - fds[1] = output; - pthread_create(&writer, nullptr, pump_thread, fds); -} - - -/** - * pts_open - * - * Opens a pts device and returns the name of the slave tty device. - * - * Arguments - * slave_name the name of the slave device - * slave_name_size the size of the buffer passed via slave_name - * - * Return Values - * on failure either -2 or -1 (errno set) is returned. - * on success, the file descriptor of the master device is returned. - */ -int pts_open(char *slave_name, size_t slave_name_size) { - int fdm; - - // Open master ptmx device - fdm = open("/dev/ptmx", O_RDWR); - if (fdm == -1) - goto error; - - // Get the slave name - if (ptsname_r(fdm, slave_name, slave_name_size - 1)) - goto error; - - slave_name[slave_name_size - 1] = '\0'; - - // Grant, then unlock - if (grantpt(fdm) == -1) - goto error; - - if (unlockpt(fdm) == -1) - goto error; - - return fdm; -error: - close(fdm); - PLOGE("pts_open"); - return -1; -} - -int get_pty_num(int fd) { - int pty_num = -1; - if (ioctl(fd, TIOCGPTN, &pty_num) != 0) { - LOGW("get_pty_num failed with %d: %s\n", errno, std::strerror(errno)); - return -1; - } - return pty_num; -} - -// Stores the previous termios of stdin -static struct termios old_stdin; -static int stdin_is_raw = 0; - -/** - * set_stdin_raw - * - * Changes stdin to raw unbuffered mode, disables echo, - * auto carriage return, etc. - * - * Return Value - * on failure -1, and errno is set - * on success 0 - */ -int set_stdin_raw() { - struct termios termios{}; - - if (tcgetattr(STDIN_FILENO, &termios) < 0) { - return -1; - } - - old_stdin = termios; - - cfmakeraw(&termios); - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) < 0) { - // https://blog.zhanghai.me/fixing-line-editing-on-android-8-0/ - if (tcsetattr(STDIN_FILENO, TCSADRAIN, &termios) < 0) { - return -1; - } - } - - stdin_is_raw = 1; - - return 0; -} - -/** - * restore_stdin - * - * Restore termios on stdin to the state it was before - * set_stdin_raw() was called. If set_stdin_raw() was - * never called, does nothing and doesn't return an error. - * - * This function is async-safe. - * - * Return Value - * on failure, -1 and errno is set - * on success, 0 - */ -int restore_stdin() { - if (!stdin_is_raw) return 0; - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) { - if (tcsetattr(STDIN_FILENO, TCSADRAIN, &old_stdin) < 0) { - return -1; - } - } - - stdin_is_raw = 0; - - return 0; -} - -// Flag indicating whether the sigwinch watcher should terminate. -static volatile bool close_sigwinch_watcher = false; - -/** - * Thread process. Wait for a SIGWINCH to be received, then update - * the terminal size. - */ -static void *watch_sigwinch(void *data) { - sigset_t winch; - int *fds = (int *)data; - int sig; - - sigemptyset(&winch); - sigaddset(&winch, SIGWINCH); - pthread_sigmask(SIG_UNBLOCK, &winch, nullptr); - - do { - if (close_sigwinch_watcher) - break; - - // Get the new terminal size - struct winsize w; - if (ioctl(fds[0], TIOCGWINSZ, &w) == -1) - continue; - - // Set the new terminal size - ioctl(fds[1], TIOCSWINSZ, &w); - - } while (sigwait(&winch, &sig) == 0); - delete[] fds; - - return nullptr; -} - -/** - * watch_sigwinch_async - * - * After calling this function, if the application receives - * SIGWINCH, the terminal window size will be read from - * "input" and set on "output". - * - * NOTE: This function blocks SIGWINCH and spawns a thread. - * NOTE 2: This function must be called before any of the - * pump functions. - * - * Arguments - * master A file descriptor of the TTY window size to follow - * slave A file descriptor of the TTY window size which is - * to be set on SIGWINCH - * - * Return Value - * on failure, -1 and errno will be set. In this case, no - * thread has been spawned and SIGWINCH will not be - * blocked. - * on success, 0 - */ -int watch_sigwinch_async(int master, int slave) { - pthread_t watcher; - int *fds = new int[2]; - - // Block SIGWINCH so sigwait can later receive it - sigset_t winch; - sigemptyset(&winch); - sigaddset(&winch, SIGWINCH); - if (pthread_sigmask(SIG_BLOCK, &winch, nullptr) == -1) { - delete[] fds; - return -1; - } - - // Initialize some variables, then start the thread - close_sigwinch_watcher = 0; - fds[0] = master; - fds[1] = slave; - int ret = pthread_create(&watcher, nullptr, &watch_sigwinch, fds); - if (ret != 0) { - delete[] fds; - errno = ret; - return -1; - } - - return 0; -} - -/** - * pump_stdin_async - * - * Forward data from STDIN to the given FD - * in a separate thread - */ -void pump_stdin_async(int outfd) { - // Put stdin into raw mode - set_stdin_raw(); - - // Pump data from stdin to the PTY - pump_async(STDIN_FILENO, outfd); -} - -/** - * pump_stdout_blocking - * - * Forward data from the FD to STDOUT. - * Returns when the remote end of the FD closes. - * - * Before returning, restores stdin settings. - */ -void pump_stdout_blocking(int infd) { - // Pump data from stdout to PTY - pump(infd, STDOUT_FILENO, false /* Don't close output when done */); - - // Cleanup - restore_stdin(); - close_sigwinch_watcher = true; - raise(SIGWINCH); -} diff --git a/native/src/core/su/pts.hpp b/native/src/core/su/pts.hpp deleted file mode 100644 index 25b655cca..000000000 --- a/native/src/core/su/pts.hpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2013, Tan Chee Eng (@tan-ce) - */ - - /* - * pts.hpp - * - * Manages the pseudo-terminal driver on Linux/Android and provides some - * helper functions to handle raw input mode and terminal window resizing - */ - -#ifndef _PTS_H_ -#define _PTS_H_ - -#include - -/** - * pts_open - * - * Opens a pts device and returns the name of the slave tty device. - * - * Arguments - * slave_name the name of the slave device - * slave_name_size the size of the buffer passed via slave_name - * - * Return Values - * on failure either -2 or -1 (errno set) is returned. - * on success, the file descriptor of the master device is returned. - */ -int pts_open(char *slave_name, size_t slave_name_size); - - -int get_pty_num(int fd); - -/** - * set_stdin_raw - * - * Changes stdin to raw unbuffered mode, disables echo, - * auto carriage return, etc. - * - * Return Value - * on failure -1, and errno is set - * on success 0 - */ -int set_stdin_raw(void); - -/** - * restore_stdin - * - * Restore termios on stdin to the state it was before - * set_stdin_raw() was called. If set_stdin_raw() was - * never called, does nothing and doesn't return an error. - * - * This function is async-safe. - * - * Return Value - * on failure, -1 and errno is set - * on success, 0 - */ -int restore_stdin(void); - -/** - * watch_sigwinch_async - * - * After calling this function, if the application receives - * SIGWINCH, the terminal window size will be read from - * "input" and set on "output". - * - * NOTE: This function blocks SIGWINCH and spawns a thread. - * - * Arguments - * master A file descriptor of the TTY window size to follow - * slave A file descriptor of the TTY window size which is - * to be set on SIGWINCH - * - * Return Value - * on failure, -1 and errno will be set. In this case, no - * thread has been spawned and SIGWINCH will not be - * blocked. - * on success, 0 - */ -int watch_sigwinch_async(int master, int slave); - -/** - * pump_stdin_async - * - * Forward data from STDIN to the given FD - * in a separate thread - */ -void pump_stdin_async(int outfd); - -/** - * pump_stdout_blocking - * - * Forward data from the FD to STDOUT. - * Returns when the remote end of the FD closes. - * - * Before returning, restores stdin settings. - */ -void pump_stdout_blocking(int infd); - -#endif diff --git a/native/src/core/su/pts.rs b/native/src/core/su/pts.rs new file mode 100644 index 000000000..68d2ae49f --- /dev/null +++ b/native/src/core/su/pts.rs @@ -0,0 +1,133 @@ +use base::{ + error, + libc::{ + POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, + TIOCSWINSZ, cfmakeraw, close, poll, pollfd, raise, read, sigaddset, sigemptyset, signalfd, + sigprocmask, sigset_t, tcsetattr, winsize, write, + }, + libc::{STDIN_FILENO, ioctl, tcgetattr, termios}, + warn, +}; +use std::ptr::null_mut; + +static mut OLD_STDIN: Option = None; + +pub fn get_pty_num(fd: i32) -> i32 { + let mut pty_num = -1i32; + if unsafe { ioctl(fd, 0x80045430u32 as _, &mut pty_num) } != 0 { + warn!("Failed to get pty number"); + } + pty_num +} + +fn set_stdin_raw() -> bool { + unsafe { + let mut termios: termios = std::mem::zeroed(); + + if tcgetattr(STDIN_FILENO, &mut termios) < 0 { + return false; + } + + OLD_STDIN = Some(termios); + + cfmakeraw(&mut termios); + + if tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) < 0 + && tcsetattr(STDIN_FILENO, TCSADRAIN, &termios) < 0 + { + warn!("Failed to set terminal attributes"); + return false; + } + } + + true +} + +pub fn restore_stdin() -> bool { + unsafe { + if let Some(ref termios) = OLD_STDIN { + if tcsetattr(STDIN_FILENO, TCSAFLUSH, termios) < 0 + && tcsetattr(STDIN_FILENO, TCSADRAIN, termios) < 0 + { + warn!("Failed to restore terminal attributes"); + return false; + } + } + OLD_STDIN = None; + true + } +} + +fn resize_pty(outfd: i32) { + let mut ws: winsize = unsafe { std::mem::zeroed() }; + if unsafe { ioctl(STDIN_FILENO, TIOCGWINSZ, &mut ws) } >= 0 { + unsafe { ioctl(outfd, TIOCSWINSZ, &ws) }; + } +} + +pub fn pump_stdin_stdout(infd: i32, outfd: i32) { + set_stdin_raw(); + + let sfd = unsafe { + let mut mask: sigset_t = std::mem::zeroed(); + sigemptyset(&mut mask); + sigaddset(&mut mask, SIGWINCH); + if sigprocmask(SIG_BLOCK, &mask, null_mut()) < 0 { + error!("sigprocmask"); + } + signalfd(-1, &mask, SFD_CLOEXEC) + }; + + resize_pty(outfd); + + let mut pfds = [ + pollfd { + fd: STDIN_FILENO, + events: POLLIN, + revents: 0, + }, + pollfd { + fd: infd, + events: POLLIN, + revents: 0, + }, + pollfd { + fd: sfd, + events: POLLIN, + revents: 0, + }, + ]; + + let mut buf = [0u8; 4096]; + 'poll: loop { + let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) }; + + if ready < 0 { + error!("poll error"); + break; + } + + for pfd in &pfds { + if pfd.revents & POLLIN != 0 { + let n = unsafe { read(pfd.fd, buf.as_mut_ptr() as _, buf.len()) }; + if n <= 0 { + error!("read error"); + break 'poll; + } + if pfd.fd == STDIN_FILENO { + unsafe { write(outfd, buf.as_ptr() as _, n as _) }; + } else if pfd.fd == infd { + unsafe { write(STDOUT_FILENO, buf.as_ptr() as _, n as _) }; + } else if pfd.fd == sfd { + resize_pty(outfd); + } + } else if pfd.revents != 0 && pfd.fd == infd { + unsafe { close(pfd.fd) }; + break 'poll; + } + } + } + + restore_stdin(); + unsafe { raise(SIGWINCH) }; +} diff --git a/native/src/core/su/su.cpp b/native/src/core/su/su.cpp index e85e5926c..772b248e5 100644 --- a/native/src/core/su/su.cpp +++ b/native/src/core/su/su.cpp @@ -20,8 +20,6 @@ #include #include -#include "pts.hpp" - using namespace std; #define DEFAULT_SHELL "/system/bin/sh" @@ -241,9 +239,7 @@ int su_client_main(int argc, char *argv[]) { if (atty) { setup_sighandlers(sighandler); - watch_sigwinch_async(STDOUT_FILENO, ptmx); - pump_stdin_async(ptmx); - pump_stdout_blocking(ptmx); + pump_stdin_stdout(ptmx, ptmx); } // Get the exit code