diff --git a/adb_cli/Cargo.toml b/adb_cli/Cargo.toml index fd1f1ac..93b0436 100644 --- a/adb_cli/Cargo.toml +++ b/adb_cli/Cargo.toml @@ -13,4 +13,7 @@ adb_client = { version = "1.0.6" } anyhow = { version = "1.0.89" } clap = { version = "4.5.18", features = ["derive"] } env_logger = { version = "0.11.5" } -log = "0.4.22" +log = { version = "0.4.22" } + +[target.'cfg(unix)'.dependencies] +termios = { version = "0.3.3" } diff --git a/adb_client/src/adb_termios.rs b/adb_cli/src/adb_termios.rs similarity index 94% rename from adb_client/src/adb_termios.rs rename to adb_cli/src/adb_termios.rs index 5792ca1..5217f6f 100644 --- a/adb_client/src/adb_termios.rs +++ b/adb_cli/src/adb_termios.rs @@ -1,3 +1,5 @@ +#![cfg(any(target_os = "linux", target_os = "macos"))] + use std::os::unix::prelude::{AsRawFd, RawFd}; use termios::{tcsetattr, Termios, TCSANOW, VMIN, VTIME}; diff --git a/adb_cli/src/main.rs b/adb_cli/src/main.rs index 424f78b..4fceaa4 100644 --- a/adb_cli/src/main.rs +++ b/adb_cli/src/main.rs @@ -1,3 +1,5 @@ +#[cfg(any(target_os = "linux", target_os = "macos"))] +mod adb_termios; mod commands; mod models; @@ -53,7 +55,19 @@ fn main() -> Result<()> { } LocalCommand::Shell { command } => { if command.is_empty() { - device.shell()?; + // Need to duplicate some code here as ADBTermios [Drop] implementation resets terminal state. + // Using a scope here would call drop() too early.. + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let mut adb_termios = adb_termios::ADBTermios::new(std::io::stdin())?; + adb_termios.set_adb_termios()?; + device.shell(std::io::stdin(), std::io::stdout())?; + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + device.shell(std::io::stdin(), std::io::stdout())?; + } } else { device.shell_command(command, std::io::stdout())?; } diff --git a/adb_client/Cargo.toml b/adb_client/Cargo.toml index f1b0b02..6ff14dd 100644 --- a/adb_client/Cargo.toml +++ b/adb_client/Cargo.toml @@ -11,11 +11,9 @@ version.workspace = true [dependencies] byteorder = { version = "1.5.0" } chrono = { version = "0.4.38" } -homedir = "0.3.3" +homedir = { version = "0.3.3" } image = { version = "0.25.2" } lazy_static = { version = "1.5.0" } -log = "0.4.22" -mio = { version = "1.0.2", features = ["os-ext", "os-poll"] } +log = { version = "0.4.22" } regex = { version = "1.10.6", features = ["perf", "std", "unicode"] } -termios = { version = "0.3.3" } thiserror = { version = "1.0.64" } diff --git a/adb_client/src/lib.rs b/adb_client/src/lib.rs index 5d147c0..d188000 100644 --- a/adb_client/src/lib.rs +++ b/adb_client/src/lib.rs @@ -4,7 +4,6 @@ #![forbid(missing_docs)] #![doc = include_str!("../README.md")] -mod adb_termios; mod constants; mod emulator; mod error; diff --git a/adb_client/src/server/device_commands/shell.rs b/adb_client/src/server/device_commands/shell.rs index 0a32c0e..d1ebc66 100644 --- a/adb_client/src/server/device_commands/shell.rs +++ b/adb_client/src/server/device_commands/shell.rs @@ -1,29 +1,11 @@ -use std::{ - io::{self, Read, Write}, - sync::mpsc, - time::Duration, -}; - -use mio::{unix::SourceFd, Events, Interest, Poll, Token}; +use std::io::{ErrorKind, Read, Write}; use crate::{ - adb_termios::ADBTermios, models::{AdbServerCommand, HostFeatures}, ADBServerDevice, Result, RustADBError, }; -const STDIN: Token = Token(0); const BUFFER_SIZE: usize = 512; -const POLL_DURATION: Duration = Duration::from_millis(100); - -fn setup_poll_stdin() -> std::result::Result { - let poll = Poll::new()?; - let stdin_fd = 0; - poll.registry() - .register(&mut SourceFd(&stdin_fd), STDIN, Interest::READABLE)?; - - Ok(poll) -} impl ADBServerDevice { /// Runs 'command' in a shell on the device, and write its output and error streams into [`output`]. @@ -73,13 +55,14 @@ impl ADBServerDevice { } } - /// Starts an interactive shell session on the device. Redirects stdin/stdout/stderr as appropriate. - pub fn shell(&mut self) -> Result<()> { - let mut adb_termios = ADBTermios::new(std::io::stdin())?; - adb_termios.set_adb_termios()?; - - // TODO: FORWARD CTRL+C !! - + /// Starts an interactive shell session on the device. + /// Input data is read from [reader] and write to [writer]. + /// [W] has a 'static bound as it is internally used in a thread. + pub fn shell( + &mut self, + mut reader: R, + mut writer: W, + ) -> Result<()> { let supported_features = self.host_features()?; if !supported_features.contains(&HostFeatures::ShellV2) && !supported_features.contains(&HostFeatures::Cmd) @@ -93,26 +76,22 @@ impl ADBServerDevice { self.get_transport_mut() .send_adb_request(AdbServerCommand::Shell)?; - // let read_stream = Arc::new(self.tcp_stream); let mut read_stream = self.get_transport_mut().get_raw_connection()?.try_clone()?; - let (tx, rx) = mpsc::channel::(); - let mut write_stream = read_stream.try_clone()?; - // Reading thread + // Reading thread, reads response from adb-server std::thread::spawn(move || -> Result<()> { loop { let mut buffer = [0; BUFFER_SIZE]; match read_stream.read(&mut buffer) { Ok(0) => { - let _ = tx.send(true); read_stream.shutdown(std::net::Shutdown::Both)?; return Ok(()); } Ok(size) => { - std::io::stdout().write_all(&buffer[..size])?; - std::io::stdout().flush()?; + writer.write_all(&buffer[..size])?; + writer.flush()?; } Err(e) => { return Err(RustADBError::IOError(e)); @@ -121,33 +100,14 @@ impl ADBServerDevice { } }); - let mut buf = [0; BUFFER_SIZE]; - let mut events = Events::with_capacity(1); - - let mut poll = setup_poll_stdin()?; - - // Polling either by checking that reading socket hasn't been closed, and if is there is something to read on stdin. - loop { - poll.poll(&mut events, Some(POLL_DURATION))?; - match rx.try_recv() { - Ok(_) | Err(mpsc::TryRecvError::Disconnected) => return Ok(()), - Err(mpsc::TryRecvError::Empty) => (), - } - - for event in events.iter() { - match event.token() { - STDIN => { - let size = match std::io::stdin().read(&mut buf) { - Ok(0) => return Ok(()), - Ok(size) => size, - Err(_) => return Ok(()), - }; - - write_stream.write_all(&buf[0..size])?; - } - _ => unreachable!(), - } + // Read from given reader (that could be stdin e.g), and write content to server socket + if let Err(e) = std::io::copy(&mut reader, &mut write_stream) { + match e.kind() { + ErrorKind::BrokenPipe => return Ok(()), + _ => return Err(RustADBError::IOError(e)), } } + + Ok(()) } }