mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-10-27 15:12:56 +00:00
Enable term for nix
This commit is contained in:
@@ -26,4 +26,4 @@ bytemuck = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
bit-set = { workspace = true }
|
||||
argh = { workspace = true }
|
||||
nix = { workspace = true, features = ["fs", "poll", "signal"] }
|
||||
nix = { workspace = true, features = ["fs", "poll", "signal", "term"] }
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::fs::File;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::FromRawFd;
|
||||
use su::{get_pty_num, pump_tty, restore_stdin};
|
||||
use su::{get_pty_num, pump_tty};
|
||||
use zygisk::zygisk_should_load_module;
|
||||
|
||||
#[path = "../include/consts.rs"]
|
||||
@@ -181,9 +181,8 @@ pub mod ffi {
|
||||
fn recv_fd(socket: i32) -> i32;
|
||||
fn recv_fds(socket: i32) -> Vec<i32>;
|
||||
fn write_to_fd(self: &SuRequest, fd: i32);
|
||||
fn pump_tty(infd: i32, outfd: i32);
|
||||
fn pump_tty(ptmx: i32, pump_stdin: bool);
|
||||
fn get_pty_num(fd: i32) -> i32;
|
||||
fn restore_stdin() -> bool;
|
||||
fn restorecon();
|
||||
fn lgetfilecon(path: Utf8CStrRef, con: &mut [u8]) -> bool;
|
||||
fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool;
|
||||
|
||||
@@ -4,4 +4,4 @@ mod db;
|
||||
mod pts;
|
||||
|
||||
pub use daemon::SuInfo;
|
||||
pub use pts::{get_pty_num, pump_tty, restore_stdin};
|
||||
pub use pts::{get_pty_num, pump_tty};
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use base::{FileOrStd, LibcReturn, LoggedResult, OsResult, libc, warn};
|
||||
use libc::{
|
||||
STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, c_int, cfmakeraw,
|
||||
ssize_t, tcgetattr, tcsetattr, termios, winsize,
|
||||
};
|
||||
use base::{FileOrStd, LibcReturn, LoggedResult, OsResult, ResultExt, libc, warn};
|
||||
use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, TIOCSWINSZ, c_int, ssize_t, winsize};
|
||||
use nix::{
|
||||
fcntl::OFlag,
|
||||
poll::{PollFd, PollFlags, PollTimeout},
|
||||
sys::signal::{SigSet, Signal},
|
||||
poll::{PollFd, PollFlags, PollTimeout, poll},
|
||||
sys::signal::{SigSet, Signal, raise},
|
||||
sys::signalfd::{SfdFlags, SignalFd},
|
||||
sys::termios::{SetArg, Termios, cfmakeraw, tcgetattr, tcsetattr},
|
||||
unistd::pipe2,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
@@ -16,7 +15,6 @@ use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
static mut OLD_STDIN: Option<termios> = None;
|
||||
static SHOULD_USE_SPLICE: AtomicBool = AtomicBool::new(true);
|
||||
const TIOCGPTN: u32 = 0x80045430;
|
||||
|
||||
@@ -33,51 +31,10 @@ pub fn get_pty_num(fd: i32) -> i32 {
|
||||
pty_num
|
||||
}
|
||||
|
||||
fn set_stdin_raw() -> bool {
|
||||
unsafe {
|
||||
let mut termios: termios = std::mem::zeroed();
|
||||
|
||||
if tcgetattr(STDIN_FILENO, &mut termios) < 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old_c_oflag = termios.c_oflag;
|
||||
OLD_STDIN = Some(termios);
|
||||
|
||||
cfmakeraw(&mut termios);
|
||||
|
||||
// don't modify output flags, since we are not setting stdout raw
|
||||
termios.c_oflag = old_c_oflag;
|
||||
|
||||
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
|
||||
&& 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) {
|
||||
fn sync_winsize(ptmx: i32) {
|
||||
let mut ws: winsize = unsafe { std::mem::zeroed() };
|
||||
if unsafe { ioctl(STDIN_FILENO, TIOCGWINSZ as u32, &mut ws) } >= 0 {
|
||||
unsafe { ioctl(outfd, TIOCSWINSZ as u32, &ws) };
|
||||
unsafe { ioctl(ptmx, TIOCSWINSZ as u32, &ws) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,21 +71,55 @@ fn pump_via_splice(infd: RawFd, outfd: RawFd, pipe: &(OwnedFd, OwnedFd)) -> Logg
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pump_tty_impl(infd: OwnedFd, raw_out: RawFd) -> LoggedResult<()> {
|
||||
let mut set = SigSet::empty();
|
||||
set.add(Signal::SIGWINCH);
|
||||
set.thread_block()
|
||||
.check_os_err("pthread_sigmask", None, None)?;
|
||||
let signal_fd =
|
||||
SignalFd::with_flags(&set, SfdFlags::SFD_CLOEXEC).into_os_result("signalfd", None, None)?;
|
||||
fn set_stdin_raw() -> LoggedResult<Termios> {
|
||||
let mut term = tcgetattr(FileOrStd::StdIn.as_file())?;
|
||||
let old_term = term.clone();
|
||||
|
||||
let raw_in = infd.as_raw_fd();
|
||||
let raw_sig = signal_fd.as_raw_fd();
|
||||
let old_output_flags = old_term.output_flags;
|
||||
cfmakeraw(&mut term);
|
||||
|
||||
// Preserve output_flags, since we are not setting stdout raw
|
||||
term.output_flags = old_output_flags;
|
||||
|
||||
tcsetattr(FileOrStd::StdIn.as_file(), SetArg::TCSAFLUSH, &term)
|
||||
.or_else(|_| tcsetattr(FileOrStd::StdIn.as_file(), SetArg::TCSADRAIN, &term))
|
||||
.check_os_err("tcsetattr", None, None)
|
||||
.log_with_msg(|w| w.write_str("Failed to set terminal attributes"))?;
|
||||
|
||||
Ok(old_term)
|
||||
}
|
||||
|
||||
fn restore_stdin(term: Termios) -> LoggedResult<()> {
|
||||
tcsetattr(FileOrStd::StdIn.as_file(), SetArg::TCSAFLUSH, &term)
|
||||
.or_else(|_| tcsetattr(FileOrStd::StdIn.as_file(), SetArg::TCSADRAIN, &term))
|
||||
.check_os_err("tcsetattr", None, None)
|
||||
.log_with_msg(|w| w.write_str("Failed to restore terminal attributes"))
|
||||
}
|
||||
|
||||
fn pump_tty_impl(ptmx: OwnedFd, pump_stdin: bool) -> LoggedResult<()> {
|
||||
let mut signal_fd: Option<SignalFd> = None;
|
||||
|
||||
let raw_ptmx = ptmx.as_raw_fd();
|
||||
let mut raw_sig = -1;
|
||||
|
||||
let mut poll_fds = Vec::with_capacity(3);
|
||||
poll_fds.push(PollFd::new(infd.as_fd(), PollFlags::POLLIN));
|
||||
poll_fds.push(PollFd::new(signal_fd.as_fd(), PollFlags::POLLIN));
|
||||
if raw_out >= 0 {
|
||||
poll_fds.push(PollFd::new(ptmx.as_fd(), PollFlags::POLLIN));
|
||||
if pump_stdin {
|
||||
// If stdin is tty, we need to monitor SIGWINCH
|
||||
let mut set = SigSet::empty();
|
||||
set.add(Signal::SIGWINCH);
|
||||
set.thread_block()
|
||||
.check_os_err("pthread_sigmask", None, None)?;
|
||||
let sig = SignalFd::with_flags(&set, SfdFlags::SFD_CLOEXEC)
|
||||
.into_os_result("signalfd", None, None)?;
|
||||
raw_sig = sig.as_raw_fd();
|
||||
signal_fd = Some(sig);
|
||||
poll_fds.push(PollFd::new(
|
||||
signal_fd.as_ref().unwrap().as_fd(),
|
||||
PollFlags::POLLIN,
|
||||
));
|
||||
|
||||
// We also need to pump stdin to ptmx
|
||||
poll_fds.push(PollFd::new(
|
||||
FileOrStd::StdIn.as_file().as_fd(),
|
||||
PollFlags::POLLIN,
|
||||
@@ -139,21 +130,21 @@ fn pump_tty_impl(infd: OwnedFd, raw_out: RawFd) -> LoggedResult<()> {
|
||||
let stop_flags = PollFlags::POLLERR | PollFlags::POLLHUP | PollFlags::POLLNVAL;
|
||||
|
||||
// Open a pipe to bypass userspace copy with splice
|
||||
let pipe_fd = nix::unistd::pipe2(OFlag::O_CLOEXEC).into_os_result("pipe2", None, None)?;
|
||||
let pipe_fd = pipe2(OFlag::O_CLOEXEC).into_os_result("pipe2", None, None)?;
|
||||
|
||||
'poll: loop {
|
||||
// Wait for event
|
||||
nix::poll::poll(&mut poll_fds, PollTimeout::NONE).check_os_err("poll", None, None)?;
|
||||
poll(&mut poll_fds, PollTimeout::NONE).check_os_err("poll", None, None)?;
|
||||
for pfd in &poll_fds {
|
||||
if pfd.all().unwrap_or(false) {
|
||||
let raw_fd = pfd.as_fd().as_raw_fd();
|
||||
if raw_fd == STDIN_FILENO {
|
||||
pump_via_splice(STDIN_FILENO, raw_out, &pipe_fd)?;
|
||||
} else if raw_fd == raw_in {
|
||||
pump_via_splice(raw_in, STDOUT_FILENO, &pipe_fd)?;
|
||||
pump_via_splice(STDIN_FILENO, raw_ptmx, &pipe_fd)?;
|
||||
} else if raw_fd == raw_ptmx {
|
||||
pump_via_splice(raw_ptmx, STDOUT_FILENO, &pipe_fd)?;
|
||||
} else if raw_fd == raw_sig {
|
||||
resize_pty(raw_out);
|
||||
signal_fd.read_signal()?;
|
||||
sync_winsize(raw_ptmx);
|
||||
signal_fd.as_ref().unwrap().read_signal()?;
|
||||
}
|
||||
} else if pfd
|
||||
.revents()
|
||||
@@ -168,12 +159,19 @@ fn pump_tty_impl(infd: OwnedFd, raw_out: RawFd) -> LoggedResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pump_tty(infd: i32, outfd: i32) {
|
||||
set_stdin_raw();
|
||||
pub fn pump_tty(ptmx: RawFd, pump_stdin: bool) {
|
||||
let old_term = if pump_stdin {
|
||||
sync_winsize(ptmx);
|
||||
set_stdin_raw().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let infd = unsafe { OwnedFd::from_raw_fd(infd) };
|
||||
pump_tty_impl(infd, outfd).ok();
|
||||
let ptmx = unsafe { OwnedFd::from_raw_fd(ptmx) };
|
||||
pump_tty_impl(ptmx, pump_stdin).ok();
|
||||
|
||||
restore_stdin();
|
||||
nix::sys::signal::raise(Signal::SIGWINCH).ok();
|
||||
if let Some(term) = old_term {
|
||||
restore_stdin(term).ok();
|
||||
}
|
||||
raise(Signal::SIGWINCH).ok();
|
||||
}
|
||||
|
||||
@@ -63,14 +63,8 @@ int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGI
|
||||
}
|
||||
|
||||
static void sighandler(int sig) {
|
||||
restore_stdin();
|
||||
|
||||
// Assume we'll only be called before death
|
||||
// See note before sigaction() in set_stdin_raw()
|
||||
//
|
||||
// Now, close all standard I/O to cause the pumps
|
||||
// to exit so we can continue and retrieve the exit
|
||||
// code
|
||||
// Close all standard I/O to cause the pumps to exit
|
||||
// so we can continue and retrieve the exit code.
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
@@ -243,9 +237,9 @@ int su_client_main(int argc, char *argv[]) {
|
||||
write_int(fd, 1);
|
||||
int ptmx = recv_fd(fd);
|
||||
setup_sighandlers(sighandler);
|
||||
// if stdin is not a tty, if we pump to ptmx, our process may intercept the input to ptmx and
|
||||
// If stdin is not a tty, and if we pump to ptmx, our process may intercept the input to ptmx and
|
||||
// output to stdout, which cause the target process lost input.
|
||||
pump_tty(ptmx, (atty & ATTY_IN) ? ptmx : -1);
|
||||
pump_tty(ptmx, atty & ATTY_IN);
|
||||
} else {
|
||||
write_int(fd, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user